Skip to content

Commit 7cc5e0b

Browse files
authored
Merge pull request #3434 from chrismoradi/unimplemented-abstract-methods-raise-error
Check that all abstract methods are implemented prior to instantiation
2 parents 908b239 + a685b23 commit 7cc5e0b

File tree

6 files changed

+12
-44
lines changed

6 files changed

+12
-44
lines changed

Lib/test/test_abc.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
def test_factory(abc_ABCMeta, abc_get_cache_token):
1616
class TestLegacyAPI(unittest.TestCase):
1717

18-
# TODO: RUSTPYTHON
19-
@unittest.expectedFailure
2018
def test_abstractproperty_basics(self):
2119
@abc.abstractproperty
2220
def foo(self): pass
@@ -34,8 +32,6 @@ def foo(self): return super().foo
3432
self.assertEqual(D().foo, 3)
3533
self.assertFalse(getattr(D.foo, "__isabstractmethod__", False))
3634

37-
# TODO: RUSTPYTHON
38-
@unittest.expectedFailure
3935
def test_abstractclassmethod_basics(self):
4036
@abc.abstractclassmethod
4137
def foo(cls): pass
@@ -54,8 +50,6 @@ def foo(cls): return super().foo()
5450
self.assertEqual(D.foo(), 'D')
5551
self.assertEqual(D().foo(), 'D')
5652

57-
# TODO: RUSTPYTHON
58-
@unittest.expectedFailure
5953
def test_abstractstaticmethod_basics(self):
6054
@abc.abstractstaticmethod
6155
def foo(): pass
@@ -225,9 +219,6 @@ def bar(self):
225219
bar.__isabstractmethod__ = NotBool()
226220
foo = property(bar)
227221

228-
229-
# TODO: RUSTPYTHON
230-
@unittest.expectedFailure
231222
def test_customdescriptors_with_abstractmethod(self):
232223
class Descriptor:
233224
def __init__(self, fget, fset=None):

Lib/test/test_collections.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -905,8 +905,6 @@ def __hash__(self):
905905
self.validate_abstract_methods(Hashable, '__hash__')
906906
self.validate_isinstance(Hashable, '__hash__')
907907

908-
# TODO: RUSTPYTHON
909-
@unittest.expectedFailure
910908
def test_AsyncIterable(self):
911909
class AI:
912910
def __aiter__(self):
@@ -943,8 +941,6 @@ async def __anext__(self):
943941
self.assertNotIsInstance(AnextOnly(), AsyncIterator)
944942
self.validate_abstract_methods(AsyncIterator, '__anext__', '__aiter__')
945943

946-
# TODO: RUSTPYTHON
947-
@unittest.expectedFailure
948944
def test_Iterable(self):
949945
# Check some non-iterables
950946
non_samples = [None, 42, 3.14, 1j]
@@ -979,8 +975,6 @@ class ItBlocked(It):
979975
self.assertFalse(issubclass(ItBlocked, Iterable))
980976
self.assertFalse(isinstance(ItBlocked(), Iterable))
981977

982-
# TODO: RUSTPYTHON
983-
@unittest.expectedFailure
984978
def test_Reversible(self):
985979
# Check some non-reversibles
986980
non_samples = [None, 42, 3.14, 1j, set(), frozenset()]
@@ -1038,8 +1032,6 @@ class RevRevBlocked(Rev):
10381032
self.assertFalse(issubclass(RevRevBlocked, Reversible))
10391033
self.assertFalse(isinstance(RevRevBlocked(), Reversible))
10401034

1041-
# TODO: RUSTPYTHON
1042-
@unittest.expectedFailure
10431035
def test_Collection(self):
10441036
# Check some non-collections
10451037
non_collections = [None, 42, 3.14, 1j, lambda x: 2*x]
@@ -1127,7 +1119,6 @@ class NonCol(ColImpl):
11271119
self.assertFalse(issubclass(NonCol, Collection))
11281120
self.assertFalse(isinstance(NonCol(), Collection))
11291121

1130-
11311122
# TODO: RUSTPYTHON
11321123
@unittest.expectedFailure
11331124
def test_Iterator(self):
@@ -1155,8 +1146,6 @@ def __next__(self):
11551146
return
11561147
self.assertNotIsInstance(NextOnly(), Iterator)
11571148

1158-
# TODO: RUSTPYTHON
1159-
@unittest.expectedFailure
11601149
def test_Generator(self):
11611150
class NonGen1:
11621151
def __iter__(self): return self
@@ -1228,8 +1217,6 @@ def throw(self, *args): pass
12281217

12291218
self.assertRaises(RuntimeError, IgnoreGeneratorExit().close)
12301219

1231-
# TODO: RUSTPYTHON
1232-
@unittest.expectedFailure
12331220
def test_AsyncGenerator(self):
12341221
class NonAGen1:
12351222
def __aiter__(self): return self
@@ -1311,8 +1298,6 @@ async def athrow(self, *args): pass
13111298
with self.assertRaises(RuntimeError):
13121299
run_async(IgnoreGeneratorExit().aclose())
13131300

