From 73e67fefd13206bdb8c1114b8bbb59cce5247b1b Mon Sep 17 00:00:00 2001 From: Frank Martinez Date: Thu, 9 Oct 2025 19:33:59 -0500 Subject: [PATCH] [bindings] Automatic export of function signatures with annotations to runtime. (PyMethodDef.ml_doc) --- .../bindings/model/generateModel_Python.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/Tools/bindings/model/generateModel_Python.py b/src/Tools/bindings/model/generateModel_Python.py index 1c0ddab78a..08bd9b9fb3 100644 --- a/src/Tools/bindings/model/generateModel_Python.py +++ b/src/Tools/bindings/model/generateModel_Python.py @@ -19,6 +19,8 @@ from model.typedModel import ( SequenceProtocol, ) +SIGNATURE_SEP = re.compile(r"\s+--\s+", re.DOTALL) +SELF_CLS_ARG = re.compile(r"\(\s*(self|cls)(\s*,\s*)?") class ArgumentKind(Enum): PositionOnly = 0 @@ -43,6 +45,8 @@ class FunctionSignature: args: list[FuncArgument] has_keywords: bool docstring: str + annotated_text: str + text: str const_flag: bool = False static_flag: bool = False @@ -121,6 +125,19 @@ class FunctionSignature: ), ) + # Annotated signatures (Not supported by __text_signature__) + returns = ast.unparse(func.returns) if func.returns else "object" + parameters = ast.unparse(func.args) + self.annotated_text = SELF_CLS_ARG.sub("(", f"{func.name}({parameters}) -> {returns}", 1) + + # Not Annotated signatures (supported by __text_signature__) + all_args = [*args.posonlyargs, *args.args, args.vararg, *args.kwonlyargs, args.kwarg] + for item in all_args: + if item: + item.annotation = None + parameters = ast.unparse(args) + self.text = SELF_CLS_ARG.sub(r"($\1\2", f"{func.name}({parameters})", 1) + def get_annotation_str(self, node: ast.AST | None) -> str: if not node: return "object" @@ -195,6 +212,27 @@ class Function: def noargs_flag(self) -> bool: return any(sig.noargs_flag for sig in self.signatures) + def add_signature_docs(self, doc: Documentation) -> None: + if len (self.signatures) == 1: + docstring = [self.signatures[0].text] + signature = [self.signatures[0].annotated_text] + else: + docstring = [sig.text for sig in self.signatures if not sig.is_overload] + signature = [sig.annotated_text for sig in self.signatures if sig.is_overload] + + if not docstring: + return + + user_doc = doc.UserDocu or "" + marker = SIGNATURE_SEP.search(user_doc) + if marker: + user_doc = user_doc[marker.end():].strip() + + docstring.append("--\n") # mark __text_signature__ + docstring.extend(signature) # Include real annotated signature in user docstring + docstring.append(f"\n{user_doc}") # Rest of the docstring + doc.UserDocu = "\n".join(docstring) + def _extract_decorator_kwargs(decorator: ast.expr) -> dict: """ @@ -420,6 +458,7 @@ def _parse_methods(class_node: ast.ClassDef) -> List[Methode]: for func in functions.values(): doc_obj = _parse_docstring_for_documentation(func.docstring) + func.add_signature_docs(doc_obj) method_params = [] signature = func.signature