From 1d7b2f26b98c607c306d557a7bd53db15cf7aa00 Mon Sep 17 00:00:00 2001 From: Tang Xiaoyu Date: Tue, 4 Nov 2025 16:13:35 +0100 Subject: [PATCH 01/11] DOC: Add entry for custom warnings in CHANGELOG.md Added a placeholder in the [Unreleased] section for the upcoming feature to add custom warnings when a rocket is missing motors and/or aero-surface. See #285. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7090973ad..24e7045f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ Attention: The newest changes should be on top --> ### Added +- ENH: Custom Exception errors and messages [#285](https://github.com/RocketPy-Team/RocketPy/issues/285) + ### Changed ### Fixed From 4eff12e46a194f83b5199e15386d91927383adcb Mon Sep 17 00:00:00 2001 From: Tang Xiaoyu Date: Tue, 4 Nov 2025 16:52:07 +0100 Subject: [PATCH 02/11] ENH: add custom warning for Rocket with no components This enhancement adds a warning when a Rocket object has no motors, parachutes, or AeroSurface components. It notifies the user so that they can add missing components before running simulations. See #285 --- rocketpy/rocket/rocket.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 1112a98f3..00a76c3a1 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -384,6 +384,35 @@ def __init__( # pylint: disable=too-many-statements self.prints = _RocketPrints(self) self.plots = _RocketPlots(self) + def _check_missing_components(self): + """Check if the rocket is missing any essential components and issue a warning. + + This method verifies whether the rocket has the following key components: + - motor + - parachute(s) + - aerodynamic surface(s) + + If any of these components are missing, a single warning message is issued + listing all missing components. This helps users quickly identify potential + issues before running simulations or analyses. + + Notes: + - The warning uses Python's built-in `warnings.warn` function. + """ + missing_components = [] + if isinstance(self.motor, EmptyMotor): + missing_components.append("motor") + if not self.parachutes: + missing_components.append("parachutes") + if not self.aerodynamic_surfaces or len(self.aerodynamic_surfaces) == 0: + missing_components.append("aerodynamic surfaces") + + if missing_components: + component_list = ", ".join(missing_components) + warnings.warn( + f"[WARNING] Rocket has no {component_list} defined.", UserWarning + ) + @property def nosecones(self): """A list containing all the nose cones currently added to the rocket.""" From a4f44c467071da83c7fdcb92240f3b22969e4e84 Mon Sep 17 00:00:00 2001 From: Bizo883 <15618311793@163.com> Date: Wed, 5 Nov 2025 15:37:08 +0100 Subject: [PATCH 03/11] ENH:Add tests for _check_missing_components method in Rocket class --- tests/unit/rocket/test_rocket.py | 38 +++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/unit/rocket/test_rocket.py b/tests/unit/rocket/test_rocket.py index 3c1e7168d..686dc3363 100644 --- a/tests/unit/rocket/test_rocket.py +++ b/tests/unit/rocket/test_rocket.py @@ -10,7 +10,9 @@ @patch("matplotlib.pyplot.show") -def test_elliptical_fins(mock_show, calisto_robust, calisto_trapezoidal_fins): # pylint: disable=unused-argument +def test_elliptical_fins( + mock_show, calisto_robust, calisto_trapezoidal_fins +): # pylint: disable=unused-argument test_rocket = calisto_robust calisto_robust.aerodynamic_surfaces.remove(calisto_trapezoidal_fins) test_rocket.add_elliptical_fins(4, span=0.100, root_chord=0.120, position=-1.168) @@ -370,6 +372,40 @@ def test_add_motor(calisto_motorless, cesaroni_m1670): assert center_of_mass_motorless is not center_of_mass_with_motor +def test_check_missing_all_components(calisto_motorless): + """Tests the _check_missing_components method for a Rocket with no components.""" + with pytest.warns(UserWarning) as record: + calisto_motorless._check_missing_components() + + assert len(record) == 1 + msg = str(record[0].message) + assert "motor" in msg + assert "parachutes" in msg + assert "aerodynamic surfaces" in msg + + +def test_check_missing_some_components(calisto): + """Tests the _check_missing_components method for a Rocket missing some components.""" + calisto.parachutes = [] + calisto.aerodynamic_surfaces = [] + + with pytest.warns(UserWarning) as record: + calisto._check_missing_components() + + assert len(record) == 1 + msg = str(record[0].message) + assert "parachutes" in msg + assert "aerodynamic surfaces" in msg + + +def test_check_missing_no_components_missing(calisto_robust): + """Tests the _check_missing_components method for a complete Rocket.""" + # Call directly — no warnings expected + calisto_robust._check_missing_components() + # If any warning occurs, pytest will fail automatically + assert True + + def test_set_rail_button(calisto): rail_buttons = calisto.set_rail_buttons(0.2, -0.5, 30) # assert buttons_distance From c00e2dd9cecd55b70898abc8d7a19e1d7f5447b8 Mon Sep 17 00:00:00 2001 From: Bizo883 <15618311793@163.com> Date: Wed, 12 Nov 2025 18:51:01 +0100 Subject: [PATCH 04/11] TST:improve test_check_missing_no_components_missing to correctly handle warnings --- tests/unit/rocket/test_rocket.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unit/rocket/test_rocket.py b/tests/unit/rocket/test_rocket.py index 686dc3363..fe7fb6704 100644 --- a/tests/unit/rocket/test_rocket.py +++ b/tests/unit/rocket/test_rocket.py @@ -401,9 +401,10 @@ def test_check_missing_some_components(calisto): def test_check_missing_no_components_missing(calisto_robust): """Tests the _check_missing_components method for a complete Rocket.""" # Call directly — no warnings expected - calisto_robust._check_missing_components() + with pytest.warns(None) as record: + calisto_robust._check_missing_components() # If any warning occurs, pytest will fail automatically - assert True + assert len(record) == 0 def test_set_rail_button(calisto): From 1d75f805e0ff4a50431921fc0ca800a22ab9b727 Mon Sep 17 00:00:00 2001 From: Tang Xiaoyu Date: Wed, 12 Nov 2025 17:36:47 +0100 Subject: [PATCH 05/11] BUG: do not warn for rockets without parachute Only warn if motor or aerodynamic surfaces are missing. Never raise a warning for no parachute. See #285 --- rocketpy/rocket/rocket.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 00a76c3a1..68943ca18 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -389,7 +389,6 @@ def _check_missing_components(self): This method verifies whether the rocket has the following key components: - motor - - parachute(s) - aerodynamic surface(s) If any of these components are missing, a single warning message is issued @@ -402,8 +401,6 @@ def _check_missing_components(self): missing_components = [] if isinstance(self.motor, EmptyMotor): missing_components.append("motor") - if not self.parachutes: - missing_components.append("parachutes") if not self.aerodynamic_surfaces or len(self.aerodynamic_surfaces) == 0: missing_components.append("aerodynamic surfaces") From c787b3699dd35415a52b827b0d0e73cfd138d4e4 Mon Sep 17 00:00:00 2001 From: Bizo883 <15618311793@163.com> Date: Mon, 17 Nov 2025 20:10:27 +0100 Subject: [PATCH 06/11] TST:Update the test:do not warn when missing parachute --- tests/unit/rocket/test_rocket.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/unit/rocket/test_rocket.py b/tests/unit/rocket/test_rocket.py index fe7fb6704..001dc5a62 100644 --- a/tests/unit/rocket/test_rocket.py +++ b/tests/unit/rocket/test_rocket.py @@ -380,7 +380,6 @@ def test_check_missing_all_components(calisto_motorless): assert len(record) == 1 msg = str(record[0].message) assert "motor" in msg - assert "parachutes" in msg assert "aerodynamic surfaces" in msg @@ -394,7 +393,6 @@ def test_check_missing_some_components(calisto): assert len(record) == 1 msg = str(record[0].message) - assert "parachutes" in msg assert "aerodynamic surfaces" in msg From 39a23cfa0aa68067828bf552c7c613c92dd89986 Mon Sep 17 00:00:00 2001 From: x25tang Date: Tue, 25 Nov 2025 14:41:32 +0100 Subject: [PATCH 07/11] MNT: simplify checks and update docstrings in rocket.py - Removed redundant len() check for aerodynamic_surfaces. - Added 'Returns: None' section to NumPy-style docstring. - Removed redundant '[WARNING]' prefix in warnings messages. --- rocketpy/rocket/rocket.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 68943ca18..2ac582458 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -395,19 +395,24 @@ def _check_missing_components(self): listing all missing components. This helps users quickly identify potential issues before running simulations or analyses. - Notes: + Notes + ----- - The warning uses Python's built-in `warnings.warn` function. + + Returns + ------- + None """ missing_components = [] if isinstance(self.motor, EmptyMotor): missing_components.append("motor") - if not self.aerodynamic_surfaces or len(self.aerodynamic_surfaces) == 0: + if not self.aerodynamic_surfaces: missing_components.append("aerodynamic surfaces") if missing_components: component_list = ", ".join(missing_components) warnings.warn( - f"[WARNING] Rocket has no {component_list} defined.", UserWarning + f"Rocket has no {component_list} defined.", UserWarning ) @property From 192455e7c4330d6a3257ac9b342d5bb1e8e8703e Mon Sep 17 00:00:00 2001 From: Bizo883 <15618311793@163.com> Date: Tue, 25 Nov 2025 19:08:07 +0100 Subject: [PATCH 08/11] TST:update test_check_missing_no_components_missing to avoid TypeError: exceptions must be derived from Warning, not --- tests/unit/rocket/test_rocket.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/unit/rocket/test_rocket.py b/tests/unit/rocket/test_rocket.py index 001dc5a62..62caaf835 100644 --- a/tests/unit/rocket/test_rocket.py +++ b/tests/unit/rocket/test_rocket.py @@ -385,7 +385,6 @@ def test_check_missing_all_components(calisto_motorless): def test_check_missing_some_components(calisto): """Tests the _check_missing_components method for a Rocket missing some components.""" - calisto.parachutes = [] calisto.aerodynamic_surfaces = [] with pytest.warns(UserWarning) as record: @@ -398,11 +397,13 @@ def test_check_missing_some_components(calisto): def test_check_missing_no_components_missing(calisto_robust): """Tests the _check_missing_components method for a complete Rocket.""" - # Call directly — no warnings expected - with pytest.warns(None) as record: + import warnings + # Catch all warnings that occur inside this 'with' block. + with warnings.catch_warnings(record=True) as w: + # Ensure that *all* warnings are captured. + warnings.simplefilter("always") calisto_robust._check_missing_components() - # If any warning occurs, pytest will fail automatically - assert len(record) == 0 + assert len(w) == 0 def test_set_rail_button(calisto): From 6e16e60852aa117f2e32e3583e68ded245ac6487 Mon Sep 17 00:00:00 2001 From: Bizo883 <15618311793@163.com> Date: Wed, 26 Nov 2025 09:58:31 +0100 Subject: [PATCH 09/11] TST:update test_check_missing_no_components_missing:import the lib at the top of file --- tests/unit/rocket/test_rocket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/rocket/test_rocket.py b/tests/unit/rocket/test_rocket.py index 62caaf835..d31ccce44 100644 --- a/tests/unit/rocket/test_rocket.py +++ b/tests/unit/rocket/test_rocket.py @@ -400,9 +400,9 @@ def test_check_missing_no_components_missing(calisto_robust): import warnings # Catch all warnings that occur inside this 'with' block. with warnings.catch_warnings(record=True) as w: - # Ensure that *all* warnings are captured. warnings.simplefilter("always") calisto_robust._check_missing_components() + # For a complete rocket, this method should NOT issue any warnings. assert len(w) == 0 From 85544e69a6da7f7aa18bff77b78b547bd2c884e1 Mon Sep 17 00:00:00 2001 From: Bizo883 <15618311793@163.com> Date: Wed, 26 Nov 2025 10:16:12 +0100 Subject: [PATCH 10/11] TST:update test_check_missing_no_components_missing:import the lib at the top of file --- tests/unit/rocket/test_rocket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/rocket/test_rocket.py b/tests/unit/rocket/test_rocket.py index d31ccce44..e9ea35e90 100644 --- a/tests/unit/rocket/test_rocket.py +++ b/tests/unit/rocket/test_rocket.py @@ -2,6 +2,7 @@ import numpy as np import pytest +import warnings from rocketpy import Function, NoseCone, Rocket, SolidMotor from rocketpy.mathutils.vector_matrix import Vector @@ -397,7 +398,6 @@ def test_check_missing_some_components(calisto): def test_check_missing_no_components_missing(calisto_robust): """Tests the _check_missing_components method for a complete Rocket.""" - import warnings # Catch all warnings that occur inside this 'with' block. with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") From dc369f09bbe9366d036014f9e9bef495c708ae95 Mon Sep 17 00:00:00 2001 From: Marchma0 Date: Wed, 26 Nov 2025 10:26:03 +0000 Subject: [PATCH 11/11] Fix lint errors --- rocketpy/rocket/rocket.py | 4 +--- tests/unit/rocket/test_rocket.py | 6 ++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index 2ac582458..63853a399 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -411,9 +411,7 @@ def _check_missing_components(self): if missing_components: component_list = ", ".join(missing_components) - warnings.warn( - f"Rocket has no {component_list} defined.", UserWarning - ) + warnings.warn(f"Rocket has no {component_list} defined.", UserWarning) @property def nosecones(self): diff --git a/tests/unit/rocket/test_rocket.py b/tests/unit/rocket/test_rocket.py index e9ea35e90..5c3fed359 100644 --- a/tests/unit/rocket/test_rocket.py +++ b/tests/unit/rocket/test_rocket.py @@ -1,8 +1,8 @@ +import warnings from unittest.mock import patch import numpy as np import pytest -import warnings from rocketpy import Function, NoseCone, Rocket, SolidMotor from rocketpy.mathutils.vector_matrix import Vector @@ -11,9 +11,7 @@ @patch("matplotlib.pyplot.show") -def test_elliptical_fins( - mock_show, calisto_robust, calisto_trapezoidal_fins -): # pylint: disable=unused-argument +def test_elliptical_fins(mock_show, calisto_robust, calisto_trapezoidal_fins): # pylint: disable=unused-argument test_rocket = calisto_robust calisto_robust.aerodynamic_surfaces.remove(calisto_trapezoidal_fins) test_rocket.add_elliptical_fins(4, span=0.100, root_chord=0.120, position=-1.168)