1314-
# TODO: RUSTPYTHON
1315-
@unittest.expectedFailure
13161301
def test_Sized(self):
13171302
non_samples = [None, 42, 3.14, 1j,
13181303
_test_gen(),
@@ -1331,8 +1316,6 @@ def test_Sized(self):
13311316
self.validate_abstract_methods(Sized, '__len__')
13321317
self.validate_isinstance(Sized, '__len__')
13331318

1334-
# TODO: RUSTPYTHON
1335-
@unittest.expectedFailure
13361319
def test_Container(self):
13371320
non_samples = [None, 42, 3.14, 1j,
13381321
_test_gen(),
@@ -1351,8 +1334,6 @@ def test_Container(self):
13511334
self.validate_abstract_methods(Container, '__contains__')
13521335
self.validate_isinstance(Container, '__contains__')
13531336

1354-
# TODO: RUSTPYTHON
1355-
@unittest.expectedFailure
13561337
def test_Callable(self):
13571338
non_samples = [None, 42, 3.14, 1j,
13581339
"", b"", (), [], {}, set(),
@@ -1414,8 +1395,6 @@ class TestCollectionABCs(ABCTestCase):
14141395
# We should also test the proper behavior of the collection ABCs
14151396
# as real base classes or mix-in classes.
14161397

1417-
# TODO: RUSTPYTHON
1418-
@unittest.expectedFailure
14191398
def test_Set(self):
14201399
for sample in [set, frozenset]:
14211400
self.assertIsInstance(sample(), Set)
@@ -1498,8 +1477,6 @@ def __len__(self):
14981477
s3 = s1 & s2
14991478
self.assertEqual(s3, MySet((3,)))
15001479

1501-
# TODO: RUSTPYTHON
1502-
@unittest.expectedFailure
15031480
def test_MutableSet(self):
15041481
self.assertIsInstance(set(), MutableSet)
15051482
self.assertTrue(issubclass(set, MutableSet))

Lib/test/test_contextlib.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ def __exit__(self, *args):
2020
manager = DefaultEnter()
2121
self.assertIs(manager.__enter__(), manager)
2222

23-
# TODO: RUSTPYTHON
24-
@unittest.expectedFailure
2523
def test_exit_is_abstract(self):
2624
class MissingExit(AbstractContextManager):
2725
pass

Lib/test/test_dynamicclassattribute.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,6 @@ def foo(self):
167167
foo.__isabstractmethod__ = NotBool()
168168
foo = DynamicClassAttribute(foo)
169169

170-
# TODO: RUSTPYTHON
171-
@unittest.expectedFailure
172170
def test_abstract_virtual(self):
173171
self.assertRaises(TypeError, ClassWithAbstractVirtualProperty)
174172
self.assertRaises(TypeError, ClassWithPropertyAbstractVirtual)

Lib/test/test_typing.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,6 @@ def test_eq_hash(self):
438438
self.assertNotEqual(Callable[[int], int], Callable[[], int])
439439
self.assertNotEqual(Callable[[int], int], Callable)
440440

441-
# TODO: RUSTPYTHON
442-
@unittest.expectedFailure
443441
def test_cannot_instantiate(self):
444442
with self.assertRaises(TypeError):
445443
Callable()
@@ -741,8 +739,6 @@ class CG(PG[T]): pass
741739

742740
self.assertIsInstance(CG[int](), CG)
743741

744-
# TODO: RUSTPYTHON
745-
@unittest.expectedFailure
746742
def test_cannot_instantiate_abstract(self):
747743
@runtime_checkable
748744
class P(Protocol):
@@ -3291,8 +3287,6 @@ def foo():
32913287
g = foo()
32923288
self.assertIsSubclass(type(g), typing.Generator)
32933289

3294-
# TODO: RUSTPYTHON
3295-
@unittest.expectedFailure
32963290
def test_no_generator_instantiation(self):
32973291
with self.assertRaises(TypeError):
32983292
typing.Generator()
@@ -3308,8 +3302,6 @@ def test_async_generator(self):
33083302
g = ns['f']()
33093303
self.assertIsSubclass(type(g), typing.AsyncGenerator)
33103304

3311-
# TODO: RUSTPYTHON
3312-
@unittest.expectedFailure
33133305
def test_no_async_generator_instantiation(self):
33143306
with self.assertRaises(TypeError):
33153307
typing.AsyncGenerator()

vm/src/builtins/object.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ impl PyBaseObject {
3434
} else {
3535
Some(vm.ctx.new_dict())
3636
};
37+
38+
// Ensure that all abstract methods are implemented before instantiating instance.
39+
if let Some(abs_methods) = cls.get_attr("__abstractmethods__") {
40+
if let Some(unimplemented_abstract_method_count) = vm.obj_len_opt(&abs_methods) {
41+
if unimplemented_abstract_method_count? > 0 {
42+
return Err(
43+
vm.new_type_error("You must implement the abstract methods".to_owned())
44+
);
45+
}
46+
}
47+
}
48+
3749
Ok(crate::PyRef::new_ref(PyBaseObject, cls, dict).into())
3850
}
3951

0 commit comments

Comments
 (0)