Skip to content

Conversation

@Marchma0
Copy link

@Marchma0 Marchma0 commented Nov 4, 2025

Pull request type

  • Code changes (bugfix, features)
  • Code maintenance (refactoring, formatting, tests)

Checklist

  • Tests for the changes have been added (if needed)
  • Lint (black rocketpy/ tests/) has passed locally
  • All tests (pytest tests -m slow --runslow) have passed locally

Current behavior

#661

New behavior

We added the function to load a motor from the thrustcruve API and we tested it.

Breaking change

  • No

@Marchma0 Marchma0 requested a review from a team as a code owner November 4, 2025 18:09
@Gui-FernandesBR Gui-FernandesBR changed the base branch from master to develop November 4, 2025 18:20
@Gui-FernandesBR
Copy link
Member

I really like this implementation!!

Copy link

Copilot AI left a 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 .eng files
  • 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

Comment on lines +1920 to +1921
@staticmethod
def load_from_thrustcurve_api(name: str, **kwargs):
Copy link

Copilot AI Nov 4, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines 1947 to 1948
print("No motor found.")
return None
Copy link

Copilot AI Nov 4, 2025

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.

Suggested change
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."
)

Copilot uses AI. Check for mistakes.
Comment on lines 1947 to 1948
print("No motor found.")
return None
Copy link

Copilot AI Nov 4, 2025

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.

Suggested change
print("No motor found.")
return None
raise ValueError(
f"No motor found for name '{name}'. Please check the motor name and try again."
)

Copilot uses AI. Check for mistakes.
Comment on lines 1953 to 1954
print(f"Motor found: {designation} ({motor['manufacturer']})")

Copy link

Copilot AI Nov 4, 2025

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.

Suggested change
print(f"Motor found: {designation} ({motor['manufacturer']})")
warnings.warn(
f"Motor found: {designation} ({motor['manufacturer']})",
UserWarning,
)

Copilot uses AI. Check for mistakes.
Copy link

@Monta120 Monta120 Nov 4, 2025

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.

Copy link
Member

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.

Copy link

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()

Copy link

Copilot AI Nov 4, 2025

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.

Suggested change
if not data.get("results"):
print("No .eng file found for this motor in the ThrustCurve API.")
return None

Copilot uses AI. Check for mistakes.
Comment on lines 1973 to 1975

return motor

Copy link

Copilot AI Nov 4, 2025

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.

Suggested change
return motor
return motor

Copilot uses AI. Check for mistakes.
Comment on lines +1942 to +1943
response = requests.get(f"{base_url}/search.json", params={"commonName": name})
response.raise_for_status()
Copy link

Copilot AI Nov 4, 2025

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.

Copilot uses AI. Check for mistakes.
@Gui-FernandesBR
Copy link
Member

I see 2 major problems:

1 - Documentation: we should add a description of how to use the new feature.
2 - Tests: We should mock the API call so we don't exhaust the endpoint whenever we run the tests

@Monta120 Monta120 force-pushed the enh/motor-thrustcurve-api branch from ff57272 to 2770ba2 Compare November 4, 2025 21:32
@Monta120 Monta120 force-pushed the enh/motor-thrustcurve-api branch from 2770ba2 to da39fcb Compare November 4, 2025 21:57
Comment on lines 1997 to 1998
motor_instance = GenericMotor.load_from_eng_file(tmp_path, **kwargs)
return motor_instance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noblock:

Suggested change
motor_instance = GenericMotor.load_from_eng_file(tmp_path, **kwargs)
return motor_instance
return GenericMotor.load_from_eng_file(tmp_path, **kwargs)

@Gui-FernandesBR Gui-FernandesBR requested review from Monta120, Copilot and phmbressan and removed request for Monta120 November 5, 2025 01:34
Copy link

Copilot AI left a 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.

Comment on lines 1 to 12
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
Copy link

Copilot AI Nov 5, 2025

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.

Copilot uses AI. Check for mistakes.
Copy link
Member

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

tmp_file.flush()
tmp_path = tmp_file.name


Copy link

Copilot AI Nov 5, 2025

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.

Suggested change

Copilot uses AI. Check for mistakes.
Copy link
Member

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
Copy link

Copilot AI Nov 5, 2025

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'.

Suggested change
# Ensuring the temporary file is removed
# Ensure the temporary file is removed

Copilot uses AI. Check for mistakes.
interpolation_method=interpolation_method,
coordinate_system_orientation=coordinate_system_orientation,
)

Copy link

Copilot AI Nov 5, 2025

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.

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 5
import numpy as np
import pytest
import scipy.integrate
import requests
import base64
Copy link

Copilot AI Nov 5, 2025

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.

Suggested change
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

Copilot uses AI. Check for mistakes.
Copy link
Member

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.
Copy link

Copilot AI Nov 5, 2025

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.

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines 219 to 227
"""
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.
Copy link

Copilot AI Nov 5, 2025

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.

Suggested change
"""
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.

Copilot uses AI. Check for mistakes.
@codecov
Copy link

codecov bot commented Nov 5, 2025

Codecov Report

❌ Patch coverage is 94.59459% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.31%. Comparing base (9cf3dd4) to head (d6c5dee).
⚠️ Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
rocketpy/motors/motor.py 94.59% 2 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Monta120
Copy link

Monta120 commented Nov 5, 2025

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

3 participants