82 """Introspect a scripting extension base class and return a JSON schema
83 describing its members. Used by `scripting extension generate` (via
84 `ScriptInterpreterPython::GetExtensionSchema`) to emit a skeleton
85 subclass with `# TODO: Implement` stubs for each method the base class
86 defines. Each method entry carries the signature, type hints,
87 docstring, and whether it's `@abstractmethod`, so the generator can
88 decide which methods to stub out (all of them with `-a`, otherwise
89 just the abstract ones). The schema also lists non-callable
90 attributes the base class exposes -- class-level values plus
91 class-body type annotations -- so the generator can advertise them in
92 the derived class' docstring. A `typing_imports` field enumerates
93 every `typing` generic (`Optional`, `Union`, ...) referenced by the
94 signatures or attribute types, so the generator can add the right
95 `from typing import` line without having to re-scan strings."""
96 import inspect, json, typing
100 def _record_typing(s):
106 for name
in typing.__all__:
108 used_typing.add(name)
115 if isinstance(t, type):
116 if t.__module__ ==
"builtins":
118 return f
"{t.__module__}.{t.__name__}"
123 formatted = str(t).replace(
"typing.",
"").replace(
"NoneType",
"None")
124 _record_typing(formatted)
127 def _build_signature(func):
133 hints = typing.get_type_hints(func)
136 sig = inspect.signature(func)
138 for name, param
in sig.parameters.items():
141 piece += f
": {_fmt_type(hints[name])}"
142 if param.default
is not inspect.Parameter.empty:
143 piece += f
" = {param.default!r}"
145 rendered =
"(" +
", ".join(parts) +
")"
146 if "return" in hints:
147 rendered += f
" -> {_fmt_type(hints['return'])}"
150 def _get_function_metadata(func):
152 hints = typing.get_type_hints(func)
153 type_hints = {k: str(v)
for k, v
in hints.items()}
157 "signature": _build_signature(func),
158 "type_hints": type_hints,
159 "is_abstract": getattr(func,
"__isabstractmethod__",
False),
160 "doc": inspect.getdoc(func),
164 class_hints = typing.get_type_hints(cls)
171 for name, member
in inspect.getmembers(cls):
172 if inspect.isfunction(member):
173 members.append({
"name": name, **_get_function_metadata(member)})
175 if name.startswith(
"_"):
177 entry = {
"name": name}
178 if name
in class_hints:
179 entry[
"type"] = _fmt_type(class_hints[name])
180 attributes.append(entry)
186 for name
in class_hints:
187 if name.startswith(
"_")
or name
in seen_attrs:
189 attributes.append({
"name": name,
"type": _fmt_type(class_hints[name])})
194 "class": cls.__name__,
195 "module": cls.__module__,
196 "doc": inspect.getdoc(cls),
198 "attributes": attributes,
199 "typing_imports": sorted(used_typing),
201 separators=(
",",
":"),