Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
added explicit check for ABCMeta
  • Loading branch information
ben avrahami committed Oct 1, 2020
commit 2f95b840e24e1f2e358035633f5aa156d15e2ee6
2 changes: 2 additions & 0 deletions Doc/library/abc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ The :mod:`abc` module also provides the following functions:

If *cls* has any subclasses, raises a :exc:`RuntimeError`.

If *cls* is not an instance of ABCMeta, raises a :exc:`TypeError`.

.. versionadded:: 3.10

.. rubric:: Footnotes
Expand Down
20 changes: 15 additions & 5 deletions Lib/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def update_abstractmethods(cls):

If a class has had one of its abstract methods implemented after the
class was created, the method will not be considered implemented until
this function is called. Alternativly, if a new abstract method has been
this function is called. Alternatively, if a new abstract method has been
added to the class, it will only be considered an abstract method of the
class after this function is called.

Expand All @@ -137,17 +137,27 @@ class after this function is called.
Returns cls, to allow usage as a class decorator.

If cls is has any subclasses, raises a RuntimeError.

If cls is not an instance of ABCMeta, raises a TypeError.
"""
if not hasattr(cls, '__abstractmethods__'):
# We check for __abstractmethods__ here because cls might by a C
# implementation or a python implementation (especially during
# testing), and we want to handle both cases.
raise TypeError('cls must be an abstract class or subclass of an abstract'
' class')
if cls.__subclasses__():
raise RuntimeError("cannot update abstract methods of class after subclassing")
raise RuntimeError("cannot update abstract methods of class after"
" subclassing")

abstracts = set()
# check the existing abstract methods, keep only the ones that are
# still abstract
# Check the existing abstract methods, keep only the ones that are
# still abstract.
for name in cls.__abstractmethods__:
Copy link
Copy Markdown
Member

@iritkatriel iritkatriel Oct 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you didn't check in this function that it's an ABC, it's not guaranteed that cls.__abstractmethods__ works:

>>> class FooABC: pass
... 
>>> FooABC.__abstractmethods__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: __abstractmethods__

Maybe add a test for when this function is called for a non-ABC.

value = getattr(cls, name, None)
if getattr(value, "__isabstractmethod__", False):
abstracts.add(name)
# also add any other newly added abstract methods
# Also add any other newly added abstract methods.
for name, value in cls.__dict__.items():
if getattr(value, "__isabstractmethod__", False):
abstracts.add(name)
Expand Down
11 changes: 11 additions & 0 deletions Lib/test/test_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,17 @@ class B(A):
B()
self.assertEqual(B.__abstractmethods__, set())

def test_update_non_abc(self):
class A:
pass

@abc.abstractmethod
def updated_foo(self):
pass

A.foo = updated_foo
self.assertRaises(TypeError, abc.update_abstractmethods, A)


class TestABCWithInitSubclass(unittest.TestCase):
def test_works_with_init_subclass(self):
Expand Down