[Python-Dev] PEP 560

2017-11-14 Thread Ivan Levkivskyi
After some discussion on python-ideas, see
https://mail.python.org/pipermail/python-ideas/2017-September/047220.html,
this PEP received positive comments. The updated version that takes into
account the comments that appeared in the discussion so far is available at
https://www.python.org/dev/peps/pep-0560/

Here I post the full text for convenience:

++

PEP: 560
Title: Core support for typing module and generic types
Author: Ivan Levkivskyi 
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 03-Sep-2017
Python-Version: 3.7
Post-History: 09-Sep-2017


Abstract


Initially PEP 484 was designed in such way that it would not introduce
*any* changes to the core CPython interpreter. Now type hints and
the ``typing`` module are extensively used by the community, e.g. PEP 526
and PEP 557 extend the usage of type hints, and the backport of ``typing``
on PyPI has 1M downloads/month. Therefore, this restriction can be removed.
It is proposed to add two special methods ``__class_getitem__`` and
``__mro_entries__`` to the core CPython for better support of
generic types.


Rationale
=

The restriction to not modify the core CPython interpreter led to some
design decisions that became questionable when the ``typing`` module started
to be widely used. There are three main points of concern:
performance of the ``typing`` module, metaclass conflicts, and the large
number of hacks currently used in ``typing``.


Performance
---

The ``typing`` module is one of the heaviest and slowest modules in
the standard library even with all the optimizations made. Mainly this is
because of subscripted generic types (see PEP 484 for definition of terms
used in this PEP) are class objects (see also [1]_). The three main ways how
the performance can be improved with the help of the proposed special
methods:

- Creation of generic classes is slow since the ``GenericMeta.__new__`` is
  very slow; we will not need it anymore.

- Very long MROs for generic classes will be twice shorter; they are present
  because we duplicate the ``collections.abc`` inheritance chain
  in ``typing``.

- Time of instantiation of generic classes will be improved
  (this is minor however).


Metaclass conflicts
---

All generic types are instances of ``GenericMeta``, so if a user uses
a custom metaclass, then it is hard to make a corresponding class generic.
This is particularly hard for library classes that a user doesn't control.
A workaround is to always mix-in ``GenericMeta``::

  class AdHocMeta(GenericMeta, LibraryMeta):
  pass

  class UserClass(LibraryBase, Generic[T], metaclass=AdHocMeta):
  ...

but this is not always practical or even possible. With the help of the
proposed special attributes the ``GenericMeta`` metaclass will not be
needed.


Hacks and bugs that will be removed by this proposal


- ``_generic_new`` hack that exists because ``__init__`` is not called on
  instances with a type differing form the type whose ``__new__`` was
called,
  ``C[int]().__class__ is C``.

- ``_next_in_mro`` speed hack will be not necessary since subscription will
  not create new classes.

- Ugly ``sys._getframe`` hack. This one is particularly nasty since it looks
  like we can't remove it without changes outside ``typing``.

- Currently generics do dangerous things with private ABC caches
  to fix large memory consumption that grows at least as O(N\ :sup:`2`),
  see [2]_. This point is also important because it was recently proposed to
  re-implement ``ABCMeta`` in C.

- Problems with sharing attributes between subscripted generics,
  see [3]_. The current solution already uses ``__getattr__`` and
``__setattr__``,
  but it is still incomplete, and solving this without the current proposal
  will be hard and will need ``__getattribute__``.

- ``_no_slots_copy`` hack, where we clean up the class dictionary on every
  subscription thus allowing generics with ``__slots__``.

- General complexity of the ``typing`` module. The new proposal will not
  only allow to remove the above mentioned hacks/bugs, but also simplify
  the implementation, so that it will be easier to maintain.


Specification
=

``__class_getitem__``
-

