def test_generate_c_property_with_pybind11(self) -> None: """Signatures included by PyBind11 inside property.fget are read.""" class TestClass: def get_attribute(self) -> None: """ (self: TestClass) -> str """ pass attribute = property(get_attribute, doc="") output = [] # type: List[str] generate_c_property_stub('attribute', TestClass.attribute, output, readonly=True) assert_equal(output, ['@property', 'def attribute(self) -> str: ...'])
def test_generate_c_property_with_pybind11(self) -> None: """Signatures included by PyBind11 inside property.fget are read.""" class TestClass: def get_attribute(self) -> None: """ (self: TestClass) -> str """ pass attribute = property(get_attribute, doc="") readwrite_properties: List[str] = [] readonly_properties: List[str] = [] generate_c_property_stub('attribute', TestClass.attribute, [], readwrite_properties, readonly_properties, is_c_property_readonly(TestClass.attribute)) assert_equal(readwrite_properties, []) assert_equal(readonly_properties, ['@property', 'def attribute(self) -> str: ...'])
def test_generate_c_property_with_rw_property(self) -> None: class TestClass: def __init__(self) -> None: self._attribute = 0 @property def attribute(self) -> int: return self._attribute @attribute.setter def attribute(self, value: int) -> None: self._attribute = value readwrite_properties: List[str] = [] readonly_properties: List[str] = [] generate_c_property_stub("attribute", type(TestClass.attribute), [], readwrite_properties, readonly_properties, is_c_property_readonly(TestClass.attribute)) assert_equal(readwrite_properties, ['attribute: Any']) assert_equal(readonly_properties, [])
def generate_c_type_stub( module: ModuleType, class_name: str, obj: type, output: List[str], imports: List[str], sigs: Optional[Dict[str, str]] = None, class_sigs: Optional[Dict[str, str]] = None, ) -> 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'. """ # typeshed gives obj.__dict__ the not quite correct type Dict[str, Any] # (it could be a mappingproxy!), which makes mypyc mad, so obfuscate it. obj_dict = getattr(obj, "__dict__") # type: Mapping[str, Any] # noqa items = sorted(obj_dict.items(), key=lambda x: stubgenc.method_name_sort_key(x[0])) methods = [] # type: List[str] properties = [] # type: List[str] done = set() # type: Set[str] for attr, value in items: if stubgenc.is_c_method(value) or stubgenc.is_c_classmethod(value): done.add(attr) if not is_skipped_attribute(attr): if attr == "__new__": # TODO: We should support __new__. if "__init__" in obj_dict: # Avoid duplicate functions if both are present. # But is there any case where .__new__() has a # better signature than __init__() ? continue attr = "__init__" if stubgenc.is_c_classmethod(value): methods.append("@classmethod") self_var = "cls" else: self_var = "self" stubgenc.generate_c_function_stub( module, attr, value, methods, imports=imports, self_var=self_var, sigs=sigs, class_name=class_name, class_sigs=class_sigs, ) elif stubgenc.is_c_property(value): done.add(attr) stubgenc.generate_c_property_stub( attr, value, properties, stubgenc.is_c_property_readonly(value)) elif attr in obj.__annotations__: done.add(attr) properties.append("%s: %s = ..." % (attr, obj.__annotations__[attr].__name__)) variables = [] for attr, value in items: if stubgenc.is_skipped_attribute(attr): continue for base in obj.mro()[1:]: if attr in base.__dict__: break else: if attr not in done: variables.append("%s: Any = ..." % attr) all_bases = obj.mro() 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 = [] # type: List[type] for base in all_bases: if not any(issubclass(b, base) for b in bases): bases.append(base) if bases: bases_str = "(%s)" % ", ".join( stubgenc.strip_or_import(stubgenc.get_type_fullname(base), module, imports) for base in bases) else: bases_str = "" output.append('') if dataclasses.is_dataclass(obj): output.append("@dataclasses.dataclass") if not methods and not variables and not properties: output.append("class %s%s: ..." % (class_name, bases_str)) else: output.append("class %s%s:" % (class_name, bases_str)) for variable in variables: output.append(" %s" % variable) for method in methods: output.append(" %s" % method) for prop in properties: output.append(" %s" % prop)