This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Drop restriction that meta.__prepare__() must return a dict (subclass)
Type: enhancement Stage: patch review
Components: Versions: Python 3.4
process
Status: open Resolution:
Dependencies: 17422 31588 Superseder:
Assigned To: Nosy List: daniel.urban, donovick, eric.snow, loewis, ncoghlan, serhiy.storchaka, vstinner
Priority: normal Keywords: patch

Created on 2013-03-14 20:20 by eric.snow, last changed 2022-04-11 14:57 by admin.

Files
File name Uploaded Description Edit
type-namespace-restriction.diff eric.snow, 2013-03-14 20:20
Messages (6)
msg184187 - (view) Author: Eric Snow (eric.snow) * (Python committer) 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 "<stdin>", line 1, in <module>
TypeError: type() argument 3 must be dict, not ClassMapping
msg184203 - (view) Author: Martin v. Löwis (loewis) * (Python committer) 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.
msg184206 - (view) Author: Eric Snow (eric.snow) * (Python committer) 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().
msg184309 - (view) Author: Alyssa Coghlan (ncoghlan) * (Python committer) 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.
msg303022 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-09-26 07:50
> Are you sure that non-dicts work fine?

They don't. See issue31588.
msg337814 - (view) 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:42adminsetgithub: 61623
2019-03-12 22:17:10donovicksetmessages: + msg337814
2019-03-12 22:14:15donovicksetnosy: + donovick
2017-09-26 07:50:01serhiy.storchakasetnosy: + serhiy.storchaka
dependencies: + SystemError in class creation in case of a metaclass with a bad __prepare__() method
messages: + msg303022
2016-09-09 20:09:19eric.snowsetassignee: eric.snow ->
2015-07-21 08:00:58ethan.furmansetnosy: - ethan.furman
2014-05-21 01:21:03vstinnersetnosy: + vstinner
2014-05-19 01:33:27ethan.furmansetnosy: + ethan.furman
2013-06-25 05:21:42eric.snowsetassignee: eric.snow
2013-03-16 12:56:01ncoghlansetdependencies: + language reference should specify restrictions on class namespace
messages: + msg184309
2013-03-14 23:16:19eric.snowsetmessages: + msg184206
2013-03-14 22:38:28loewissetnosy: + loewis
messages: + msg184203
2013-03-14 21:57:58daniel.urbansetnosy: + daniel.urban
2013-03-14 20:20:27eric.snowcreate