The idea of ``__class_getitem__`` is simple: it is an exact analog of
``__getitem__`` with an exception that it is called on a class that
defines it, not on its instances. This allows us to avoid
``GenericMeta.__getitem__`` for things like ``Iterable[int]``.
The ``__class_getitem__`` is automatically a class method and
does not require ``@classmethod`` decorator (similar to
``__init_subclass__``) and is inherited like normal attributes.
For example::

  class MyList:
  def __getitem__(self, index):
  return index + 1
  def __class_getitem__(cls, item):
  return f"{cls.__name__}[{item.__name__}]"

  class MyOtherList(MyList):
  pass

  assert MyL

Re: [Python-Dev] PEP 560

2017-11-14 Thread Nick Coghlan
On 15 November 2017 at 06:26, Ivan Levkivskyi  wrote:

> After some discussion on python-ideas, see https://mail.python.org/
> pipermail/python-ideas/2017-September/047220.html, this PEP received
> positive comments. The updated version that takes into account the comments
> that appeared in the discussion so far is available at
> https://www.python.org/dev/peps/pep-0560/
>

I don't have anything to add to the python-ideas comments you already
incorporated, so +1 for this version from me.

* ``importlib.reload(typing)`` is up to 7x faster
>

Nice! That's getting much closer to the "negligible" range, even for
command line apps.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560

2017-11-15 Thread Terry Reedy

On 11/14/2017 3:26 PM, Ivan Levkivskyi wrote:
After some discussion on python-ideas, see 
https://mail.python.org/pipermail/python-ideas/2017-September/047220.html, 
this PEP received positive comments. The updated version that takes into 
account the comments that appeared in the discussion so far is available 
at https://www.python.org/dev/peps/pep-0560/


Here I post the full text for convenience:

++

PEP: 560
Title: Core support for typing module and generic types
Author: Ivan Levkivskyi mailto:levkivs...@gmail.com>>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 03-Sep-2017
Python-Version: 3.7
Post-History: 09-Sep-2017

...

Suggested wording improvements:


Performance
---

The ``typing`` module is one of the heaviest and slowest modules in
the standard library even with all the optimizations made.



Mainly this is
because of subscripted generic types (see PEP 484 for definition of terms
used in this PEP) are class objects (see also [1]_).


Delete 'of' after 'because' to make this a proper sentence.


The three main ways how


"There are three ..." reads better to me.

the performance can be improved with the help of the proposed special 
methods:


- Creation of generic classes is slow since the ``GenericMeta.__new__`` is
   very slow; we will not need it anymore.

- Very long MROs for generic classes will be twice shorter;


I  believe by 'twice shorter', which is meaningless by itself, you mean 
'half as long'.  If so, please say the latter.

 > they are present

   because we duplicate the ``collections.abc`` inheritance chain
   in ``typing``.

- Time of instantiation of generic classes will be improved


Instantiation of generic classes will be faster.


   (this is minor however).


--
Terry Jan Reedy


___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


[Python-Dev] PEP 560: bases classes / confusion

2017-11-15 Thread Jim J. Jewett
(1)  I found the following (particularly "bases classes") very confusing:

"""
If an object that is not a class object appears in the bases of a class

definition, then ``__mro_entries__`` is searched on it. If found,
it is called with the original tuple of bases as an argument. The result
of the call must be a tuple, that is unpacked in the bases classes in place
of this object. (If the tuple is empty, this means that the original bases
is
simply discarded.)
"""

Based on the following GenericAlias/NewList/Tokens example, I think I
now I understand what you mean, and would have had somewhat less
difficulty if it were expressed as:

"""
When an object that is not a class object appears in the (tuple of)
bases of a class
definition, then attribute ``__mro_entries__`` is searched on that
non-class object.  If ``__mro_entries__`` found,
it is called with the entire original tuple of bases as an argument. The result
of the call must be a tuple, which is unpacked and replaces only the
non-class object in the tuple of bases.  (If the tuple is empty, this
means that the original bases
is
simply discarded.)
"""

Note that this makes some assumptions about the __mro_entries__
signature that I wasn't quite sure about from the example.  So
building on that:

class ABList(A, NewList[int], B):

I *think* the following will happen:

