diff --git a/scripts/extract.py b/scripts/extract.py index f0391596..9eae4646 100644 --- a/scripts/extract.py +++ b/scripts/extract.py @@ -33,6 +33,7 @@ param_desc as extract_param_description, infer_type as infer_param_type, extract_block, + extract_event, block_icon, ) @@ -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 # ============================================================================= diff --git a/scripts/pathview_introspect.py b/scripts/pathview_introspect.py index 27786e33..a29f4707 100644 --- a/scripts/pathview_introspect.py +++ b/scripts/pathview_introspect.py @@ -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__) @@ -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 @@ -267,6 +269,19 @@ 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 "" @@ -274,5 +289,5 @@ def extract_event(cls): "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), } diff --git a/src/lib/events/generated/events.ts b/src/lib/events/generated/events.ts index 95e399a3..59b842f6 100644 --- a/src/lib/events/generated/events.ts +++ b/src/lib/events/generated/events.ts @@ -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" } ] @@ -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": "" } ] @@ -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": "" } ] @@ -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" } ] @@ -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" } ] @@ -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": "" } ]