Skip to content
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

abstract class instantiable when subclassing built-in types #50246

Closed
thet mannequin opened this issue May 11, 2009 · 22 comments
Closed

abstract class instantiable when subclassing built-in types #50246

thet mannequin opened this issue May 11, 2009 · 22 comments
Labels
3.10 only security fixes 3.11 only security fixes 3.12 bugs and security fixes pending The issue will be closed if no feedback is provided stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@thet
Copy link
Mannequin

thet mannequin commented May 11, 2009

BPO 5996
Nosy @aleaxit, @rhettinger, @terryjreedy, @pitrou, @benjaminp, @jwilk, @merwok, @bitdancer, @thet, @durban, @eltoder, @ericsnowcurrently, @soltysh, @MojoVampire, @poleto, @zhangyangyu, @nathanielmanistaatgoogle, @DimitrisJim, @remilapeyre, @2xB
Files
  • abc.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = None
    created_at = <Date 2009-05-11.14:06:01.619>
    labels = ['3.8', 'type-bug', 'library', '3.9', '3.10']
    title = 'abstract class instantiable when subclassing built-in types'
    updated_at = <Date 2020-10-30.23:49:27.541>
    user = 'https://github.com/thet'

    bugs.python.org fields:

    activity = <Date 2020-10-30.23:49:27.541>
    actor = 'iritkatriel'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2009-05-11.14:06:01.619>
    creator = 'thet'
    dependencies = []
    files = ['21600']
    hgrepos = []
    issue_num = 5996
    keywords = ['patch']
    message_count = 16.0
    messages = ['87574', '87576', '87854', '113698', '133440', '138411', '140108', '140114', '266704', '279479', '279481', '279483', '279484', '279499', '299815', '335337']
    nosy_count = 24.0
    nosy_names = ['aleax', 'rhettinger', 'terry.reedy', 'pitrou', 'nadeem.vawda', 'benjamin.peterson', 'jwilk', 'eric.araujo', 'r.david.murray', 'cvrebert', 'thet', 'daniel.urban', 'eltoder', 'eric.snow', 'maciej.szulik', 'josh.r', 'luiz.poleto', 'xiang.zhang', 'Kevin Shweh', 'Nathaniel Manista', 'Jim Fasarakis-Hilliard', 'remi.lapeyre', 'Jon McMahon', '2xB']
    pr_nums = []
    priority = 'low'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue5996'
    versions = ['Python 3.8', 'Python 3.9', 'Python 3.10']

    @thet
    Copy link
    Mannequin Author

    thet mannequin commented May 11, 2009

    when declaring a abstract base class with an abstract property or method
    and subclassing from dict, the class is instantiable (instanceable?).

    >>> import abc
    >>> class A(object):
    ...     __metaclass__ = abc.ABCMeta
    ...     @abc.abstractproperty
    ...     def abstract(self): return True
    ... 
    >>> a = A()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: Can't instantiate abstract class A with abstract methods abstract
    >>> 
    >>> class A2(dict):
    ...     __metaclass__ = abc.ABCMeta
    ...     @abc.abstractproperty
    ...     def abstract(self): return True
    ... 
    >>> a2 = A2()
    >>> 

    although, when using the dict definition from __builtin__.pi directly,
    the abc behaves like expected. but this may be a bug in the
    c-implementation from dict.

    platform:
    Python 2.6.2 (r262:71600, Apr 25 2009, 21:56:41)
    [GCC 4.3.2] on linux2

    @thet thet mannequin added stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error labels May 11, 2009
    @pitrou
    Copy link
    Member

    pitrou commented May 11, 2009

    Also happens with other builtin types such as tuple. It's probably
    related to the way subclasses of those types are instantiated, bypassing
    the normal metaclass mechanism.

    @terryjreedy
    Copy link
    Member

    I presume you claim the dict example to be a bug in relation to "A class
    that has a metaclass derived from ABCMeta cannot be instantiated unless
    all of its abstract methods and properties are overridden."

    There is the same difference with @abstractproperty
    Windows, 3.0.1

    class C(metaclass=abc.ABCMeta):
    	@abc.abstractmethod
    	def f(self): return True
    
    class C2(dict,metaclass=abc.ABCMeta):
    	@abc.abstractmethod
    	def f(self): return True
    
    c2=C2()
    print(c2.f())
    
    c=C()
    # prints
    True
    ...
    TypeError: Can't instantiate abstract class C with abstract methods f

    @durban
    Copy link
    Mannequin

    durban mannequin commented Aug 12, 2010

    I think, that the reason is that, object.__new__ checks, if the class is instantiable (object_new in Objects/typeobject.c ). dict.__new__ (and tuple.__new__, and I guess the __new__ method of other built-in types) doesn't call object.__new__, but user defined types typically either doesn't have a __new__, or call object.__new__ from it (directly or with super).

    @eltoder
    Copy link
    Mannequin

    eltoder mannequin commented Apr 10, 2011

    This patch fixes the problem by moving the check from object_new to PyType_GenericAlloc. The check is very cheap, so this should not be an issue.

    @eltoder
    Copy link
    Mannequin

    eltoder mannequin commented Jun 16, 2011

    Anyone has any thoughts on this?

    @rhettinger
    Copy link
    Contributor

    IIRC, classes weren't supposed to be able inherit from two classes that had different metaclasses (since differing metaclasses don't necessarily play well with one another).

    @eltoder
    Copy link
    Mannequin

    eltoder mannequin commented Jul 11, 2011

    They are, when there's a most specific metaclass -- the one which is a subclass of all others (described here http://www.python.org/download/releases/2.2.3/descrintro/#metaclasses, implemented here http://hg.python.org/cpython/file/ab162f925761/Objects/typeobject.c#l1956). Since ABCMeta is a subclass of type this holds.

    Also, in the original example there's no multiple inheritance at all.

    @rhettinger rhettinger self-assigned this Jul 11, 2011
    @rhettinger rhettinger removed their assignment May 26, 2016
    @zhangyangyu
    Copy link
    Member

    I think subclassing builtin types and making it abstract is rare. And when there is a need, we can mimic this in application level (this may also apply to types having custom __new__):

    In [2]: class CustomDict(dict, metaclass=abc.ABCMeta):
    ...: def __new__(cls, *args, **kwargs):
    ...: if getattr(cls, '__abstractmethods__', None):
    ...: raise TypeError
    ...: return super().__new__(cls, *args, **kwargs)
    ...: @abc.abstractmethod
    ...: def f(self):
    ...: pass

    Adding the abstract class checking in tp_alloc or builtin types' tp_new maybe degrade performance.

    Is it necessary to add this support?

    @gvanrossum
    Copy link
    Member

    Honestly let's just forget about this.

    @nathanielmanistaatgoogle
    Copy link
    Mannequin

    Wait, really? My report came out of a real bug that I had in my system and shipped to my users; it wasn't academic or contrived at all.

    @gvanrossum
    Copy link
    Member

    Where did you report that? I don't see your name on this bug -- it has a patch that's been unapplied for 5 years, so I doubt it's very important.

    @gvanrossum
    Copy link
    Member

    Oh sorry. I received the emails in a strange order. I guess it can stay open.

    @gvanrossum gvanrossum reopened this Oct 26, 2016
    @bitdancer
    Copy link
    Member

    My apologies, I added Nathaniel to nosy here when I closed the duplicate, but forgot to add a link to the closed issue: bpo-28537.

    @bitdancer
    Copy link
    Member

    Closed bpo-31127 as a duplicate of this one.

    @MojoVampire
    Copy link
    Mannequin

    MojoVampire mannequin commented Feb 12, 2019

    Closed bpo-35958 as a duplicate of this issue (and updated the title, since clearly the problem is not specific to dict).

    Patch probably needs to be rebased/rewritten against latest trunk (given it dates from Mercurial days).

    @MojoVampire MojoVampire mannequin added 3.7 (EOL) end of life 3.8 only security fixes labels Feb 12, 2019
    @MojoVampire MojoVampire mannequin changed the title abstract class instantiable when subclassing dict abstract class instantiable when subclassing built-in types Feb 12, 2019
    @iritkatriel iritkatriel added 3.9 only security fixes 3.10 only security fixes and removed 3.7 (EOL) end of life labels Oct 30, 2020
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    @iritkatriel iritkatriel removed the 3.9 only security fixes label Oct 3, 2022
    @iritkatriel iritkatriel added 3.11 only security fixes 3.12 bugs and security fixes and removed 3.8 only security fixes labels Oct 3, 2022
    @Yc7521
    Copy link

    Yc7521 commented Mar 17, 2023

    The reason for this bug is that the creation of the subclass avoids the invocation of the 'object_new' method (e.g. for built-in types or when __new__ is overridden without calling super().__new__).
    It seems that considering adding an abstract flag check in tp_new_wrapper could solve this issue.

    some links:
    object_new
    tp_new_wrapper

    A simple code for enforcing ABC check to correct the behavior. (only for fixing inheriting built-in types)

    class MyABC(ABC):
        def __new__(cls, *args, **kwargs):
            if len(cls.__abstractmethods__) > 0:
                raise TypeError("Can't instantiate abstract class MyABC with abstract methods test")
            return super().__new__(cls, *args, **kwargs)
    
    
    ABC = MyABC

    @gvanrossum
    Copy link
    Member

    gvanrossum commented Mar 18, 2023

    I got as far as a repro, but this morning my Pooh brain doesn't follow the root cause. I'll come back to it.

    import abc
    class A(object, metaclass=abc.ABCMeta):
        @abc.abstractproperty
        def pooh(self): return True
    
    class A2(dict, metaclass=abc.ABCMeta):
        @abc.abstractproperty
        def pooh(self): return True
    
    a2 = A2()  # Passes
    a = A()  # Can't instantiate abstract class A without an implementation for abstract method 'pooh'
    

    @gvanrossum
    Copy link
    Member

    The reason for this bug is that the creation of the subclass avoids the invocation of the 'object_new' method (e.g. for built-in types or when __new__ is overridden without calling super().__new__).

    You're right that it is because object_new isn't called when the base class is a builtin.

    I'm skeptical of Python-level code whose __new__ doesn't call super().__new__, because how would it come up with an object to return at all?

    I'm also skeptical of putting anything in tp_new_wrapper, since that isn't called by e.g. dict_new nor by most other builtin types' tp_new callback.

    One of the duplicates reported the same problem when inheriting from Exception, which doesn't even override tp_new, so there's something more going on there that I haven't tracked down yet.

    My gut feeling still says that the exception you get for instantiating an abstract class is intended to be helpful, but not a feature you can depend on, but that's at least in part because I don't know how to solve this yet without slowing down e.g. dict creation, which is pretty time sensitive.

    @gvanrossum gvanrossum added the pending The issue will be closed if no feedback is provided label May 23, 2023
    @gvanrossum
    Copy link
    Member

    I'm tempted to close this as won't fix, unless there is further discussion. (A PR would also be helpful to focus the discussion; I'm not considering old patch files from bpo.)

    @Yc7521
    Copy link

    Yc7521 commented May 24, 2023

    Maybe someday someone will come up with a relatively good solution. At least this is indeed a bug.

    @gvanrossum
    Copy link
    Member

    Thanks, it is indeed a bug, but we won't fix it (yet).

    @gvanrossum gvanrossum closed this as not planned Won't fix, can't repro, duplicate, stale May 24, 2023
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.10 only security fixes 3.11 only security fixes 3.12 bugs and security fixes pending The issue will be closed if no feedback is provided stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    8 participants