From 25a817821800757ab082881c8a4423a0181358c9 Mon Sep 17 00:00:00 2001 From: Aasit Vora Date: Sun, 5 Oct 2025 01:27:44 +0530 Subject: [PATCH 1/3] BUG: temp accepting length only. --- src/models/sub/aerosurfaces.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/sub/aerosurfaces.py b/src/models/sub/aerosurfaces.py index 339066e..0452a57 100644 --- a/src/models/sub/aerosurfaces.py +++ b/src/models/sub/aerosurfaces.py @@ -42,7 +42,6 @@ class Fins(BaseModel): Tuple[List[Tuple[float, float]], Literal['radians', 'degrees']] ] = None sweep_length: Optional[float] = None - sweep_angle: Optional[float] = None def get_additional_parameters(self): return { From 60a7aeac45be2b25bb3a7d8e8b23836584481c14 Mon Sep 17 00:00:00 2001 From: Aasit Vora Date: Fri, 10 Oct 2025 02:04:17 +0530 Subject: [PATCH 2/3] ENH: add sweep_angle to Fins model and refactor get_additional_parameters method --- src/models/sub/aerosurfaces.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/models/sub/aerosurfaces.py b/src/models/sub/aerosurfaces.py index 0452a57..c367506 100644 --- a/src/models/sub/aerosurfaces.py +++ b/src/models/sub/aerosurfaces.py @@ -42,15 +42,19 @@ class Fins(BaseModel): Tuple[List[Tuple[float, float]], Literal['radians', 'degrees']] ] = None sweep_length: Optional[float] = None + sweep_angle: Optional[float] = None + + _base_keys = {"fins_kind", "name", "n", "root_chord", "span", "position"} def get_additional_parameters(self): - return { - key: value - for key, value in self.dict().items() - if value is not None - and key - not in ["fins_kind", "name", "n", "root_chord", "span", "position"] + params = { + k: v for k, v in self.dict().items() + if v is not None and k not in self._base_keys } + if "sweep_angle" in params and "sweep_length" in params: + params.pop("sweep_length") + + return params # TODO: implement airbrakes From 57e015951e4ac84e11165b676768c60e3f830a07 Mon Sep 17 00:00:00 2001 From: Gabriel Barberini Date: Fri, 10 Oct 2025 21:00:38 +0200 Subject: [PATCH 3/3] linter sanity --- Makefile | 62 ++++++++++--- src/models/sub/aerosurfaces.py | 3 +- src/utils.py | 154 +++++++++++++++------------------ 3 files changed, 124 insertions(+), 95 deletions(-) diff --git a/Makefile b/Makefile index c65bc7f..337c8b2 100644 --- a/Makefile +++ b/Makefile @@ -1,27 +1,69 @@ +VENV_BIN ?= ./infinity_env/bin +PYLINTHOME ?= .pylint.d + +export PYLINTHOME + +ifneq (,$(wildcard $(VENV_BIN)/black)) +BLACK := $(VENV_BIN)/black +else +BLACK := black +endif + +ifneq (,$(wildcard $(VENV_BIN)/ruff)) +RUFF := $(VENV_BIN)/ruff +else +RUFF := ruff +endif + +ifneq (,$(wildcard $(VENV_BIN)/flake8)) +FLAKE8 := $(VENV_BIN)/flake8 +else +FLAKE8 := flake8 +endif + +ifneq (,$(wildcard $(VENV_BIN)/pylint)) +PYLINT := $(VENV_BIN)/pylint +else +PYLINT := pylint +endif + +ifneq (,$(wildcard $(VENV_BIN)/pytest)) +PYTEST := $(VENV_BIN)/pytest +else +PYTEST := python3 -m pytest +endif + +ifneq (,$(wildcard $(VENV_BIN)/uvicorn)) +UVICORN := $(VENV_BIN)/uvicorn +else +UVICORN := python3 -m uvicorn +endif + format: black ruff lint: flake8 pylint black: - black ./src || true - black ./tests || true + $(BLACK) ./src || true + $(BLACK) ./tests || true flake8: - flake8 --ignore E501,E402,F401,W503,C0414 ./src || true - flake8 --ignore E501,E402,F401,W503,C0414 ./tests || true + $(FLAKE8) --ignore E501,E402,F401,W503,C0414 ./src || true + $(FLAKE8) --ignore E501,E402,F401,W503,C0414 ./tests || true pylint: - pylint ./src || true - pylint ./tests || true + @mkdir -p $(PYLINTHOME) + $(PYLINT) ./src || true + $(PYLINT) ./tests || true ruff: - ruff check --fix ./src || true - ruff check --fix ./tests || true + $(RUFF) check --fix ./src || true + $(RUFF) check --fix ./tests || true test: - python3 -m pytest . + $(PYTEST) . dev: - python3 -m uvicorn src:app --reload --port 3000 --loop uvloop + $(UVICORN) src:app --reload --port 3000 --loop uvloop clean: docker stop infinity-api diff --git a/src/models/sub/aerosurfaces.py b/src/models/sub/aerosurfaces.py index c367506..b6dd590 100644 --- a/src/models/sub/aerosurfaces.py +++ b/src/models/sub/aerosurfaces.py @@ -48,7 +48,8 @@ class Fins(BaseModel): def get_additional_parameters(self): params = { - k: v for k, v in self.dict().items() + k: v + for k, v in self.dict().items() if v is not None and k not in self._base_keys } if "sweep_angle" in params and "sweep_length" in params: diff --git a/src/utils.py b/src/utils.py index d24d724..c964e3f 100644 --- a/src/utils.py +++ b/src/utils.py @@ -54,10 +54,8 @@ def for_flight(cls) -> 'DiscretizeConfig': class InfinityEncoder(RocketPyEncoder): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def default(self, obj): + def default(self, o): + obj = o if ( isinstance(obj, Function) and not callable(obj.source) @@ -75,7 +73,7 @@ def default(self, obj): mutate_self=False, ) if isinstance(obj, Flight): - obj._Flight__evaluate_post_process + obj._Flight__evaluate_post_process() solution = np.array(obj.solution) size = len(solution) if size > 25: @@ -117,92 +115,80 @@ def rocketpy_encoder(obj): def collect_attributes(obj, attribute_classes=None): - """ - Collect attributes from various simulation classes and populate them from the flight object. - """ - if attribute_classes is None: - attribute_classes = [] + """Collect and serialize attributes from simulation classes.""" + attribute_classes = attribute_classes or [] attributes = rocketpy_encoder(obj) - for attribute_class in attribute_classes: - if issubclass(attribute_class, FlightSimulation): - flight_attributes_list = [ - attr - for attr in attribute_class.__annotations__.keys() - if attr not in ["message", "rocket", "env"] - ] - try: - for key in flight_attributes_list: - if key not in attributes: - try: - value = getattr(obj, key) - attributes[key] = value - except Exception: - pass - except Exception: - pass - - elif issubclass(attribute_class, RocketSimulation): - rocket_attributes_list = [ - attr - for attr in attribute_class.__annotations__.keys() - if attr not in ["message", "motor"] - ] - try: - for key in rocket_attributes_list: - if key not in attributes.get("rocket", {}): - try: - value = getattr(obj.rocket, key) - attributes.setdefault("rocket", {})[key] = value - except Exception: - pass - except Exception: - pass - - elif issubclass(attribute_class, MotorSimulation): - motor_attributes_list = [ - attr - for attr in attribute_class.__annotations__.keys() - if attr not in ["message"] - ] - try: - for key in motor_attributes_list: - if key not in attributes.get("rocket", {}).get( - "motor", {} - ): - try: - value = getattr(obj.rocket.motor, key) - attributes.setdefault("rocket", {}).setdefault( - "motor", {} - )[key] = value - except Exception: - pass - except Exception: - pass - - elif issubclass(attribute_class, EnvironmentSimulation): - environment_attributes_list = [ - attr - for attr in attribute_class.__annotations__.keys() - if attr not in ["message"] - ] - try: - for key in environment_attributes_list: - if key not in attributes.get("env", {}): - try: - value = getattr(obj.env, key) - attributes.setdefault("env", {})[key] = value - except Exception: - pass - except Exception: - pass - else: - continue + _populate_simulation_attributes(obj, attribute_class, attributes) return rocketpy_encoder(attributes) +def _populate_simulation_attributes(obj, attribute_class, attributes): + if not isinstance(attribute_class, type): + return + + mappings = ( + (FlightSimulation, (), (), {"message", "rocket", "env"}), + (RocketSimulation, ("rocket",), ("rocket",), {"message", "motor"}), + ( + MotorSimulation, + ("rocket", "motor"), + ("rocket", "motor"), + {"message"}, + ), + (EnvironmentSimulation, ("env",), ("env",), {"message"}), + ) + + for klass, source_path, target_path, exclusions in mappings: + if not issubclass(attribute_class, klass): + continue + + keys = _annotation_keys(attribute_class, exclusions) + if not keys: + return + + source = _resolve_attribute_path(obj, source_path) + if source is None: + return + + target = _resolve_attribute_target(attributes, target_path) + _copy_missing_attributes(source, target, keys) + return + + +def _annotation_keys(attribute_class, exclusions): + annotations = getattr(attribute_class, "__annotations__", {}) + return [key for key in annotations if key not in exclusions] + + +def _resolve_attribute_path(root, path): + current = root + for attr in path: + if current is None: + return None + current = getattr(current, attr, None) + return current + + +def _resolve_attribute_target(attributes, path): + target = attributes + for key in path: + target = target.setdefault(key, {}) + return target + + +def _copy_missing_attributes(source, target, keys): + for key in keys: + if key in target: + continue + try: + target[key] = getattr(source, key) + except AttributeError: + continue + + def _fix_datetime_fields(data): """ Fix datetime fields that RocketPyEncoder converted to lists.