[issue28437] Documentation for handling of non-type metaclass hints is unclear

2016-10-16 Thread Nick Coghlan

Nick Coghlan added the comment:

The "used directly as the metaclass" is a reference to 
https://docs.python.org/3/reference/datamodel.html#creating-the-class-object 
further down, and specifically the "metaclass(name, bases, namespace, **kwds)" 
call. It's not saying Python has a way to bypass that instantiation process.

As a result, your code is consistently getting to that step just fine, but 
*that call* is throwing an exception.

Hence my comment earlier that there's a case to be made that we should be 
better indicating where we were in the type creation process when the metaclass 
resolution failed.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue28437] Documentation for handling of non-type metaclass hints is unclear

2016-10-16 Thread Nick Coghlan

Nick Coghlan added the comment:

Oops, couple of typos:

"... only triggers metaclass resolution at that point ..."

"... if either the metaclass being instantiated is a subclass of all the 
metaclasses of all of the bases ..."

But the only way to bypass the "most derived metaclass" rule is for the the 
metaclass hint to be a callable that creates something that *isn't* a subclass 
of type. If you look at the tracebacks you're getting, you should see that the 
failure *isn't* in the class statement or the outer dynamic type, it's in 
"metaclass_callable", where the *inner* dynamic type creation is failing.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue28437] Documentation for handling of non-type metaclass hints is unclear

2016-10-16 Thread Neil Girdhar

Neil Girdhar added the comment:

Okay, I understand what you're saying, but I it says in the documentation that 
"if an explicit metaclass is given and it is not an instance of type(), then it 
is used directly as the metaclass".  My recent updated "metaclass_callable" is 
not an instance of type.  Why should it raise an exception?

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue28437] Documentation for handling of non-type metaclass hints is unclear

2016-10-16 Thread Nick Coghlan

Nick Coghlan added the comment:

Because they're checking for different things:

- types.prepare_class is only checking "How do I call __prepare__?". It only 
triggers type resolution at that point if the metaclass hint is an instance of 
type, otherwise it skips that process entirely and queries the metaclass hint 
directly.

- type.__new__ is checking "Can I actually create a new instance of this 
metaclass with these bases?". It can only do that if either the metaclass being 
instantiated is a subclass of all the bases of the type being defined, or else 
such a metaclass exists amongst the bases.

To be clear, your example isn't failing due to the way MyDerived is defined - 
it's failing because OtherMetaclass is itself an instance of type, and you're 
declaring it (directly or indirectly) as the metaclass of MyDerived, while 
inheriting from MyClass, which is an instance of MyMetaclass.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue28437] Documentation for handling of non-type metaclass hints is unclear

2016-10-16 Thread Neil Girdhar

Neil Girdhar added the comment:

Thanks for taking the time to explain, but it's still not working for me:

from types import new_class


class MyMetaclass(type):
pass

class OtherMetaclass(type):
pass

def metaclass_callable(name, bases, namespace):
return new_class(name, bases, dict(metaclass=OtherMetaclass))

class MyClass(metaclass=MyMetaclass):
pass

try:
class MyDerived(MyClass, metaclass=metaclass_callable):
pass
except:
print("Gotcha!")


try:
MyDerived = new_class("MyDerived", (MyClass,), 
dict(metaclass=metaclass_callable))
except:
print("Gotcha again!")

So my questions are:

1. Why shouldn't Lib/types:new_class behave in exactly the same way as 
declaring a class using "class…" notation?

2. What's the point of checking if the metaclass is an instance of type?  It 
seems to me that in Python 2, non-type metaclasses did not have to be the "most 
derived class" (that's what the documentation seems to suggest with the second 
rule).  However, we no longer accept that in CPython 3 — neither in the 
Lib/types, nor in a regular declaration.  In fact, the exception is:

"metaclass conflict: "
"the metaclass of a derived class "
"must be a (non-strict) subclass "
"of the metaclasses of all its bases");

So why not just check that unconditionally in Lib/types.py?

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue28437] Documentation for handling of non-type metaclass hints is unclear

2016-10-16 Thread Nick Coghlan

Nick Coghlan added the comment:

Why should it trip the PEP 3115 namespace preparation exception? We only check 
for __prepare__ (and hence need to do an early most-derived metaclass 
resolution) on instances of "type", not on arbitrary callables used as a 
metaclass hint

The checks are different in the two places because the rules are different in 
the two places. (One case that *can* be made is that we should be throwing 
different exceptions or chaining them to show which metaclass resolution 
failed, rather than re-using the same message in both places).

This means that when you define a non-type metaclass hint, you're bypassing 
*all* of the PEP 3115 machinery, including the early metaclass resolution.

However, if your metaclass hint still creates a type instance, you're not 
bypassing tp_new.

If you're asking "Where is the bug in the presented example code?", it's here, 
in the definition of your metaclass hint:

def metaclass_callable(name, bases, namespace):
return OtherMetaclass(name, bases, namespace)

Instantiating instances of "type" directly in Python 3 bypasses the PEP 3115 
machinery - that's the entire reason that types.new_class was added. So if you 
want that metaclass hint to be PEP 3115 compliant, you need to explicitly write 
it that way:

def metaclass_callable(name, bases, namespace):
return types.new_class(name, bases, dict(metaclass=OtherMetaclass)

--
resolution: not a bug -> 
stage: resolved -> 
status: closed -> open
title: Class definition is not consistent with types.new_class -> Documentation 
for handling of non-type metaclass hints is unclear

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com