Skip to content

Commit 74df7fb

Browse files
authored
Fix Stubgen's behavior for Instance Variables in C extensions (#12524)
It is not necessary for instance variables to have the fget attrbute (e.g. instance variable in a C class in an extension) but inspect.isdatadescriptor return True as expected, hence we update the 'is_c_property' check. Since special attributes (like __dict__ etc) also passes 'is_c_property' check, we ignore all such special attributes in 'generate_c_property_stub' while creating the contents of stub file. Also, 'is_c_property_readonly' assumed that the property would always have 'fset' attribute which again is not true for instance variables in C extension. Hence make the check defensive by first checking if 'fset' attribute even exists or not. Fixes #12150.
1 parent 6a24e1d commit 74df7fb

File tree

3 files changed

+36
-10
lines changed

3 files changed

+36
-10
lines changed

mypy/stubgenc.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,11 @@ def is_c_classmethod(obj: object) -> bool:
121121

122122

123123
def is_c_property(obj: object) -> bool:
124-
return inspect.isdatadescriptor(obj) and hasattr(obj, 'fget')
124+
return inspect.isdatadescriptor(obj) or hasattr(obj, 'fget')
125125

126126

127127
def is_c_property_readonly(prop: Any) -> bool:
128-
return prop.fset is None
128+
return hasattr(prop, 'fset') and prop.fset is None
129129

130130

131131
def is_c_type(obj: object) -> bool:
@@ -287,6 +287,10 @@ def infer_prop_type(docstr: Optional[str]) -> Optional[str]:
287287
else:
288288
return None
289289

290+
# Ignore special properties/attributes.
291+
if name.startswith('__') and name.endswith('__'):
292+
return
293+
290294
inferred = infer_prop_type(getattr(obj, '__doc__', None))
291295
if not inferred:
292296
fget = getattr(obj, 'fget', None)

mypy/test/teststubgen.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
)
2121
from mypy.stubutil import walk_packages, remove_misplaced_type_comments, common_dir_prefix
2222
from mypy.stubgenc import (
23-
generate_c_type_stub, infer_method_sig, generate_c_function_stub, generate_c_property_stub
23+
generate_c_type_stub, infer_method_sig, generate_c_function_stub, generate_c_property_stub,
24+
is_c_property_readonly
2425
)
2526
from mypy.stubdoc import (
2627
parse_signature, parse_all_signatures, build_signature, find_unique_signatures,
@@ -868,9 +869,34 @@ def get_attribute(self) -> None:
868869
pass
869870
attribute = property(get_attribute, doc="")
870871

871-
output: List[str] = []
872-
generate_c_property_stub('attribute', TestClass.attribute, [], [], output, readonly=True)
873-
assert_equal(output, ['@property', 'def attribute(self) -> str: ...'])
872+
readwrite_properties: List[str] = []
873+
readonly_properties: List[str] = []
874+
generate_c_property_stub('attribute', TestClass.attribute, [],
875+
readwrite_properties, readonly_properties,
876+
is_c_property_readonly(TestClass.attribute))
877+
assert_equal(readwrite_properties, [])
878+
assert_equal(readonly_properties, ['@property', 'def attribute(self) -> str: ...'])
879+
880+
def test_generate_c_property_with_rw_property(self) -> None:
881+
class TestClass:
882+
def __init__(self) -> None:
883+
self._attribute = 0
884+
885+
@property
886+
def attribute(self) -> int:
887+
return self._attribute
888+
889+
@attribute.setter
890+
def attribute(self, value: int) -> None:
891+
self._attribute = value
892+
893+
readwrite_properties: List[str] = []
894+
readonly_properties: List[str] = []
895+
generate_c_property_stub("attribute", type(TestClass.attribute), [],
896+
readwrite_properties, readonly_properties,
897+
is_c_property_readonly(TestClass.attribute))
898+
assert_equal(readwrite_properties, ['attribute: Any'])
899+
assert_equal(readonly_properties, [])
874900

875901
def test_generate_c_type_with_single_arg_generic(self) -> None:
876902
class TestClass:

test-data/stubgen/pybind11_mypy_demo/basics.pyi

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ PI: float
55

66
class Point:
77
class AngleUnit:
8-
__doc__: ClassVar[str] = ... # read-only
9-
__members__: ClassVar[dict] = ... # read-only
108
__entries: ClassVar[dict] = ...
119
degree: ClassVar[Point.AngleUnit] = ...
1210
radian: ClassVar[Point.AngleUnit] = ...
@@ -22,8 +20,6 @@ class Point:
2220
def name(self) -> str: ...
2321

2422
class LengthUnit:
25-
__doc__: ClassVar[str] = ... # read-only
26-
__members__: ClassVar[dict] = ... # read-only
2723
__entries: ClassVar[dict] = ...
2824
inch: ClassVar[Point.LengthUnit] = ...
2925
mm: ClassVar[Point.LengthUnit] = ...

0 commit comments

Comments
 (0)