bpo-29822: Make inspect.isabstract() work during init_subclass. (… · python/cpython@fcfe80e (original) (raw)

Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@
31 31 __author__ = ('Ka-Ping Yee ping@lfw.org',
32 32 'Yury Selivanov yselivanov@sprymix.com')
33 33
34 +import abc
34 35 import ast
35 36 import dis
36 37 import collections.abc
@@ -291,7 +292,27 @@ def isroutine(object):
291 292
292 293 def isabstract(object):
293 294 """Return true if the object is an abstract base class (ABC)."""
294 -return bool(isinstance(object, type) and object.__flags__ & TPFLAGS_IS_ABSTRACT)
295 +if not isinstance(object, type):
296 +return False
297 +if object.__flags__ & TPFLAGS_IS_ABSTRACT:
298 +return True
299 +if not issubclass(type(object), abc.ABCMeta):
300 +return False
301 +if hasattr(object, '__abstractmethods__'):
302 +# It looks like ABCMeta.__new__ has finished running;
303 +# TPFLAGS_IS_ABSTRACT should have been accurate.
304 +return False
305 +# It looks like ABCMeta.__new__ has not finished running yet; we're
306 +# probably in __init_subclass__. We'll look for abstractmethods manually.
307 +for name, value in object.__dict__.items():
308 +if getattr(value, "__isabstractmethod__", False):
309 +return True
310 +for base in object.__bases__:
311 +for name in getattr(base, "__abstractmethods__", ()):
312 +value = getattr(object, name, None)
313 +if getattr(value, "__isabstractmethod__", False):
314 +return True
315 +return False
295 316
296 317 def getmembers(object, predicate=None):
297 318 """Return all members of an object as (name, value) pairs sorted by name.