-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
bpo-29822: make inspect.isabstract() work during __init_subclass__ #678
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
At the time when an abstract base class' __init_subclass__ runs, ABCMeta.__new__ has not yet finished running, so in the presence of __init_subclass__, inspect.isabstract() can no longer depend only on TPFLAGS_IS_ABSTRACT.
|
@Soares, thanks for your PR! By analyzing the history of the files in this pull request, we identified @zestyping, @1st1 and @larryhastings to be potential reviewers. |
Lib/inspect.py
Outdated
| def isabstract(object): | ||
| """Return true if the object is an abstract base class (ABC).""" | ||
| return bool(isinstance(object, type) and object.__flags__ & TPFLAGS_IS_ABSTRACT) | ||
| if isinstance(object, type): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if isinstance(object, type):
return FalseThis will decrease an indentation level.
Lib/inspect.py
Outdated
| def isabstract(object): | ||
| """Return true if the object is an abstract base class (ABC).""" | ||
| return bool(isinstance(object, type) and object.__flags__ & TPFLAGS_IS_ABSTRACT) | ||
| if isinstance(object, type): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if not isinstance(object, type):
return FalseThis will decrease an indentation level.
Lib/inspect.py
Outdated
| if isinstance(object, type): | ||
| if object.__flags__ & TPFLAGS_IS_ABSTRACT: | ||
| return True | ||
| elif (issubclass(type(object), abc.ABCMeta) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just if rather than elif. And you can return False if the condition is false for decreasing an indentation level.
Lib/inspect.py
Outdated
| # We're likely in the __init_subclass__ of an ABC. ABCMeta.__new__ | ||
| # hasn't finished running yet. We have to search for abstractmethods | ||
| # by hand. Code copied from ABCMeta.__new__. | ||
| abstracts = {name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me that collecting names of abstract method is not needed.
False can be returned just after finding any abstract method.
- return earlier to decrease indentation levels - return immediately upon finding an abstractmethod, instead of collecting the whole set
Lib/inspect.py
Outdated
| if getattr(value, "__isabstractmethod__", False): | ||
| return True | ||
| for base in object.__bases__: | ||
| for name in getattr(base, "__abstractmethods__", frozenset()): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use an empty tuple () rather than frozenset(). This is shorter and faster.
| def foo(self): | ||
| pass | ||
|
|
||
| class ClassExample(AbstractClassExample): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test doesn't cover the case when the abstract method is not in the __dict__ of final class, but is inherited from base class.
Misc/NEWS
Outdated
| - Issue #29581: ABCMeta.__new__ now accepts **kwargs, allowing abstract base | ||
| classes to use keyword parameters in __init_subclass__. Patch by Nate Soares. | ||
|
|
||
| - Issue #29822: inspect.isabstract() now works during __init_subclass__. Patch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this at the start of the Library section. Use two spaces between sentences.
Lib/test/test_inspect.py
Outdated
|
|
||
| def test_isabstract_during_init_subclass(self): | ||
| from abc import ABCMeta, abstractmethod | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use blank lines in test method only to indicate logical sections.
- Added a new test. - Cleaned up some code style here and there
Lib/test/test_inspect.py
Outdated
| isabstract_checks.clear() | ||
| class AbstractChild(AbstractClassExample): | ||
| pass | ||
| class AbstractGrandchild(AbstractClassExample): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the difference between AbstractChild and AbstractGrandchild?
|
Oh, my bad, I meant to have AbstractGrandchild inherit AbstractChild. AbstractGrandchild is not strictly necessary, I suppose; I have it in there to make sure that we're walking the whole |
Lib/inspect.py
Outdated
| # TPFLAGS_IS_ABSTRACT should have been accurate. | ||
| return False | ||
| # It looks like ABCMeta.__new__ has not finished running yet; we're | ||
| # probably in __init_subcalss_. We'll look for abstractmethods manually. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pedantic but typo in comment. Should be:
# probably in __init_subclass__. We'll look for abstractmethods manually.|
Thanks for the review. Fixed the typo, and updated the NEWS file to resolve conflicts. |
|
Bump. @agoose77, I have fixed the aforementioned typo, are there any outstanding issues as far as you're concerned? |
Misc/NEWS
Outdated
|
|
||
| What's New in Python 3.6.0 beta 1? | ||
| ================================== | ||
| What's New in Python 3.6.0 beta ============================== |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eaten "1?" and a newline.
|
My bad; thanks for catching that; should be fixed now. |
Lib/inspect.py
Outdated
| # TPFLAGS_IS_ABSTRACT should have been accurate. | ||
| return False | ||
| # It looks like ABCMeta.__new__ has not finished running yet; we're | ||
| # probably in __init_subcalss__. We'll look for abstractmethods manually. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: "__init_subcalss__".
|
Fixed thanks. |
ncoghlan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice find, and the proposed fix and test case look good to me.
|
Thanks @Soares LGTM👍 |
|
Thank you for your contribution @Soares. Do you mind to backport the fix to 3.6? |
|
I'd be happy to backport. Note that #527 will also need backporting (in Python 3.6, ABCMeta doesn't work with |
…s__. (pythonGH-678) At the time when an abstract base class' __init_subclass__ runs, ABCMeta.__new__ has not yet finished running, so in the presence of __init_subclass__, inspect.isabstract() can no longer depend only on TPFLAGS_IS_ABSTRACT.. (cherry picked from commit fcfe80e)
At the time when an abstract base class'
__init_subclass__runs,ABCMeta.__new__has not yet finished running, so in the presence of__init_subclass__,inspect.isabstract()can no longer depend only onTPFLAGS_IS_ABSTRACT.