Issue 17421: Drop restriction that meta.prepare() must return a dict (subclass) (original) (raw)
Created on 2013-03-14 20:20 by eric.snow, last changed 2022-04-11 14:57 by admin.
Messages (6)
Author: Eric Snow (eric.snow) *
Date: 2013-03-14 20:20
Currently type_new() in Objects/typeobject.c enforces a restriction that the namespace be a dict or dict subclass. It does this via the PyArg_ParseTupleAndKeywords() call there.
This means that valid mappings may not be used even though they should work just fine. A demonstration of the problem is below.
I've attached a patch that relaxes this restriction. Should we also add a note in the docs that type() will take anything for namespace that dict() will take?
Demonstration
class Meta(type): @classmethod def prepare(cls, name, bases, **kwargs): return ClassMapping()
from collections import MutableMapping class ClassMapping(MutableMapping): def init(self, *args, **kwargs): self._dict = dict(*args, **kwargs) def len(self): return len(self._dict) def iter(self): return iter(self._dict) def getitem(self, key): return self._dict[key] def setitem(self, key, value): self._dict[key] = value def delitem(self, key): del self._dict[key]
class X(metaclass=Meta): ... pass ... Traceback (most recent call last): File "", line 1, in TypeError: type() argument 3 must be dict, not ClassMapping
Author: Martin v. Löwis (loewis) *
Date: 2013-03-14 22:38
Are you sure that non-dicts work fine? ISTM that there is quite some code that relies on tp_dict being a dict-or-subdict instance, e.g. in typeobject.c:type_module,type_get_doc etc.
Author: Eric Snow (eric.snow) *
Date: 2013-03-14 23:16
Sorry I wasn't clear. Later in type.new() it copies that passed namespace into a new dict (see issue #17422). So as long as the namespace argument is a valid argument to dict(), it's going to work fine. We don't need the extra explicit check performed by PyArg_ParseTupleAndKeywords().
Author: Alyssa Coghlan (ncoghlan) *
Date: 2013-03-16 12:56
Eric and I discussed this, and I've come to the conclusion that the check doesn't serve much purpose at this point.
I initially thought it conveyed useful information about the runtime behavioural restriction, but it doesn't even do that correctly, as dict subclasses (like collections.OrderedDict) will pass the check but will also be copied into a vanilla dict instance.
However, we definitely shouldn't drop it until the copying behaviour is properly documented, so I've added #17422 as an explicit dependency.
Author: Serhiy Storchaka (serhiy.storchaka) *
Date: 2017-09-26 07:50
Are you sure that non-dicts work fine?
They don't. See .
Author: Caleb Donovick (donovick) *
Date: 2019-03-12 22:17
In my mind this seems like a bug. PEP 3115 states: ''' prepare returns a dictionary-like object which is used to store the class member definitions during evaluation of the class body. In other words, the class body is evaluated as a function block (just like it is now), except that the local variables dictionary is replaced by the dictionary returned from prepare. This dictionary object can be a regular dictionary or a custom mapping type. '''
History
Date
User
Action
Args
2022-04-11 14:57:42
admin
set
github: 61623
2019-03-12 22:17:10
donovick
set
messages: +
2019-03-12 22:14:15
donovick
set
nosy: + donovick
2017-09-26 07:50:01
serhiy.storchaka
set
nosy: + serhiy.storchaka
dependencies: + SystemError in class creation in case of a metaclass with a bad __prepare__() method
messages: +
2016-09-09 20:09:19
eric.snow
set
assignee: eric.snow ->
2015-07-21 08:00:58
ethan.furman
set
nosy: - ethan.furman
2014-05-21 01:21:03
vstinner
set
nosy: + vstinner
2014-05-19 01:33:27
ethan.furman
set
nosy: + ethan.furman
2013-06-25 05:21:42
eric.snow
set
assignee: eric.snow
2013-03-16 12:56:01
ncoghlan
set
dependencies: + language reference should specify restrictions on class namespace
messages: +
2013-03-14 23:16:19
eric.snow
set
messages: +
2013-03-14 22:38:28
loewis
set
nosy: + loewis
messages: +
2013-03-14 21:57:58
daniel.urban
set
nosy: + daniel.urban
2013-03-14 20:20:27
eric.snow
create