"NewList[int]" will be evaluated, and __class_getitem__ called, so
that the bases tuple will be (A, GenericAlias(NewList, int), B)

# (A)  I *think* __mro_entries__ gets called with the full tuple,
# instead of just the object it is found on.
# (B) I *think* it is called on the results of evaluating
# the terms within the tuple, instead of the original
# string representation.
_tmp = __mro_entries__(A, GenericAlias(NewList, int), B)

# (C)  I *think* __mro_entries__ returns a replacement for
# just the single object, even though it was called on
# the whole tuple, without knowing which object it
# represents.
bases = (A, _tmp, B)

# (D) If there are two non-class objects, I *think* the
# second one gets the same arguments as the first,
# rather than an intermediate tuple with the first such
# object already substituted out.

-jJ
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560: bases classes / confusion

2017-11-15 Thread Koos Zevenhoven
For anyone confused about similar things, I expect you to be interested in
my post on python-ideas from today:

https://mail.python.org/pipermail/python-ideas/2017-November/047896.html

––Koos


On Wed, Nov 15, 2017 at 4:20 PM, Jim J. Jewett  wrote:

> (1)  I found the following (particularly "bases classes") very confusing:
>
> """
> If an object that is not a class object appears in the bases of a class
>
> definition, then ``__mro_entries__`` is searched on it. If found,
> it is called with the original tuple of bases as an argument. The result
> of the call must be a tuple, that is unpacked in the bases classes in place
> of this object. (If the tuple is empty, this means that the original bases
> is
> simply discarded.)
> """
>
> Based on the following GenericAlias/NewList/Tokens example, I think I
> now I understand what you mean, and would have had somewhat less
> difficulty if it were expressed as:
>
> """
> When an object that is not a class object appears in the (tuple of)
> bases of a class
> definition, then attribute ``__mro_entries__`` is searched on that
> non-class object.  If ``__mro_entries__`` found,
> it is called with the entire original tuple of bases as an argument. The
> result
> of the call must be a tuple, which is unpacked and replaces only the
> non-class object in the tuple of bases.  (If the tuple is empty, this
> means that the original bases
> is
> simply discarded.)
> """
>
> Note that this makes some assumptions about the __mro_entries__
> signature that I wasn't quite sure about from the example.  So
> building on that:
>
> class ABList(A, NewList[int], B):
>
> I *think* the following will happen:
>
> "NewList[int]" will be evaluated, and __class_getitem__ called, so
> that the bases tuple will be (A, GenericAlias(NewList, int), B)
>
> # (A)  I *think* __mro_entries__ gets called with the full tuple,
> # instead of just the object it is found on.
> # (B) I *think* it is called on the results of evaluating
> # the terms within the tuple, instead of the original
> # string representation.
> _tmp = __mro_entries__(A, GenericAlias(NewList, int), B)
>
> # (C)  I *think* __mro_entries__ returns a replacement for
> # just the single object, even though it was called on
> # the whole tuple, without knowing which object it
> # represents.
> bases = (A, _tmp, B)
>
> # (D) If there are two non-class objects, I *think* the
> # second one gets the same arguments as the first,
> # rather than an intermediate tuple with the first such
> # object already substituted out.
>
> -jJ
> ___
> Python-Dev mailing list
> Python-Dev@python.org
> https://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: https://mail.python.org/mailman/options/python-dev/
> k7hoven%40gmail.com
>



-- 
+ Koos Zevenhoven + http://twitter.com/k7hoven +
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560: bases classes / confusion

2017-11-15 Thread Nick Coghlan
On 16 November 2017 at 00:20, Jim J. Jewett  wrote:

> I *think* the following will happen:
>
> "NewList[int]" will be evaluated, and __class_getitem__ called, so
> that the bases tuple will be (A, GenericAlias(NewList, int), B)
>
> # (A)  I *think* __mro_entries__ gets called with the full tuple,
> # instead of just the object it is found on.
> # (B) I *think* it is called on the results of evaluating
> # the terms within the tuple, instead of the original
> # string representation.
> _tmp = __mro_entries__(A, GenericAlias(NewList, int), B)
>
> # (C)  I *think* __mro_entries__ returns a replacement for
> # just the single object, even though it was called on
> # the whole tuple, without knowing which object it
> # represents.
> bases = (A, _tmp, B)
>

