Skip to content

Conversation

@Soares
Copy link
Contributor

@Soares Soares commented Mar 15, 2017

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.

Nate Soares added 2 commits March 15, 2017 11:48
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.
@mention-bot
Copy link

@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):
Copy link
Member

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 False

This 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):
Copy link
Member

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 False

This 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)
Copy link
Member

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
Copy link
Member

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.

@serhiy-storchaka serhiy-storchaka requested a review from 1st1 March 18, 2017 06:09
- 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()):
Copy link
Member

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):
Copy link
Member

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
Copy link
Member

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.


def test_isabstract_during_init_subclass(self):
from abc import ABCMeta, abstractmethod

Copy link
Member

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
isabstract_checks.clear()
class AbstractChild(AbstractClassExample):
pass
class AbstractGrandchild(AbstractClassExample):
Copy link
Member

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?

@Soares
Copy link
Contributor Author

Soares commented Mar 18, 2017

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 __mro__ instead of just the __bases__.

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.
Copy link
Contributor

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.

@Soares
Copy link
Contributor Author

Soares commented Apr 4, 2017

Thanks for the review. Fixed the typo, and updated the NEWS file to resolve conflicts.

@Soares
Copy link
Contributor Author

Soares commented Apr 23, 2017

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 ==============================
Copy link
Member

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.

Fixed "1?\n====" that I accidentally killed during the resolution of a merge conflict.
@Soares
Copy link
Contributor Author

Soares commented Apr 23, 2017

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.
Copy link
Member

@serhiy-storchaka serhiy-storchaka Apr 23, 2017

Choose a reason for hiding this comment

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

Typo: "__init_subcalss__".

@Soares
Copy link
Contributor Author

Soares commented Apr 23, 2017

Fixed thanks.

Copy link
Contributor

@ncoghlan ncoghlan left a 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.

@agoose77
Copy link
Contributor

Thanks @Soares LGTM👍

@serhiy-storchaka serhiy-storchaka merged commit fcfe80e into python:master Apr 24, 2017
@serhiy-storchaka serhiy-storchaka added the type-bug An unexpected behavior, bug, or error label Apr 24, 2017
@serhiy-storchaka
Copy link
Member

Thank you for your contribution @Soares. Do you mind to backport the fix to 3.6?

@Soares
Copy link
Contributor Author

Soares commented Apr 24, 2017

I'd be happy to backport. Note that #527 will also need backporting (in Python 3.6, ABCMeta doesn't work with __init_subclass__ at all). IIRC, the developer guide says that that PR will need a "needs backport to 3.6" label that only core devs can apply; if that's correct, can you apply that label? Then I'll backport both PRs. Thanks!

Soares pushed a commit to Soares/cpython that referenced this pull request Jun 7, 2017
…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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type-bug An unexpected behavior, bug, or error

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants