Skip to content

Commit 4409460

Browse files
committed
Fix __isabstractmethod__ for class/static methods and properties
Includes a minor departure from CPython implementation of inspect.isabstract because TPFLAGS_IS_ABSTRACT is not set in obj.__flags__.
1 parent 2ceeea8 commit 4409460

File tree

5 files changed

+58
-16
lines changed

5 files changed

+58
-16
lines changed

Lib/inspect.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,16 +300,16 @@ def isroutine(object):
300300

301301
def isabstract(object):
302302
"""Return true if the object is an abstract base class (ABC)."""
303+
# TODO: RUSTPYTHON
304+
# TPFLAGS_IS_ABSTRACT is not being set for abstract classes, so this implementation differs from CPython.
303305
if not isinstance(object, type):
304306
return False
305-
if object.__flags__ & TPFLAGS_IS_ABSTRACT:
306-
return True
307307
if not issubclass(type(object), abc.ABCMeta):
308308
return False
309309
if hasattr(object, '__abstractmethods__'):
310310
# It looks like ABCMeta.__new__ has finished running;
311311
# TPFLAGS_IS_ABSTRACT should have been accurate.
312-
return False
312+
return bool(getattr(object, '__abstractmethods__'))
313313
# It looks like ABCMeta.__new__ has not finished running yet; we're
314314
# probably in __init_subclass__. We'll look for abstractmethods manually.
315315
for name, value in object.__dict__.items():

Lib/test/test_abc.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ def foo(): return 4
7171

7272
class TestABC(unittest.TestCase):
7373

74-
# TODO: RUSTPYTHON
75-
@unittest.expectedFailure
7674
def test_ABC_helper(self):
7775
# create an ABC using the helper class and perform basic checks
7876
class C(abc.ABC):
@@ -93,8 +91,6 @@ def foo(self): pass
9391
def bar(self): pass
9492
self.assertFalse(hasattr(bar, "__isabstractmethod__"))
9593

96-
# TODO: RUSTPYTHON
97-
@unittest.expectedFailure
9894
def test_abstractproperty_basics(self):
9995
@property
10096
@abc.abstractmethod
@@ -113,8 +109,6 @@ class D(C):
113109
def foo(self): return super().foo
114110
self.assertEqual(D().foo, 3)
115111

116-
# TODO: RUSTPYTHON
117-
@unittest.expectedFailure
118112
def test_abstractclassmethod_basics(self):
119113
@classmethod
120114
@abc.abstractmethod
@@ -135,8 +129,6 @@ def foo(cls): return super().foo()
135129
self.assertEqual(D.foo(), 'D')
136130
self.assertEqual(D().foo(), 'D')
137131

138-
# TODO: RUSTPYTHON
139-
@unittest.expectedFailure
140132
def test_abstractstaticmethod_basics(self):
141133
@staticmethod
142134
@abc.abstractmethod
@@ -157,8 +149,6 @@ def foo(): return 4
157149
self.assertEqual(D.foo(), 4)
158150
self.assertEqual(D().foo(), 4)
159151

160-
# TODO: RUSTPYTHON
161-
@unittest.expectedFailure
162152
def test_abstractmethod_integration(self):
163153
for abstractthing in [abc.abstractmethod, abc.abstractproperty,
164154
abc.abstractclassmethod,
@@ -187,8 +177,6 @@ def bar(self): pass # abstract override of concrete
187177
self.assertRaises(TypeError, F) # because bar is abstract now
188178
self.assertTrue(isabstract(F))
189179

190-
# TODO: RUSTPYTHON
191-
@unittest.expectedFailure
192180
def test_descriptors_with_abstractmethod(self):
193181
class C(metaclass=abc_ABCMeta):
194182
@property

vm/src/builtins/classmethod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,20 @@ impl PyClassMethod {
7676
fn func(&self) -> PyObjectRef {
7777
self.callable.clone()
7878
}
79+
80+
#[pyproperty(magic)]
81+
fn isabstractmethod(&self, vm: &VirtualMachine) -> PyObjectRef {
82+
match vm.get_attribute_opt(self.callable.clone(), "__isabstractmethod__") {
83+
Ok(Some(is_abstract)) => is_abstract,
84+
_ => vm.ctx.new_bool(false).into(),
85+
}
86+
}
87+
88+
#[pyproperty(magic, setter)]
89+
fn set_isabstractmethod(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
90+
self.callable.set_attr("__isabstractmethod__", value, vm)?;
91+
Ok(())
92+
}
7993
}
8094

8195
pub(crate) fn init(context: &PyContext) {

vm/src/builtins/property.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,32 @@ impl PyProperty {
217217
}
218218
.into_ref_with_type(vm, TypeProtocol::clone_class(&zelf))
219219
}
220+
221+
#[pyproperty(magic)]
222+
fn isabstractmethod(&self, vm: &VirtualMachine) -> PyObjectRef {
223+
let getter_abstract = match self.getter.read().to_owned() {
224+
Some(getter) => getter
225+
.get_attr("__isabstractmethod__", vm)
226+
.unwrap_or(vm.ctx.new_bool(false).into()),
227+
_ => vm.ctx.new_bool(false).into(),
228+
};
229+
let setter_abstract = match self.setter.read().to_owned() {
230+
Some(setter) => setter
231+
.get_attr("__isabstractmethod__", vm)
232+
.unwrap_or(vm.ctx.new_bool(false).into()),
233+
_ => vm.ctx.new_bool(false).into(),
234+
};
235+
vm._or(&setter_abstract, &getter_abstract)
236+
.unwrap_or(vm.ctx.new_bool(false).into())
237+
}
238+
239+
#[pyproperty(magic, setter)]
240+
fn set_isabstractmethod(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
241+
if let Some(getter) = self.getter.read().to_owned() {
242+
getter.set_attr("__isabstractmethod__", value, vm)?;
243+
}
244+
Ok(())
245+
}
220246
}
221247

222248
pub(crate) fn init(context: &PyContext) {

vm/src/builtins/staticmethod.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,21 @@ impl PyStaticMethod {
6060
}
6161

6262
#[pyimpl(with(GetDescriptor, Constructor), flags(BASETYPE, HAS_DICT))]
63-
impl PyStaticMethod {}
63+
impl PyStaticMethod {
64+
#[pyproperty(magic)]
65+
fn isabstractmethod(&self, vm: &VirtualMachine) -> PyObjectRef {
66+
match vm.get_attribute_opt(self.callable.clone(), "__isabstractmethod__") {
67+
Ok(Some(is_abstract)) => is_abstract,
68+
_ => vm.ctx.new_bool(false).into(),
69+
}
70+
}
71+
72+
#[pyproperty(magic, setter)]
73+
fn set_isabstractmethod(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
74+
self.callable.set_attr("__isabstractmethod__", value, vm)?;
75+
Ok(())
76+
}
77+
}
6478

6579
pub fn init(context: &PyContext) {
6680
PyStaticMethod::extend_class(context, &context.types.staticmethod_type);

0 commit comments

Comments
 (0)