My understanding of the method signature:

def __mro_entries__(self, orig_bases):
...
return replacement_for_self

My assumption as to the purpose of the extra complexity was:

- given orig_bases, a method could avoid injecting bases already listed if
it wanted to
- allowing multiple items to be returned provides a way to programmatically
combine mixins without having to define a new subclass for each combination

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560: bases classes / confusion

2017-11-15 Thread Ivan Levkivskyi
Nick is exactly right here. Jim, if you want to propose alternative
wording, then we could consider it.

--
Ivan


On 15 November 2017 at 16:37, Nick Coghlan  wrote:

> On 16 November 2017 at 00:20, Jim J. Jewett  wrote:
>
>> I *think* the following will happen:
>>
>> "NewList[int]" will be evaluated, and __class_getitem__ called, so
>> that the bases tuple will be (A, GenericAlias(NewList, int), B)
>>
>> # (A)  I *think* __mro_entries__ gets called with the full tuple,
>> # instead of just the object it is found on.
>> # (B) I *think* it is called on the results of evaluating
>> # the terms within the tuple, instead of the original
>> # string representation.
>> _tmp = __mro_entries__(A, GenericAlias(NewList, int), B)
>>
>> # (C)  I *think* __mro_entries__ returns a replacement for
>> # just the single object, even though it was called on
>> # the whole tuple, without knowing which object it
>> # represents.
>> bases = (A, _tmp, B)
>>
>
> My understanding of the method signature:
>
> def __mro_entries__(self, orig_bases):
> ...
> return replacement_for_self
>
> My assumption as to the purpose of the extra complexity was:
>
> - given orig_bases, a method could avoid injecting bases already listed if
> it wanted to
> - allowing multiple items to be returned provides a way to
> programmatically combine mixins without having to define a new subclass for
> each combination
>
> Cheers,
> Nick.
>
> --
> Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
>
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560: bases classes / confusion

2017-11-15 Thread Koos Zevenhoven
On Wed, Nov 15, 2017 at 5:37 PM, Nick Coghlan  wrote:

> On 16 November 2017 at 00:20, Jim J. Jewett  wrote:
>
>> I *think* the following will happen:
>>
>> "NewList[int]" will be evaluated, and __class_getitem__ called, so
>> that the bases tuple will be (A, GenericAlias(NewList, int), B)
>>
>> # (A)  I *think* __mro_entries__ gets called with the full tuple,
>> # instead of just the object it is found on.
>> # (B) I *think* it is called on the results of evaluating
>> # the terms within the tuple, instead of the original
>> # string representation.
>> _tmp = __mro_entries__(A, GenericAlias(NewList, int), B)
>>
>> # (C)  I *think* __mro_entries__ returns a replacement for
>> # just the single object, even though it was called on
>> # the whole tuple, without knowing which object it
>> # represents.
>> bases = (A, _tmp, B)
>>
>
> My understanding of the method signature:
>
> def __mro_entries__(self, orig_bases):
> ...
> return replacement_for_self
>
> My assumption as to the purpose of the extra complexity was:
>
> - given orig_bases, a method could avoid injecting bases already listed if
> it wanted to
> - allowing multiple items to be returned provides a way to
> programmatically combine mixins without having to define a new subclass for
> each combination
>
>

​Thanks, this might provide an answer to my question about multiple mro
entries here

https://mail.python.org/pipermail/python-ideas/2017-November/047897.html​

​––Koos​



-- 
+ Koos Zevenhoven + http://twitter.com/k7hoven +
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560: bases classes / confusion

2017-11-15 Thread Nick Coghlan
On 16 November 2017 at 04:39, Ivan Levkivskyi  wrote:

> Nick is exactly right here. Jim, if you want to propose alternative
> wording, then we could consider it.
>

Jim also raised an important point that needs clarification at the spec
level: given multiple entries in "orig_bases" with __mro_entries__ methods,
do all such methods get passed the *same* orig_bases tuple? Or do they
receive partially resolved ones, such that bases listed before them have
already been resolved to their MRO entries by the time they run.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560: bases classes / confusion

2017-11-16 Thread Ivan Levkivskyi
On 16 November 2017 at 07:56, Nick Coghlan  wrote:

> On 16 November 2017 at 04:39, Ivan Levkivskyi 
> wrote:
>
>> Nick is exactly right here. Jim, if you want to propose alternative
>> wording, then we could consider it.
>>
>
> Jim also raised an important point that needs clarification at the spec
> level: given multiple entries in "orig_bases" with __mro_entries__ methods,
> do all such methods get passed the *same* orig_bases tuple? Or do they
> receive partially resolved ones, such that bases listed before them have
> already been resolved to their MRO entries by the time they run.
>
>
>
Yes, they all get the same initial bases tuple as an argument. Passing
updated ones will cost a bit more and I don't think it will be needed (in
the worst case a base can resolve another base by calling its
__mro_entries__ manually).
I will clarify this in the PEP.

--
Ivan
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560: bases classes / confusion

2017-11-16 Thread brent bejot
Hello all,

Noticed that "MRO" is not actually defined in the PEP and it seems like it
should be.  Probably in the Performance section where the abbreviation is
first used outside of a function name.

-Brent

On Thu, Nov 16, 2017 at 7:22 AM, Ivan Levkivskyi 
wrote:

> On 16 November 2017 at 07:56, Nick Coghlan  wrote:
>
>> On 16 November 2017 at 04:39, Ivan Levkivskyi 
>> wrote:
>>
>>> Nick is exactly right here. Jim, if you want to propose alternative
>>> wording, then we could consider it.
>>>
>>
>> Jim also raised an important point that needs clarification at the spec
>> level: given multiple entries in "orig_bases" with __mro_entries__ methods,
>> do all such methods get passed the *same* orig_bases tuple? Or do they
>> receive partially resolved ones, such that bases listed before them have
>> already been resolved to their MRO entries by the time they run.
>>
>>
>>
> Yes, they all get the same initial bases tuple as an argument. Passing
> updated ones will cost a bit more and I don't think it will be needed (in
> the worst case a base can resolve another base by calling its
> __mro_entries__ manually).
> I will clarify this in the PEP.
>
> --
> Ivan
>
>
>
> ___
> Python-Dev mailing list
> Python-Dev@python.org
> https://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: https://mail.python.org/mailman/options/python-dev/
> brent.bejot%40gmail.com
>
>
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560: bases classes / confusion

2017-11-16 Thread Koos Zevenhoven
On Thu, Nov 16, 2017 at 6:28 PM, brent bejot  wrote:

> Hello all,
>
> Noticed that "MRO" is not actually defined in the PEP and it seems like it
> should be.  Probably in the Performance section where the abbreviation is
> first used outside of a function name.
>
>
​I don't think it will hurt if I suggest that __bases__, bases, "original
bases"​, mro, __orig_bases__, MRO, __mro__ and "concatenated mro entries"
are all defined as synonyms of each other, except with different meanings
:-)

––Koos



> -Brent
>
> On Thu, Nov 16, 2017 at 7:22 AM, Ivan Levkivskyi 
> wrote:
>
>> On 16 November 2017 at 07:56, Nick Coghlan  wrote:
>>
>>> On 16 November 2017 at 04:39, Ivan Levkivskyi 
>>> wrote:
>>>
 Nick is exactly right here. Jim, if you want to propose alternative
 wording, then we could consider it.

