Update to allow non-class objects at module level.

This commit is contained in:
Mikaël Capelle
2026-05-08 12:13:06 +02:00
parent b33b8bceb1
commit 92dec5b19a
5 changed files with 41 additions and 36 deletions
+5 -5
View File
@@ -12,15 +12,15 @@ from .mtypes import Class, Constant, Enum, Function, PyTyping
from .parser import is_enum
from .register import MobaseRegister
from .utils import Settings, clean_class
from .writer import Writer, is_list_of_functions
from .writer import Writer, is_list_of
LOGGER = logging.getLogger(__package__)
def extract_objects(
module: object, skips: Sequence[str] = []
) -> list[tuple[str, type]]:
objects: list[tuple[str, type]] = []
) -> list[tuple[str, object]]:
objects: list[tuple[str, object]] = []
assert hasattr(module, "__name__")
module_name: str = module.__name__ # type: ignore
@@ -140,7 +140,7 @@ def main() -> None:
}
# list of objects directly in mobase
module_objects: dict[str, list[tuple[str, type]]] = {
module_objects: dict[str, list[tuple[str, object]]] = {
"mobase": extract_objects(
mobase,
[
@@ -181,7 +181,7 @@ def main() -> None:
elif isinstance(c, (PyTyping, Constant)):
...
elif is_list_of_functions(c):
elif is_list_of(c, Function):
settings.patch_functions(c)
else:
+15 -12
View File
@@ -4,7 +4,7 @@ import re
import types
from collections import OrderedDict, defaultdict
from itertools import chain
from typing import Any, Iterable, cast
from typing import Any, Callable, Iterable, cast
from .mtypes import (
Argument,
@@ -118,7 +118,7 @@ def parse_python_signature(s: str, name: str) -> tuple[PyType, list[Argument]]:
return PyType(return_type), arguments
def is_enum(e: type) -> bool:
def is_enum(e: object) -> bool:
"""Check if the given class is an enumeration.
Args:
@@ -144,17 +144,18 @@ class Overload:
self.arguments = arguments
def parse_pybind11_function_docstring(e: type) -> list[Overload]:
def parse_pybind11_function_docstring(name: str, doc: str | None) -> list[Overload]:
"""
Parse the docstring of the given element.
Parse the docstring of a Pybind11 function.
Args:
e: The function to "parse".
name: Name of the function.
doc: The docstring of the function generated by Pybind11.
Returns:
A list of overloads for the given function.
"""
lines = (e.__doc__ or "").strip().split("\n")
lines = (doc or "").strip().split("\n")
signatures: list[str]
if len(lines) == 1:
@@ -162,7 +163,7 @@ def parse_pybind11_function_docstring(e: type) -> list[Overload]:
else:
signatures = []
for line in lines:
m = re.match(rf"^[0-9]+[.]\s+({e.__name__}.*)$", line)
m = re.match(rf"^[0-9]+[.]\s+({name}.*)$", line)
if m:
signatures.append(m.group(1).strip())
@@ -174,16 +175,16 @@ def parse_pybind11_function_docstring(e: type) -> list[Overload]:
signature = signature.replace("MOBase::", "mobase.").replace("::", ".")
try:
return_type, arguments = parse_python_signature(signature, e.__name__)
return_type, arguments = parse_python_signature(signature, name)
except ValueError as err:
raise ValueError(f"invalid signature: {e.__name__}, {e.__doc__}") from err
raise ValueError(f"invalid signature: {name}, {doc}") from err
overloads.append(Overload(return_type=return_type, arguments=arguments))
return overloads
def make_functions(e: type) -> list[Function]:
overloads = parse_pybind11_function_docstring(e)
def make_functions(e: Callable[..., Any]) -> list[Function]:
overloads = parse_pybind11_function_docstring(e.__name__, e.__doc__)
return [
Function(
@@ -295,7 +296,9 @@ def make_class(e: type, register: MobaseRegister) -> Class:
# otherwise we parse the docstring
else:
overloads = parse_pybind11_function_docstring(method)
overloads = parse_pybind11_function_docstring(
method.__name__, method.__doc__
)
for overload in overloads:
args = overload.arguments
+4 -4
View File
@@ -15,14 +15,14 @@ class MobaseRegister:
objects: dict[str, Class | Constant | list[Function] | PyTyping]
def __init__(self) -> None:
self.raw_objects: dict[str, type] = OrderedDict()
self.raw_objects: dict[str, object] = OrderedDict()
self.objects = {}
def add_object(self, name: str, object: type) -> None:
self.raw_objects[name] = object
def add_object(self, n: str, o: object, /) -> None:
self.raw_objects[n] = o
def make_object(
self, name: str, e: type | None = None
self, name: str, e: object | None = None
) -> Class | list[Function] | Constant | PyTyping:
"""
Construct a Function, Class, Constant or Enum for the given object.
+9 -9
View File
@@ -215,7 +215,7 @@ class Settings:
# Check the args:
if function_settings.args is not None:
if len(function_settings.args) != len(fn.args):
LOGGER.warn(
LOGGER.warning(
f"Mismatch number of arguments for function "
f"mobase.{setting_name}."
)
@@ -243,7 +243,7 @@ class Settings:
fn.deprecated = function_settings.deprecated
else:
LOGGER.warn(
LOGGER.warning(
"Missing settings for function mobase.{}.".format(setting_name)
)
@@ -268,7 +268,7 @@ class Settings:
class_settings = self._get_class_settings(cls.canonical_name)
if class_settings is None:
LOGGER.warn("Class {} not found in settings.".format(cls.canonical_name))
LOGGER.warning("Class {} not found in settings.".format(cls.canonical_name))
return
if "__doc__" in class_settings and class_settings["__doc__"] is not None:
@@ -306,7 +306,7 @@ class Settings:
if "type" in settings_property:
prop.type = PyType(settings_property["type"])
else:
LOGGER.warn(
LOGGER.warning(
"Missing type for property {}.{}.".format(
cls.canonical_name, prop.name
)
@@ -320,7 +320,7 @@ class Settings:
prop.doc = settings_property["desc"]
else:
LOGGER.warn(
LOGGER.warning(
"Missing description for property {}.{}.".format(
cls.canonical_name, prop.name
)
@@ -366,7 +366,7 @@ class Settings:
if function_settings.args is not None:
method_arguments = m.args if m.is_static() else m.args[1:]
if len(function_settings.args) != len(method_arguments):
LOGGER.warn(
LOGGER.warning(
"Mismatch number of arguments for method {}.{}.".format(
cls.canonical_name, settings_name
)
@@ -380,7 +380,7 @@ class Settings:
not method_arg.name.startswith("arg")
and method_arg.name != settings_arg.name
):
LOGGER.warn(
LOGGER.warning(
(
"Mismatch argument name for method {}.{}: "
"{} {}, using {}."
@@ -429,7 +429,7 @@ class Settings:
if n_overloads > 0 and missing_settings:
for settings_name in missing_settings:
LOGGER.warn(
LOGGER.warning(
"Missing settings for method {}.{}.".format(
cls.canonical_name, settings_name
)
@@ -447,7 +447,7 @@ class Settings:
# Print items missing in mobase
missing_items = [k for k, v in keys.items() if not v]
if missing_items:
LOGGER.warn(
LOGGER.warning(
"The following members were found in settings but not in the actual"
" class {}: {}.".format(cls.canonical_name, ", ".join(missing_items))
)
+8 -6
View File
@@ -1,5 +1,5 @@
import logging
from typing import Any, Iterable, TextIO
from typing import Any, TextIO
from typing_extensions import TypeIs
@@ -9,10 +9,12 @@ from .utils import Settings
LOGGER = logging.getLogger(__package__)
def is_list_of_functions(e: Any | Iterable[Any]) -> TypeIs[list[Function]]:
if not isinstance(e, list):
return False
return all(isinstance(x, Function) for x in e)
def is_list_of_any(e: Any) -> TypeIs[list[Any]]:
return isinstance(e, list)
def is_list_of[T](e: Any, t: type[T]) -> TypeIs[list[T]]:
return is_list_of_any(e) and all(isinstance(x, t) for x in e)
class Writer:
@@ -270,7 +272,7 @@ class Writer:
if isinstance(e, Class):
self.print_class(e)
elif is_list_of_functions(e):
elif is_list_of(e, Function):
for fn in e:
self.print_function(fn)