mirror of
https://github.com/ModOrganizer2/pystubs-generation.git
synced 2026-07-27 14:07:13 -07:00
Update to allow non-class objects at module level.
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user