Skip to content
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
70 changes: 10 additions & 60 deletions scripts/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
param_desc as extract_param_description,
infer_type as infer_param_type,
extract_block,
extract_event,
block_icon,
)

Expand Down Expand Up @@ -326,69 +327,18 @@ def extract_all(self) -> tuple[list[dict], dict[str, str]]:
return all_events, event_import_paths

def _extract_event(self, cls) -> dict:
"""Extract metadata from a single event class."""
sig = inspect.signature(cls.__init__)
docstring = cls.__doc__ or ""

params = []
for name, param in sig.parameters.items():
if name == "self":
continue

default = None if param.default is inspect.Parameter.empty else param.default
params.append({
"name": name,
"type": self._infer_event_param_type(name, default),
"default": self._format_event_default(default),
"description": extract_param_description(docstring, name)
})

"""Extract metadata from a single event class with the canonical
`extract_event`, mapped to the schema of the generated events file."""
event = extract_event(cls)
return {
"type": f"pathsim.events.{cls.__name__}",
"name": cls.__name__,
"eventClass": cls.__name__,
"description": extract_first_line(docstring),
"docstringHtml": rst_to_html(docstring),
"params": params
"type": f"pathsim.events.{event['className']}",
"name": event["className"],
"eventClass": event["className"],
"description": event["description"],
"docstringHtml": event["docstringHtml"],
"params": event["params"],
}

def _infer_event_param_type(self, param_name: str, value: Any) -> str:
"""Infer event parameter type (uses EventParamType)."""
if param_name.startswith('func_'):
return "callable"
if callable(value) and not isinstance(value, type):
return "callable"
if isinstance(value, (int, float)):
return "number"
if isinstance(value, str):
return "string"
if isinstance(value, (list, tuple)):
return "array"
if param_name.endswith('_evt') and 'time' in param_name.lower():
return "array"
if value is None and param_name.startswith('func'):
return "callable"
if value is None and param_name == 'times_evt':
return "array"
return "number"

def _format_event_default(self, value: Any) -> str | None:
"""Format default value for event parameters."""
if value is None:
return "None"
if callable(value) and not isinstance(value, type):
return format_default_py(value) or "lambda t: None"
if isinstance(value, bool):
return "True" if value else "False"
if isinstance(value, (int, float)):
return repr(value)
if isinstance(value, str):
return f'"{value}"'
if isinstance(value, (list, tuple)):
return repr(list(value))
return repr(value)


# =============================================================================
# Simulation Extraction
# =============================================================================
Expand Down
23 changes: 19 additions & 4 deletions scripts/pathview_introspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,10 @@ def format_default(value):
return None


def extract_params_from_signature(cls, docstring):
"""Extract parameters by introspecting the class __init__ signature."""
def extract_params_from_signature(cls, docstring, infer=None):
"""Extract parameters by introspecting the class __init__ signature.
`infer(default, name)` gives the param type, `infer_type` by default."""
infer = infer or infer_type
out = []
try:
sig = inspect.signature(cls.__init__)
Expand All @@ -148,7 +150,7 @@ def extract_params_from_signature(cls, docstring):
out.append({
"name": pname,
"default": format_default(p.default),
"type": infer_type(p.default, pname),
"type": infer(None if p.default is inspect.Parameter.empty else p.default, pname),
"description": param_desc(docstring, pname),
})
return out
Expand Down Expand Up @@ -267,12 +269,25 @@ def extract_block(cls):
}


def infer_event_type(value, name=""):
"""Infer the param type of an event, matching EventParamType in TS."""
if name.startswith("func") or (callable(value) and not isinstance(value, type)):
return "callable"
if isinstance(value, (int, float)):
return "number"
if isinstance(value, str):
return "string"
if isinstance(value, (list, tuple)) or (name.endswith("_evt") and "time" in name.lower()):
return "array"
return "number"


def extract_event(cls):
"""Extract canonical event metadata from a class."""
raw_doc = cls.__doc__ or ""
return {
"className": cls.__name__,
"description": first_line(raw_doc),
"docstringHtml": rst_to_html(raw_doc),
"params": extract_params_from_signature(cls, raw_doc),
"params": extract_params_from_signature(cls, raw_doc, infer=infer_event_type),
}
40 changes: 20 additions & 20 deletions src/lib/events/generated/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ export const extractedEvents: EventTypeDefinition[] =
"params": [
{
"name": "func_evt",
"default": null,
"type": "callable",
"default": "None",
"description": "event function, where zeros are events"
},
{
"name": "func_act",
"default": null,
"type": "callable",
"default": "None",
"description": "action function for event resolution"
},
{
"name": "tolerance",
"type": "number",
"default": "0.0001",
"type": "number",
"description": "tolerance to check if detection is close to actual event"
}
]
Expand All @@ -41,20 +41,20 @@ export const extractedEvents: EventTypeDefinition[] =
"params": [
{
"name": "func_evt",
"default": null,
"type": "callable",
"default": "None",
"description": ""
},
{
"name": "func_act",
"default": null,
"type": "callable",
"default": "None",
"description": ""
},
{
"name": "tolerance",
"type": "number",
"default": "0.0001",
"type": "number",
"description": ""
}
]
Expand All @@ -68,20 +68,20 @@ export const extractedEvents: EventTypeDefinition[] =
"params": [
{
"name": "func_evt",
"default": null,
"type": "callable",
"default": "None",
"description": ""
},
{
"name": "func_act",
"default": null,
"type": "callable",
"default": "None",
"description": ""
},
{
"name": "tolerance",
"type": "number",
"default": "0.0001",
"type": "number",
"description": ""
}
]
Expand All @@ -95,32 +95,32 @@ export const extractedEvents: EventTypeDefinition[] =
"params": [
{
"name": "t_start",
"type": "number",
"default": "0",
"type": "number",
"description": "starting time for schedule"
},
{
"name": "t_end",
"default": null,
"type": "number",
"default": "None",
"description": "termination time for schedule"
},
{
"name": "t_period",
"type": "number",
"default": "1",
"type": "number",
"description": "time period of schedule, when events are triggered"
},
{
"name": "func_act",
"default": null,
"type": "callable",
"default": "None",
"description": "action function for event resolution"
},
{
"name": "tolerance",
"type": "number",
"default": "1e-16",
"type": "number",
"description": "tolerance to check if detection is close to actual event"
}
]
Expand All @@ -134,20 +134,20 @@ export const extractedEvents: EventTypeDefinition[] =
"params": [
{
"name": "times_evt",
"default": null,
"type": "array",
"default": "None",
"description": "list of event times in ascending order"
},
{
"name": "func_act",
"default": null,
"type": "callable",
"default": "None",
"description": "action function for event resolution"
},
{
"name": "tolerance",
"type": "number",
"default": "1e-16",
"type": "number",
"description": "tolerance to check if detection is close to actual event"
}
]
Expand All @@ -161,20 +161,20 @@ export const extractedEvents: EventTypeDefinition[] =
"params": [
{
"name": "func_evt",
"default": null,
"type": "callable",
"default": "None",
"description": ""
},
{
"name": "func_act",
"default": null,
"type": "callable",
"default": "None",
"description": ""
},
{
"name": "tolerance",
"type": "number",
"default": "0.0001",
"type": "number",
"description": ""
}
]
Expand Down
Loading