Skip to content
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

BUG removes lambda support from parachutes #31

Merged
merged 3 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def custom_openapi():
return app.openapi_schema
openapi_schema = get_openapi(
title="RocketPy Infinity-API",
version="2.0.0",
version="2.1.0",
description=(
"<p style='font-size: 18px;'>RocketPy Infinity-API is a RESTful Open API for RocketPy, a rocket flight simulator.</p>"
"<br/>"
Expand Down
2 changes: 1 addition & 1 deletion lib/models/rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Parachute(BaseModel):
cd_s: float = 10
sampling_rate: int = 105
lag: float = 1.5
trigger: Union[str, float] = "lambda p, h, y: y[5] < 0 and h < 800"
trigger: Union[str, float] = "apogee"
GabrielBarberini marked this conversation as resolved.
Show resolved Hide resolved
noise: Tuple[float, float, float] = (0, 8.3, 0.5)


Expand Down
72 changes: 6 additions & 66 deletions lib/services/rocket.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import ast
from typing import Self

import dill
Expand All @@ -21,10 +20,6 @@
from lib.views.rocket import RocketSummary


class InvalidParachuteTrigger(Exception):
"""Exception raised for invalid parachute trigger expressions."""


class RocketService:
_rocket: RocketPyRocket

Expand Down Expand Up @@ -81,14 +76,7 @@ def from_rocket_model(cls, rocket: Rocket) -> Self:

# Parachutes
for parachute in rocket.parachutes:
if cls.check_parachute_trigger(
trigger_expression := parachute.trigger
):
parachute.trigger = eval( # pylint: disable=eval-used
trigger_expression,
{"__builtins__": None},
{"apogee": "apogee"},
)
if cls.check_parachute_trigger(parachute.trigger):
rocketpy_parachute = cls.get_rocketpy_parachute(parachute)
rocketpy_rocket.parachutes.append(rocketpy_parachute)
else:
Expand Down Expand Up @@ -219,67 +207,19 @@ def get_rocketpy_parachute(parachute: Parachute) -> RocketPyParachute:
return rocketpy_parachute

@staticmethod
def check_parachute_trigger(expression) -> bool:
def check_parachute_trigger(trigger) -> bool:
"""
Check if the trigger expression is valid.

Args:
expression: str
trigger: str | float

Returns:
bool: True if the expression is valid, False otherwise.
"""

# Parsing the expression into an AST
try:
parsed_expression = ast.parse(expression, mode="eval")
except SyntaxError as e:
raise InvalidParachuteTrigger(
f"Invalid expression syntax: {str(e)}"
) from None

# Constant case (supported after beta v1)
if isinstance(parsed_expression.body, ast.Constant):
if trigger == "apogee":
return True
# Name case (supported after beta v1)
if (
isinstance(parsed_expression.body, ast.Name)
and parsed_expression.body.id == "apogee"
):
if isinstance(trigger, (int, float)):
return True

# Validating the expression structure
if not isinstance(parsed_expression.body, ast.Lambda):
raise InvalidParachuteTrigger(
"Invalid expression structure: not a lambda."
) from None

lambda_node = parsed_expression.body
if len(lambda_node.args.args) != 3:
raise InvalidParachuteTrigger(
"Invalid expression structure: lambda should have 3 arguments."
) from None

if not isinstance(lambda_node.body, ast.Compare):
try:
for operand in lambda_node.body.values:
if not isinstance(operand, ast.Compare):
raise InvalidParachuteTrigger(
"Invalid expression structure: not a Compare."
) from None
except AttributeError:
raise InvalidParachuteTrigger(
"Invalid expression structure: not a Compare."
) from None

# Restricting access to functions or attributes
for node in ast.walk(lambda_node):
if isinstance(node, ast.Call):
raise InvalidParachuteTrigger(
"Calling functions is not allowed in the expression."
) from None
if isinstance(node, ast.Attribute):
raise InvalidParachuteTrigger(
"Accessing attributes is not allowed in the expression."
) from None
return True
return False
2 changes: 1 addition & 1 deletion lib/settings/gunicorn.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def post_fork(server, worker): # pylint: disable=unused-argument
uptrace.configure_opentelemetry(
dsn=Secrets.get_secret("UPTRACE_DSN"),
service_name="infinity-api",
service_version="2.0.0",
service_version="2.1.0",
deployment_environment="production",
)
from lib.api import ( # pylint: disable=import-outside-toplevel
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dependencies = {file = ["requirements.txt"]}

[project]
name = "Infinity-API"
version = "2.0.0"
version = "2.1.0"
GabrielBarberini marked this conversation as resolved.
Show resolved Hide resolved
description = "RESTFULL open API for rocketpy"
dynamic = ["dependencies"]
requires-python = ">=3.12"
Expand Down
Loading