-
-
Notifications
You must be signed in to change notification settings - Fork 208
Enh/motor thrustcurve api #870
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Enh/motor thrustcurve api #870
Conversation
|
I really like this implementation!! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds a new method load_from_thrustcurve_api to the GenericMotor class that allows users to download motor data directly from the ThrustCurve.org API by providing a motor name. This feature simplifies motor initialization by eliminating the need to manually download .eng files.
- Adds API integration with ThrustCurve.org to download motor data
- Implements a new static method that searches for motors and downloads their
.engfiles - Includes comprehensive test coverage for the new functionality
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| rocketpy/motors/motor.py | Adds new static method load_from_thrustcurve_api with required imports (base64, tempfile, requests) to enable downloading and loading motor data from ThrustCurve API |
| tests/unit/motors/test_genericmotor.py | Adds test case to verify the new API loading functionality works correctly with Cesaroni M1670 motor data |
| @staticmethod | ||
| def load_from_thrustcurve_api(name: str, **kwargs): |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method signature includes name and **kwargs parameters, but the docstring example shows 'Cesaroni_M1670' format while the test uses 'M1670'. The documentation should clarify the expected input format and provide examples for both manufacturer-prefixed and non-prefixed motor names to avoid user confusion.
rocketpy/motors/motor.py
Outdated
| print("No motor found.") | ||
| return None |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning None when no motor is found is problematic. This should raise a descriptive exception (e.g., ValueError) with a helpful message suggesting the user verify the motor name format. Returning None can lead to confusing AttributeErrors later when users try to use the returned value.
| print("No motor found.") | |
| return None | |
| raise ValueError( | |
| f"No motor found for name '{name}'. " | |
| "Please verify the motor name format (e.g., 'Cesaroni_M1670') and try again." | |
| ) |
rocketpy/motors/motor.py
Outdated
| print("No motor found.") | ||
| return None |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using print() for user feedback is not a best practice in library code. Since RocketPy doesn't appear to use logging, consider raising an exception instead (as suggested in Comment 2) or using warnings.warn() if you want non-fatal feedback. Print statements make it difficult for users to capture or suppress output programmatically.
| print("No motor found.") | |
| return None | |
| raise ValueError( | |
| f"No motor found for name '{name}'. Please check the motor name and try again." | |
| ) |
rocketpy/motors/motor.py
Outdated
| print(f"Motor found: {designation} ({motor['manufacturer']})") | ||
|
|
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the previous print statement, this informational message should not use print() in library code. If this information is important for users, consider adding it to the motor's metadata or attributes, or use warnings.warn() for non-critical information.
| print(f"Motor found: {designation} ({motor['manufacturer']})") | |
| warnings.warn( | |
| f"Motor found: {designation} ({motor['manufacturer']})", | |
| UserWarning, | |
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using warnings.warn(), we replaced the print() with proper logging(in the latest commit). This allows users to control the output via RocketPy’s logging configuration and avoids polluting standard output. The message is now logged at the INFO level in rocketpy/motors/motor.py.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although you're idea was smart and does make a lot a sense, we currently do not have logging utilities in any other part of this repository.
@phmbressan I'd like to hear your opinion about this also, but I would say the "warning approach" is more familiar to the rest of our code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. Just made the change to use warnings.warn(). Thanks for the quick feedback.
| ) | ||
| dl_response.raise_for_status() | ||
| data = dl_response.json() | ||
|
|
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no validation that data['results'] is not empty before accessing data['results'][0]. If the API returns an empty results array for the download endpoint, this will raise an IndexError. Add validation similar to line 1946 to check for empty results and provide a clear error message.
| if not data.get("results"): | |
| print("No .eng file found for this motor in the ThrustCurve API.") | |
| return None |
|
|
||
| return motor | ||
|
|
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The motor is created inside the context manager, but the temporary file will be deleted when exiting the with block (due to delete=True). If load_from_eng_file or any subsequent processing tries to access the file after the context manager exits, it will fail. Move the return statement inside the with block or consider using delete=False with manual cleanup if the file needs to persist.
| return motor | |
| return motor |
| response = requests.get(f"{base_url}/search.json", params={"commonName": name}) | ||
| response.raise_for_status() |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method doesn't document the potential exceptions that can be raised, particularly requests.exceptions.RequestException from network issues or HTTP errors. According to Python best practices and to help users handle errors appropriately, add a 'Raises' section to the docstring documenting requests.exceptions.RequestException and any ValueError exceptions.
|
I see 2 major problems: 1 - Documentation: we should add a description of how to use the new feature. |
ff57272 to
2770ba2
Compare
…with clean imports
2770ba2 to
da39fcb
Compare
rocketpy/motors/motor.py
Outdated
| motor_instance = GenericMotor.load_from_eng_file(tmp_path, **kwargs) | ||
| return motor_instance |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
noblock:
| motor_instance = GenericMotor.load_from_eng_file(tmp_path, **kwargs) | |
| return motor_instance | |
| return GenericMotor.load_from_eng_file(tmp_path, **kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.
rocketpy/motors/motor.py
Outdated
| import base64 | ||
| import re | ||
| import tempfile | ||
| import warnings | ||
| import xml.etree.ElementTree as ET | ||
| from abc import ABC, abstractmethod | ||
| from functools import cached_property | ||
| from os import path | ||
| from os import path, remove | ||
|
|
||
| import numpy as np | ||
|
|
||
| import requests | ||
| import logging |
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import order violates PEP 8 conventions. Standard library imports should be grouped together, with third-party imports in a separate group. Move logging (line 12) to the standard library group (between lines 1-8), and move requests (line 11) to join numpy (line 10) in the third-party group.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simply run the command make format and it will solve the issue
rocketpy/motors/motor.py
Outdated
| tmp_file.flush() | ||
| tmp_path = tmp_file.name | ||
|
|
||
|
|
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 1996 contains only whitespace. Remove this empty line to maintain code cleanliness and consistency with the rest of the codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make format
| motor_instance = GenericMotor.load_from_eng_file(tmp_path, **kwargs) | ||
| return motor_instance | ||
| finally: | ||
| # Ensuring the temporary file is removed |
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with the codebase comment style, use imperative mood: change 'Ensuring' to 'Ensure'.
| # Ensuring the temporary file is removed | |
| # Ensure the temporary file is removed |
rocketpy/motors/motor.py
Outdated
| interpolation_method=interpolation_method, | ||
| coordinate_system_orientation=coordinate_system_orientation, | ||
| ) | ||
|
|
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 1921 contains trailing whitespace. Remove trailing spaces to maintain code cleanliness.
| import numpy as np | ||
| import pytest | ||
| import scipy.integrate | ||
| import requests | ||
| import base64 |
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import order violates PEP 8 conventions. Standard library imports (base64) should come before third-party imports. Move base64 to line 1 before numpy, and group standard library imports together.
| import numpy as np | |
| import pytest | |
| import scipy.integrate | |
| import requests | |
| import base64 | |
| import base64 | |
| import numpy as np | |
| import pytest | |
| import scipy.integrate | |
| import requests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make format
| The pytest monkeypatch fixture for mocking. | ||
| generic_motor : rocketpy.GenericMotor | ||
| The GenericMotor object to be used in the tests. | ||
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 227 contains trailing whitespace in the docstring. Remove trailing spaces to maintain code cleanliness.
| """ | ||
| Tests the GenericMotor.load_from_thrustcurve_api method with mocked ThrustCurve API responses. | ||
| Parameters | ||
| ---------- | ||
| monkeypatch : pytest.MonkeyPatch | ||
| The pytest monkeypatch fixture for mocking. | ||
| generic_motor : rocketpy.GenericMotor | ||
| The GenericMotor object to be used in the tests. | ||
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Test docstring should follow the project's NumPy-style docstring format more precisely. The first line should be a brief summary that fits on one line. Consider: 'Tests the GenericMotor.load_from_thrustcurve_api method with mocked API responses.' Move implementation details to a Notes section if needed.
| """ | |
| Tests the GenericMotor.load_from_thrustcurve_api method with mocked ThrustCurve API responses. | |
| Parameters | |
| ---------- | |
| monkeypatch : pytest.MonkeyPatch | |
| The pytest monkeypatch fixture for mocking. | |
| generic_motor : rocketpy.GenericMotor | |
| The GenericMotor object to be used in the tests. | |
| """Tests the GenericMotor.load_from_thrustcurve_api method with mocked API responses. | |
| Parameters | |
| ---------- | |
| monkeypatch : pytest.MonkeyPatch | |
| The pytest monkeypatch fixture for mocking. | |
| generic_motor : rocketpy.GenericMotor | |
| The GenericMotor object to be used in the tests. | |
| Notes | |
| ----- | |
| This test mocks the ThrustCurve API endpoints 'search.json' and 'download.json' | |
| to provide controlled responses. The .eng file is read locally and its content | |
| is base64-encoded to simulate the API's behavior. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #870 +/- ##
===========================================
+ Coverage 80.27% 80.31% +0.03%
===========================================
Files 104 104
Lines 12769 12805 +36
===========================================
+ Hits 10250 10284 +34
- Misses 2519 2521 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…urve_api with exception testing
|
@Gui-FernandesBR Thank you for going over the code. I just updated the latest commit to run make format for import/order/style cleanup and added tests for exception handling in load_from_thrustcurve_api. |
… genericmotors.rst file
Pull request type
Checklist
black rocketpy/ tests/) has passed locallypytest tests -m slow --runslow) have passed locallyCurrent behavior
#661
New behavior
We added the function to load a motor from the thrustcruve API and we tested it.
Breaking change