>>>
>>> Jim also raised an important point that needs clarification at the spec
>>> level: given multiple entries in "orig_bases" with __mro_entries__ methods,
>>> do all such methods get passed the *same* orig_bases tuple? Or do they
>>> receive partially resolved ones, such that bases listed before them have
>>> already been resolved to their MRO entries by the time they run.
>>>
>>>
>>>
>> Yes, they all get the same initial bases tuple as an argument. Passing
>> updated ones will cost a bit more and I don't think it will be needed (in
>> the worst case a base can resolve another base by calling its
>> __mro_entries__ manually).
>> I will clarify this in the PEP.
>>
>> --
>> Ivan
>>
>>
>>
>> ___
>> Python-Dev mailing list
>> Python-Dev@python.org
>> https://mail.python.org/mailman/listinfo/python-dev
>> Unsubscribe: https://mail.python.org/mailman/options/python-dev/brent.
>> bejot%40gmail.com
>>
>>
>
> ___
> Python-Dev mailing list
> Python-Dev@python.org
> https://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: https://mail.python.org/mailman/options/python-dev/
> k7hoven%40gmail.com
>
>


-- 
+ Koos Zevenhoven + http://twitter.com/k7hoven +
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560: bases classes / confusion

2017-11-16 Thread Ethan Furman

On 11/16/2017 04:22 AM, Ivan Levkivskyi wrote:

On 16 November 2017 at 07:56, Nick Coghlan wrote:



Jim also raised an important point that needs clarification at the spec

>> level: given multiple entries in "orig_bases" with __mro_entries__ methods,
>> do all such methods get passed the *same* orig_bases tuple? Or do they

receive partially resolved ones, such that bases listed before them have

>> already been resolved to their MRO entries by the time they run.


Yes, they all get the same initial bases tuple as an argument. Passing

> updated ones will cost a bit more and I don't think it will be needed
> (in the worst case a base can resolve another base by calling its
> __mro_entries__ manually).  I will clarify this in the PEP.

If the extra complexity is to:

> - given orig_bases, a method could avoid injecting bases already listed
>   if it wanted to
> - allowing multiple items to be returned provides a way to programmatically
>   combine mixins without having to define a new subclass for each combination

