blob: 0ad79a4265b3e5b408014b6df1b28faa69c4e57c [file] [log] [blame]
#!/usr/bin/env python3
"""Stub generator for C modules.
The public interface is via the mypy.stubgen module.
"""
from __future__ import annotations
import glob
import importlib
import inspect
import keyword
import os.path
from types import FunctionType, ModuleType
from typing import Any, Mapping
from mypy.fastparse import parse_type_comment
from mypy.moduleinspect import is_c_module
from mypy.stubdoc import (
ArgSig,
FunctionSig,
Sig,
find_unique_signatures,
infer_arg_sig_from_anon_docstring,
infer_prop_type_from_docstring,
infer_ret_type_sig_from_anon_docstring,
infer_ret_type_sig_from_docstring,
infer_sig_from_docstring,
parse_all_signatures,
)
from mypy.stubutil import (
BaseStubGenerator,
ClassInfo,
FunctionContext,
SignatureGenerator,
infer_method_arg_types,
infer_method_ret_type,
)
class ExternalSignatureGenerator(SignatureGenerator):
def __init__(
self, func_sigs: dict[str, str] | None = None, class_sigs: dict[str, str] | None = None
):
"""
Takes a mapping of function/method names to signatures and class name to
class signatures (usually corresponds to __init__).
"""
self.func_sigs = func_sigs or {}
self.class_sigs = class_sigs or {}
@classmethod
def from_doc_dir(cls, doc_dir: str) -> ExternalSignatureGenerator:
"""Instantiate from a directory of .rst files."""
all_sigs: list[Sig] = []
all_class_sigs: list[Sig] = []
for path in glob.glob(f"{doc_dir}/*.rst"):
with open(path) as f:
loc_sigs, loc_class_sigs = parse_all_signatures(f.readlines())
all_sigs += loc_sigs
all_class_sigs += loc_class_sigs
sigs = dict(find_unique_signatures(all_sigs))
class_sigs = dict(find_unique_signatures(all_class_sigs))
return ExternalSignatureGenerator(sigs, class_sigs)
def get_function_sig(
self, default_sig: FunctionSig, ctx: FunctionContext
) -> list[FunctionSig] | None:
# method:
if (
ctx.class_info
and ctx.name in ("__new__", "__init__")
and ctx.name not in self.func_sigs
and ctx.class_info.name in self.class_sigs
):
return [
FunctionSig(
name=ctx.name,
args=infer_arg_sig_from_anon_docstring(self.class_sigs[ctx.class_info.name]),
ret_type=infer_method_ret_type(ctx.name),
)
]
# function:
if ctx.name not in self.func_sigs:
return None
inferred = [
FunctionSig(
name=ctx.name,
args=infer_arg_sig_from_anon_docstring(self.func_sigs[ctx.name]),
ret_type=None,
)
]
if ctx.class_info:
return self.remove_self_type(inferred, ctx.class_info.self_var)
else:
return inferred
def get_property_type(self, default_type: str | None, ctx: FunctionContext) -> str | None:
return None
class DocstringSignatureGenerator(SignatureGenerator):
def get_function_sig(
self, default_sig: FunctionSig, ctx: FunctionContext
) -> list[FunctionSig] | None:
inferred = infer_sig_from_docstring(ctx.docstring, ctx.name)
if inferred:
assert ctx.docstring is not None
if is_pybind11_overloaded_function_docstring(ctx.docstring, ctx.name):
# Remove pybind11 umbrella (*args, **kwargs) for overloaded functions
del inferred[-1]
if ctx.class_info:
if not inferred and ctx.name == "__init__":
# look for class-level constructor signatures of the form <class_name>(<signature>)
inferred = infer_sig_from_docstring(ctx.class_info.docstring, ctx.class_info.name)
if inferred:
inferred = [sig._replace(name="__init__") for sig in inferred]
return self.remove_self_type(inferred, ctx.class_info.self_var)
else:
return inferred
def get_property_type(self, default_type: str | None, ctx: FunctionContext) -> str | None:
"""Infer property type from docstring or docstring signature."""
if ctx.docstring is not None:
inferred = infer_ret_type_sig_from_anon_docstring(ctx.docstring)
if not inferred:
inferred = infer_ret_type_sig_from_docstring(ctx.docstring, ctx.name)
if not inferred:
inferred = infer_prop_type_from_docstring(ctx.docstring)
return inferred
else:
return None
def is_pybind11_overloaded_function_docstring(docstring: str, name: str) -> bool:
return docstring.startswith(f"{name}(*args, **kwargs)\nOverloaded function.\n\n")
def generate_stub_for_c_module(
module_name: str,
target: str,
known_modules: list[str],
doc_dir: str = "",
*,
include_private: bool = False,
export_less: bool = False,
include_docstrings: bool = False,
) -> None:
"""Generate stub for C module.
Signature generators are called in order until a list of signatures is returned. The order
is:
- signatures inferred from .rst documentation (if given)
- simple runtime introspection (looking for docstrings and attributes
with simple builtin types)
- fallback based special method names or "(*args, **kwargs)"
If directory for target doesn't exist it will be created. Existing stub
will be overwritten.
"""
subdir = os.path.dirname(target)
if subdir and not os.path.isdir(subdir):
os.makedirs(subdir)
gen = InspectionStubGenerator(
module_name,
known_modules,
doc_dir,
include_private=include_private,
export_less=export_less,
include_docstrings=include_docstrings,
)
gen.generate_module()
output = gen.output()
with open(target, "w") as file:
file.write(output)
class CFunctionStub:
"""
Class that mimics a C function in order to provide parseable docstrings.
"""
def __init__(self, name: str, doc: str, is_abstract: bool = False):
self.__name__ = name
self.__doc__ = doc
self.__abstractmethod__ = is_abstract
@classmethod
def _from_sig(cls, sig: FunctionSig, is_abstract: bool = False) -> CFunctionStub:
return CFunctionStub(sig.name, sig.format_sig()[:-4], is_abstract)
@classmethod
def _from_sigs(cls, sigs: list[FunctionSig], is_abstract: bool = False) -> CFunctionStub:
return CFunctionStub(
sigs[0].name, "\n".join(sig.format_sig()[:-4] for sig in sigs), is_abstract
)
def __get__(self) -> None:
"""
This exists to make this object look like a method descriptor and thus
return true for CStubGenerator.ismethod()
"""
pass
class InspectionStubGenerator(BaseStubGenerator):
"""Stub generator that does not parse code.
Generation is performed by inspecting the module's contents, and thus works
for highly dynamic modules, pyc files, and C modules (via the CStubGenerator
subclass).
"""
def __init__(
self,
module_name: str,
known_modules: list[str],
doc_dir: str = "",
_all_: list[str] | None = None,
include_private: bool = False,
export_less: bool = False,
include_docstrings: bool = False,
module: ModuleType | None = None,
) -> None:
self.doc_dir = doc_dir
if module is None:
self.module = importlib.import_module(module_name)
else:
self.module = module
self.is_c_module = is_c_module(self.module)
self.known_modules = known_modules
self.resort_members = self.is_c_module
super().__init__(_all_, include_private, export_less, include_docstrings)
self.module_name = module_name
def get_default_function_sig(self, func: object, ctx: FunctionContext) -> FunctionSig:
argspec = None
if not self.is_c_module:
# Get the full argument specification of the function
try:
argspec = inspect.getfullargspec(func)
except TypeError:
# some callables cannot be inspected, e.g. functools.partial
pass
if argspec is None:
if ctx.class_info is not None:
# method:
return FunctionSig(
name=ctx.name,
args=infer_c_method_args(ctx.name, ctx.class_info.self_var),
ret_type=infer_method_ret_type(ctx.name),
)
else:
# function:
return FunctionSig(
name=ctx.name,
args=[ArgSig(name="*args"), ArgSig(name="**kwargs")],
ret_type=None,
)
# Extract the function arguments, defaults, and varargs
args = argspec.args
defaults = argspec.defaults
varargs = argspec.varargs
kwargs = argspec.varkw
annotations = argspec.annotations
def get_annotation(key: str) -> str | None:
if key not in annotations:
return None
argtype = annotations[key]
if argtype is None:
return "None"
if not isinstance(argtype, str):
return self.get_type_fullname(argtype)
return argtype
arglist: list[ArgSig] = []
# Add the arguments to the signature
for i, arg in enumerate(args):
# Check if the argument has a default value
if defaults and i >= len(args) - len(defaults):
default_value = defaults[i - (len(args) - len(defaults))]
if arg in annotations:
argtype = annotations[arg]
else:
argtype = self.get_type_annotation(default_value)
if argtype == "None":
# None is not a useful annotation, but we can infer that the arg
# is optional
incomplete = self.add_name("_typeshed.Incomplete")
argtype = f"{incomplete} | None"
arglist.append(ArgSig(arg, argtype, default=True))
else:
arglist.append(ArgSig(arg, get_annotation(arg), default=False))
# Add *args if present
if varargs:
arglist.append(ArgSig(f"*{varargs}", get_annotation(varargs)))
# Add **kwargs if present
if kwargs:
arglist.append(ArgSig(f"**{kwargs}", get_annotation(kwargs)))
# add types for known special methods
if ctx.class_info is not None and all(
arg.type is None and arg.default is False for arg in arglist
):
new_args = infer_method_arg_types(
ctx.name, ctx.class_info.self_var, [arg.name for arg in arglist if arg.name]
)
if new_args is not None:
arglist = new_args
ret_type = get_annotation("return") or infer_method_ret_type(ctx.name)
return FunctionSig(ctx.name, arglist, ret_type)
def get_sig_generators(self) -> list[SignatureGenerator]:
if not self.is_c_module:
return []
else:
sig_generators: list[SignatureGenerator] = [DocstringSignatureGenerator()]
if self.doc_dir:
# Collect info from docs (if given). Always check these first.
sig_generators.insert(0, ExternalSignatureGenerator.from_doc_dir(self.doc_dir))
return sig_generators
def strip_or_import(self, type_name: str) -> str:
"""Strips unnecessary module names from typ.
If typ represents a type that is inside module or is a type coming from builtins, remove
module declaration from it. Return stripped name of the type.
Arguments:
typ: name of the type
"""
local_modules = ["builtins", self.module_name]
parsed_type = parse_type_comment(type_name, 0, 0, None)[1]
assert parsed_type is not None, type_name
return self.print_annotation(parsed_type, self.known_modules, local_modules)
def get_obj_module(self, obj: object) -> str | None:
"""Return module name of the object."""
return getattr(obj, "__module__", None)
def is_defined_in_module(self, obj: object) -> bool:
"""Check if object is considered defined in the current module."""
module = self.get_obj_module(obj)
return module is None or module == self.module_name
def generate_module(self) -> None:
all_items = self.get_members(self.module)
if self.resort_members:
all_items = sorted(all_items, key=lambda x: x[0])
items = []
for name, obj in all_items:
if inspect.ismodule(obj) and obj.__name__ in self.known_modules:
module_name = obj.__name__
if module_name.startswith(self.module_name + "."):
# from {.rel_name} import {mod_name} as {name}
pkg_name, mod_name = module_name.rsplit(".", 1)
rel_module = pkg_name[len(self.module_name) :] or "."
self.import_tracker.add_import_from(rel_module, [(mod_name, name)])
self.import_tracker.reexport(name)
else:
# import {module_name} as {name}
self.import_tracker.add_import(module_name, name)
self.import_tracker.reexport(name)
elif self.is_defined_in_module(obj) and not inspect.ismodule(obj):
# process this below
items.append((name, obj))
else:
# from {obj_module} import {obj_name}
obj_module_name = self.get_obj_module(obj)
if obj_module_name:
self.import_tracker.add_import_from(obj_module_name, [(name, None)])
if self.should_reexport(name, obj_module_name, name_is_alias=False):
self.import_tracker.reexport(name)
self.set_defined_names(set([name for name, obj in all_items if not inspect.ismodule(obj)]))
if self.resort_members:
functions: list[str] = []
types: list[str] = []
variables: list[str] = []
else:
output: list[str] = []
functions = types = variables = output
for name, obj in items:
if self.is_function(obj):
self.generate_function_stub(name, obj, output=functions)
elif inspect.isclass(obj):
self.generate_class_stub(name, obj, output=types)
else:
self.generate_variable_stub(name, obj, output=variables)
self._output = []
if self.resort_members:
for line in variables:
self._output.append(line + "\n")
for line in types:
if line.startswith("class") and self._output and self._output[-1]:
self._output.append("\n")
self._output.append(line + "\n")
if self._output and functions:
self._output.append("\n")
for line in functions:
self._output.append(line + "\n")
else:
for i, line in enumerate(output):
if (
self._output
and line.startswith("class")
and (
not self._output[-1].startswith("class")
or (len(output) > i + 1 and output[i + 1].startswith(" "))
)
) or (
self._output
and self._output[-1].startswith("def")
and not line.startswith("def")
):
self._output.append("\n")
self._output.append(line + "\n")
self.check_undefined_names()
def is_skipped_attribute(self, attr: str) -> bool:
return (
attr
in (
"__class__",
"__getattribute__",
"__str__",
"__repr__",
"__doc__",
"__dict__",
"__module__",
"__weakref__",
"__annotations__",
)
or attr in self.IGNORED_DUNDERS
or is_pybind_skipped_attribute(attr) # For pickling
or keyword.iskeyword(attr)
)
def get_members(self, obj: object) -> list[tuple[str, Any]]:
obj_dict: Mapping[str, Any] = getattr(obj, "__dict__") # noqa: B009
results = []
for name in obj_dict:
if self.is_skipped_attribute(name):
continue
# Try to get the value via getattr
try:
value = getattr(obj, name)
except AttributeError:
continue
else:
results.append((name, value))
return results
def get_type_annotation(self, obj: object) -> str:
"""
Given an instance, return a string representation of its type that is valid
to use as a type annotation.
"""
if obj is None or obj is type(None):
return "None"
elif inspect.isclass(obj):
return "type[{}]".format(self.get_type_fullname(obj))
elif isinstance(obj, FunctionType):
return self.add_name("typing.Callable")
elif isinstance(obj, ModuleType):
return self.add_name("types.ModuleType", require=False)
else:
return self.get_type_fullname(type(obj))
def is_function(self, obj: object) -> bool:
if self.is_c_module:
return inspect.isbuiltin(obj)
else:
return inspect.isfunction(obj)
def is_method(self, class_info: ClassInfo, name: str, obj: object) -> bool:
if self.is_c_module:
return inspect.ismethoddescriptor(obj) or type(obj) in (
type(str.index),
type(str.__add__),
type(str.__new__),
)
else:
# this is valid because it is only called on members of a class
return inspect.isfunction(obj)
def is_classmethod(self, class_info: ClassInfo, name: str, obj: object) -> bool:
if self.is_c_module:
return inspect.isbuiltin(obj) or type(obj).__name__ in (
"classmethod",
"classmethod_descriptor",
)
else:
return inspect.ismethod(obj)
def is_staticmethod(self, class_info: ClassInfo | None, name: str, obj: object) -> bool:
if self.is_c_module:
return False
else:
return class_info is not None and isinstance(
inspect.getattr_static(class_info.cls, name), staticmethod
)
@staticmethod
def is_abstract_method(obj: object) -> bool:
return getattr(obj, "__abstractmethod__", False)
@staticmethod
def is_property(class_info: ClassInfo, name: str, obj: object) -> bool:
return inspect.isdatadescriptor(obj) or hasattr(obj, "fget")
@staticmethod
def is_property_readonly(prop: Any) -> bool:
return hasattr(prop, "fset") and prop.fset is None
def is_static_property(self, obj: object) -> bool:
"""For c-modules, whether the property behaves like an attribute"""
if self.is_c_module:
# StaticProperty is from boost-python
return type(obj).__name__ in ("pybind11_static_property", "StaticProperty")
else:
return False
def process_inferred_sigs(self, inferred: list[FunctionSig]) -> None:
for i, sig in enumerate(inferred):
for arg in sig.args:
if arg.type is not None:
arg.type = self.strip_or_import(arg.type)
if sig.ret_type is not None:
inferred[i] = sig._replace(ret_type=self.strip_or_import(sig.ret_type))
def generate_function_stub(
self, name: str, obj: object, *, output: list[str], class_info: ClassInfo | None = None
) -> None:
"""Generate stub for a single function or method.
The result (always a single line) will be appended to 'output'.
If necessary, any required names will be added to 'imports'.
The 'class_name' is used to find signature of __init__ or __new__ in
'class_sigs'.
"""
docstring: Any = getattr(obj, "__doc__", None)
if not isinstance(docstring, str):
docstring = None
ctx = FunctionContext(
self.module_name,
name,
docstring=docstring,
is_abstract=self.is_abstract_method(obj),
class_info=class_info,
)
if self.is_private_name(name, ctx.fullname) or self.is_not_in_all(name):
return
self.record_name(ctx.name)
default_sig = self.get_default_function_sig(obj, ctx)
inferred = self.get_signatures(default_sig, self.sig_generators, ctx)
self.process_inferred_sigs(inferred)
decorators = []
if len(inferred) > 1:
decorators.append("@{}".format(self.add_name("typing.overload")))
if ctx.is_abstract:
decorators.append("@{}".format(self.add_name("abc.abstractmethod")))
if class_info is not None:
if self.is_staticmethod(class_info, name, obj):
decorators.append("@staticmethod")
else:
for sig in inferred:
if not sig.args or sig.args[0].name not in ("self", "cls"):
sig.args.insert(0, ArgSig(name=class_info.self_var))
# a sig generator indicates @classmethod by specifying the cls arg.
if inferred[0].args and inferred[0].args[0].name == "cls":
decorators.append("@classmethod")
output.extend(self.format_func_def(inferred, decorators=decorators, docstring=docstring))
self._fix_iter(ctx, inferred, output)
def _fix_iter(
self, ctx: FunctionContext, inferred: list[FunctionSig], output: list[str]
) -> None:
"""Ensure that objects which implement old-style iteration via __getitem__
are considered iterable.
"""
if (
ctx.class_info
and ctx.class_info.cls is not None
and ctx.name == "__getitem__"
and "__iter__" not in ctx.class_info.cls.__dict__
):
item_type: str | None = None
for sig in inferred:
if sig.args and sig.args[-1].type == "int":
item_type = sig.ret_type
break
if item_type is None:
return
obj = CFunctionStub(
"__iter__", f"def __iter__(self) -> typing.Iterator[{item_type}]\n"
)
self.generate_function_stub("__iter__", obj, output=output, class_info=ctx.class_info)
def generate_property_stub(
self,
name: str,
raw_obj: object,
obj: object,
static_properties: list[str],
rw_properties: list[str],
ro_properties: list[str],
class_info: ClassInfo | None = None,
) -> None:
"""Generate property stub using introspection of 'obj'.
Try to infer type from docstring, append resulting lines to 'output'.
raw_obj : object before evaluation of descriptor (if any)
obj : object after evaluation of descriptor
"""
docstring = getattr(raw_obj, "__doc__", None)
fget = getattr(raw_obj, "fget", None)
if fget:
alt_docstr = getattr(fget, "__doc__", None)
if alt_docstr and docstring:
docstring += alt_docstr
elif alt_docstr:
docstring = alt_docstr
ctx = FunctionContext(
self.module_name, name, docstring=docstring, is_abstract=False, class_info=class_info
)
if self.is_private_name(name, ctx.fullname) or self.is_not_in_all(name):
return
self.record_name(ctx.name)
static = self.is_static_property(raw_obj)
readonly = self.is_property_readonly(raw_obj)
if static:
ret_type: str | None = self.strip_or_import(self.get_type_annotation(obj))
else:
default_sig = self.get_default_function_sig(raw_obj, ctx)
ret_type = default_sig.ret_type
inferred_type = self.get_property_type(ret_type, self.sig_generators, ctx)
if inferred_type is not None:
inferred_type = self.strip_or_import(inferred_type)
if static:
classvar = self.add_name("typing.ClassVar")
trailing_comment = " # read-only" if readonly else ""
if inferred_type is None:
inferred_type = self.add_name("_typeshed.Incomplete")
static_properties.append(
f"{self._indent}{name}: {classvar}[{inferred_type}] = ...{trailing_comment}"
)
else: # regular property
if readonly:
ro_properties.append(f"{self._indent}@property")
sig = FunctionSig(name, [ArgSig("self")], inferred_type)
ro_properties.append(sig.format_sig(indent=self._indent))
else:
if inferred_type is None:
inferred_type = self.add_name("_typeshed.Incomplete")
rw_properties.append(f"{self._indent}{name}: {inferred_type}")
def get_type_fullname(self, typ: type) -> str:
"""Given a type, return a string representation"""
if typ is Any:
return "Any"
typename = getattr(typ, "__qualname__", typ.__name__)
module_name = self.get_obj_module(typ)
assert module_name is not None, typ
if module_name != "builtins":
typename = f"{module_name}.{typename}"
return typename
def get_base_types(self, obj: type) -> list[str]:
all_bases = type.mro(obj)
if all_bases[-1] is object:
# TODO: Is this always object?
del all_bases[-1]
# remove pybind11_object. All classes generated by pybind11 have pybind11_object in their MRO,
# which only overrides a few functions in object type
if all_bases and all_bases[-1].__name__ == "pybind11_object":
del all_bases[-1]
# remove the class itself
all_bases = all_bases[1:]
# Remove base classes of other bases as redundant.
bases: list[type] = []
for base in all_bases:
if not any(issubclass(b, base) for b in bases):
bases.append(base)
return [self.strip_or_import(self.get_type_fullname(base)) for base in bases]
def generate_class_stub(self, class_name: str, cls: type, output: list[str]) -> None:
"""Generate stub for a single class using runtime introspection.
The result lines will be appended to 'output'. If necessary, any
required names will be added to 'imports'.
"""
raw_lookup = getattr(cls, "__dict__") # noqa: B009
items = self.get_members(cls)
if self.resort_members:
items = sorted(items, key=lambda x: method_name_sort_key(x[0]))
names = set(x[0] for x in items)
methods: list[str] = []
types: list[str] = []
static_properties: list[str] = []
rw_properties: list[str] = []
ro_properties: list[str] = []
attrs: list[tuple[str, Any]] = []
self.record_name(class_name)
self.indent()
class_info = ClassInfo(class_name, "", getattr(cls, "__doc__", None), cls)
for attr, value in items:
# use unevaluated descriptors when dealing with property inspection
raw_value = raw_lookup.get(attr, value)
if self.is_method(class_info, attr, value) or self.is_classmethod(
class_info, attr, value
):
if attr == "__new__":
# TODO: We should support __new__.
if "__init__" in names:
# Avoid duplicate functions if both are present.
# But is there any case where .__new__() has a
# better signature than __init__() ?
continue
attr = "__init__"
# FIXME: make this nicer
if self.is_classmethod(class_info, attr, value):
class_info.self_var = "cls"
else:
class_info.self_var = "self"
self.generate_function_stub(attr, value, output=methods, class_info=class_info)
elif self.is_property(class_info, attr, raw_value):
self.generate_property_stub(
attr,
raw_value,
value,
static_properties,
rw_properties,
ro_properties,
class_info,
)
elif inspect.isclass(value) and self.is_defined_in_module(value):
self.generate_class_stub(attr, value, types)
else:
attrs.append((attr, value))
for attr, value in attrs:
if attr == "__hash__" and value is None:
# special case for __hash__
continue
prop_type_name = self.strip_or_import(self.get_type_annotation(value))
classvar = self.add_name("typing.ClassVar")
static_properties.append(f"{self._indent}{attr}: {classvar}[{prop_type_name}] = ...")
self.dedent()
bases = self.get_base_types(cls)
if bases:
bases_str = "(%s)" % ", ".join(bases)
else:
bases_str = ""
if types or static_properties or rw_properties or methods or ro_properties:
output.append(f"{self._indent}class {class_name}{bases_str}:")
for line in types:
if (
output
and output[-1]
and not output[-1].strip().startswith("class")
and line.strip().startswith("class")
):
output.append("")
output.append(line)
for line in static_properties:
output.append(line)
for line in rw_properties:
output.append(line)
for line in methods:
output.append(line)
for line in ro_properties:
output.append(line)
else:
output.append(f"{self._indent}class {class_name}{bases_str}: ...")
def generate_variable_stub(self, name: str, obj: object, output: list[str]) -> None:
"""Generate stub for a single variable using runtime introspection.
The result lines will be appended to 'output'. If necessary, any
required names will be added to 'imports'.
"""
if self.is_private_name(name, f"{self.module_name}.{name}") or self.is_not_in_all(name):
return
self.record_name(name)
type_str = self.strip_or_import(self.get_type_annotation(obj))
output.append(f"{name}: {type_str}")
def method_name_sort_key(name: str) -> tuple[int, str]:
"""Sort methods in classes in a typical order.
I.e.: constructor, normal methods, special methods.
"""
if name in ("__new__", "__init__"):
return 0, name
if name.startswith("__") and name.endswith("__"):
return 2, name
return 1, name
def is_pybind_skipped_attribute(attr: str) -> bool:
return attr.startswith("__pybind11_module_local_")
def infer_c_method_args(
name: str, self_var: str = "self", arg_names: list[str] | None = None
) -> list[ArgSig]:
args: list[ArgSig] | None = None
if name.startswith("__") and name.endswith("__"):
name = name[2:-2]
if name in (
"hash",
"iter",
"next",
"sizeof",
"copy",
"deepcopy",
"reduce",
"getinitargs",
"int",
"float",
"trunc",
"complex",
"bool",
"abs",
"bytes",
"dir",
"len",
"reversed",
"round",
"index",
"enter",
):
args = []
elif name == "getitem":
args = [ArgSig(name="index")]
elif name == "setitem":
args = [ArgSig(name="index"), ArgSig(name="object")]
elif name in ("delattr", "getattr"):
args = [ArgSig(name="name")]
elif name == "setattr":
args = [ArgSig(name="name"), ArgSig(name="value")]
elif name == "getstate":
args = []
elif name == "setstate":
args = [ArgSig(name="state")]
elif name in ("eq", "ne", "lt", "le", "gt", "ge"):
args = [ArgSig(name="other", type="object")]
elif name in (
"add",
"radd",
"sub",
"rsub",
"mul",
"rmul",
"mod",
"rmod",
"floordiv",
"rfloordiv",
"truediv",
"rtruediv",
"divmod",
"rdivmod",
"pow",
"rpow",
"xor",
"rxor",
"or",
"ror",
"and",
"rand",
"lshift",
"rlshift",
"rshift",
"rrshift",
"contains",
"delitem",
"iadd",
"iand",
"ifloordiv",
"ilshift",
"imod",
"imul",
"ior",
"ipow",
"irshift",
"isub",
"itruediv",
"ixor",
):
args = [ArgSig(name="other")]
elif name in ("neg", "pos", "invert"):
args = []
elif name == "get":
args = [ArgSig(name="instance"), ArgSig(name="owner")]
elif name == "set":
args = [ArgSig(name="instance"), ArgSig(name="value")]
elif name == "reduce_ex":
args = [ArgSig(name="protocol")]
elif name == "exit":
args = [
ArgSig(name="type", type="type[BaseException] | None"),
ArgSig(name="value", type="BaseException | None"),
ArgSig(name="traceback", type="types.TracebackType | None"),
]
if args is None:
args = infer_method_arg_types(name, self_var, arg_names)
else:
args = [ArgSig(name=self_var)] + args
if args is None:
args = [ArgSig(name="*args"), ArgSig(name="**kwargs")]
return args