Title: type(name, bases, dict) does not call metaclass' __prepare__ attribute
Type: behavior Stage: patch review
Components: Documentation Versions: Python 3.6, Python 3.5
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: berker.peksag, docs@python, ncoghlan, nikratio, r.david.murray
Priority: normal Keywords: patch

Created on 2013-07-01 01:12 by nikratio, last changed 2016-08-03 15:12 by berker.peksag.

File name Uploaded Description Edit
type_doc_patch.diff nikratio, 2013-07-01 01:12 review
Messages (5)
msg192099 - (view) Author: Nikolaus Rath (nikratio) * Date: 2013-07-01 01:12
When using the three parameter form of type to create a new class, and any of the base classes has a metaclass with a __prepare__ function, the __prepare__ function is not executed:

      >>> class CustomMetaclass(type):
      ...     @classmethod
      ...     def __prepare__(cls, name, bases):
      ...          return { 'prepared_for': name }
      >>> class ParentClass(metaclass=CustomMetaclass):
      ...     pass
      >>> class ClassOne(ParentClass):
      ...     pass
      >>> ClassTwo = type('ClassTwo', (ParentClass,), {})
      >>> ClassOne.prepared_for
      >>> ClassTwo.prepared_for
      >>> 'prepared_for' in ClassOne.__dict__
      >>> 'prepared_for' in ClassTwo.__dict__

I am not sure if that is intended behavior or not. I am attaching a doc patch for the case that this is intended.
msg192100 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2013-07-01 01:50
"Intentional" is not perhaps the exactly right term, but it is working as expected.  There was a thread about this on python-dev ("PEP 3115 compliant dynamic type creation" and "adding types.build_class for 3.3"), that ultimately resulted in the addition of the 'new_class' function to the types module.  The documentation should probably just say that types doesn't handle metaclass __prepare__, and provide a link to the new_class function.
msg192101 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2013-07-01 02:00
I think we should actually go further, and explicitly defer to for dynamic type creation.

Type shouldn't be called with arbitrary bases any more, precisely *because* doing so breaks __prepare__ handling. It's only safe to call a metaclass directly with arbitrary bases if you call types.prepare_class first:

    mcl, namespace, kwds = types.prepare_class(name, bases)
    cls = mcl(name, bases, namespace, **kwds)

You can only skip types.prepare_class if you *know* you already have the right metaclass (such as when there aren't any base classes defined).
msg192102 - (view) Author: Nikolaus Rath (nikratio) * Date: 2013-07-01 02:19
In that cases, maybe type(name, cls, clsdict) should actually raise an error if there's a metaclass with __prepare__ involved?

Presumably that would break only code that was already broken, but it would convert previously hidden behavioral bugs into an explicit expressions raised at the right point.
msg192113 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2013-07-01 07:35
Unfortunately, it's not that simple, as calling type(name, bases, namespace) is *exactly* what a subclass will do as part of creating the type object.

From inside the type implementation, we can't tell the difference between "properly called from the child type" and "improperly called without preparing the namespace first".
Date User Action Args
2016-08-03 15:12:05berker.peksagsetnosy: + berker.peksag
stage: patch review

versions: + Python 3.5, Python 3.6, - Python 3.3, Python 3.4
2015-07-21 08:02:41ethan.furmansetnosy: - ethan.furman
2014-05-19 01:31:22ethan.furmansetnosy: + ethan.furman
2013-07-01 07:35:31ncoghlansetmessages: + msg192113
2013-07-01 02:19:17nikratiosetmessages: + msg192102
2013-07-01 02:00:51ncoghlansetmessages: + msg192101
2013-07-01 01:50:32r.david.murraysetversions: + Python 3.4
nosy: + r.david.murray, ncoghlan, docs@python

messages: + msg192100

assignee: docs@python
components: + Documentation, - Interpreter Core
2013-07-01 01:12:18nikratiocreate