And each method is passed the same original tuple (without other methods' updates) then don't we end up in a situation 
where we can have duplicates base classes?


--
~Ethan~
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560: bases classes / confusion

2017-11-16 Thread Ivan Levkivskyi
On 16 November 2017 at 18:58, Ethan Furman  wrote:

> On 11/16/2017 04:22 AM, Ivan Levkivskyi wrote:
>
>> On 16 November 2017 at 07:56, Nick Coghlan wrote:
>>
>
> Jim also raised an important point that needs clarification at the spec
>>>
>> >> level: given multiple entries in "orig_bases" with __mro_entries__
> methods,
> >> do all such methods get passed the *same* orig_bases tuple? Or do they
>
>> receive partially resolved ones, such that bases listed before them have
>>>
>> >> already been resolved to their MRO entries by the time they run.
>
>>
>> Yes, they all get the same initial bases tuple as an argument. Passing
>>
> > updated ones will cost a bit more and I don't think it will be needed
> > (in the worst case a base can resolve another base by calling its
> > __mro_entries__ manually).  I will clarify this in the PEP.
>
> If the extra complexity is to:
>
> > - given orig_bases, a method could avoid injecting bases already listed
> >   if it wanted to
> > - allowing multiple items to be returned provides a way to
> programmatically
> >   combine mixins without having to define a new subclass for each
> combination
>
> And each method is passed the same original tuple (without other methods'
> updates) then don't we end up in a situation where we can have duplicates
> base classes?
>

Not that it is impossible now (in certain sense):

class MultiMeta(type):
def __new__(mcls, name, bases, ns):
return super().__new__(mcls, name, (), ns)

class MultiBase(metaclass=MultiMeta):
pass

class C(MultiBase, list, list, MultiBase, dict, dict, dict):  # OK
pass

What is probably confusing in the current PEP text, is that it doesn't say
clearly that
the substitution happens before any other steps in __build_class__.
Therefore all normal checks (like duplicate bases and MRO consistency)
happen and e.g.

class C(List[int], List[str]):
pass

will fail with:

TypeError: duplicate base class list

(by the way while playing with this I have found a bug in the reference
implementation)

--
Ivan
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


[Python-Dev] PEP 560 vs metaclass' class definition protections [was Re: What is the design purpose of metaclasses ...]

2017-10-14 Thread Ethan Furman

On 10/14/2017 08:57 AM, Ivan Levkivskyi wrote:

On 14 October 2017 at 17:49, Ethan Furman wrote:



The problem with PEP 560 is that it doesn't allow the class definition

>> protections that a metaclass does.


Since the discussion turned to PEP 560, I can say that I don't want this

> to be a general mechanism, the PEP rationale section gives several specific
> examples why we don't want metaclasses to implement generic class
> machinery/internals.


Could you please elaborate more what is wrong with PEP 560 and what do you

> mean by "class definition protections"

Nothing is wrong with PEP 560.  What I am referring to is:

class MyEnum(Enum):
   red = 0
   red = 1

The Enum metaclass machinery will raise an error at the "red = 1" line because it detects the redefinition of "red". 
This check can only happen during class definition, so only the metaclass can do it.


--
~Ethan~
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560 vs metaclass' class definition protections [was Re: What is the design purpose of metaclasses ...]

2017-10-14 Thread Nick Coghlan
On 15 October 2017 at 02:14, Ethan Furman  wrote:

> On 10/14/2017 08:57 AM, Ivan Levkivskyi wrote:
>
>> On 14 October 2017 at 17:49, Ethan Furman wrote:
>>
>
> The problem with PEP 560 is that it doesn't allow the class definition
>>>
>> >> protections that a metaclass does.
>
>>
>> Since the discussion turned to PEP 560, I can say that I don't want this
>>
> > to be a general mechanism, the PEP rationale section gives several
> specific
> > examples why we don't want metaclasses to implement generic class
> > machinery/internals.
>
>>
>> Could you please elaborate more what is wrong with PEP 560 and what do you
>>
> > mean by "class definition protections"
>
> Nothing is wrong with PEP 560.  What I am referring to is:
>
> class MyEnum(Enum):
>red = 0
>red = 1
>
> The Enum metaclass machinery will raise an error at the "red = 1" line
> because it detects the redefinition of "red". This check can only happen
> during class definition, so only the metaclass can do it.
>

That's not necessarily an inherent restriction though - if we did decide to
go even further in the direction of "How do we let base classes override
semantics that currently require a custom metaclass?", then there's a
fairly clear parallel between "mcl.__init__/bases.__init_subclass__" and
"mcl.__prepare__/bases.__prepare_subclass__".

OTOH, if you have multiple bases with competing __prepare__  methods you
really *should* get a metaclass conflict, since the class body can only be
executed in one namespace.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560 vs metaclass' class definition protections [was Re: What is the design purpose of metaclasses ...]

2017-10-14 Thread Ivan Levkivskyi
On 14 October 2017 at 18:14, Ethan Furman  wrote:

> On 10/14/2017 08:57 AM, Ivan Levkivskyi wrote:
>
>>
>> Could you please elaborate more what is wrong with PEP 560 and what do
>> you mean by "class definition protections"
>>
>
> Nothing is wrong with PEP 560.  What I am referring to is:
>
[snip]
>
>
OK thanks, then let us keep PEP 560 to its original scope. Its design is
specific to generic classes,
so it will probably not help with "wider" metaclass problems.

As a side note, I don't think elimination of metaclasses should be a "goal
by itself".
This is a powerful and flexible mechanism, but there are specific
situations where
metaclasses don't work well because of e.g. frequent conflicts or
performance penalties.

--
Ivan
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 560 vs metaclass' class definition protections [was Re: What is the design purpose of metaclasses ...]

2017-10-14 Thread Ethan Furman

On 10/14/2017 11:30 AM, Ivan Levkivskyi wrote:


As a side note, I don't think elimination of metaclasses should be a "goal by 
itself".
This is a powerful and flexible mechanism, but there are specific situations 
where
metaclasses don't work well because of e.g. frequent conflicts or performance 
penalties.


+1

--
~Ethan~

___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com