[Python-Dev] PEP 580/590 discussion

Mark Shannon mark at hotpy.org
Sat Apr 27 08:07:25 EDT 2019

Hi Jeroen,

On 25/04/2019 3:42 pm, Jeroen Demeyer wrote:
> On 2019-04-25 00:24, Petr Viktorin wrote:
>> I believe we can achieve
>> that by having PEP 590's (o+offset) point not just to function pointer,
>> but to a {function pointer; flags} struct with flags defined for two
>> optimizations:
> What's the rationale for putting the flags in the instance? Do you 
> expect flags to be different between one instance and another instance 
> of the same class?
>> Both type flags and
>> nargs bits are very limited resources.
> Type flags are only a limited resource if you think that all flags ever 
> added to a type must be put into tp_flags. There is nothing wrong with 
> adding new fields tp_extraflags or tp_vectorcall_flags to a type.
>> What I don't like about it is that it has
>> the extensions built-in; mandatory for all callers/callees.
> I don't agree with the above sentence about PEP 580:
> - callers should use APIs like PyCCall_FastCall() and shouldn't need to 
> worry about the implementation details at all.
> - callees can opt out of all the extensions by not setting any special 
> flags and setting cr_self to a non-NULL value. When using the flags 
> CCALL_FASTCALL | CCALL_KEYWORDS, then implementing the callee is exactly 
> the same as PEP 590.
>> As in PEP 590, any class that uses this mechanism shall not be usable as
>> a base class.
> Can we please lift this restriction? There is really no reason for it. 
> I'm not aware of any similar restriction anywhere in CPython. Note that 
> allowing subclassing is not the same as inheriting the protocol. As a 
> compromise, we could simply never inherit the protocol.

AFAICT, any limitations on subclassing exist solely to prevent tp_call 
and the PEP 580/590 function pointer being in conflict. This limitation 
is inherent and the same for both PEPs. Do you agree?

Let us conside a class C that sets the 
It will set the function pointer in a new instance, C(), when the object 
is created. If we create a new class D:
class D(C):
     __call__(self, ...):

and then create an instance `d = D()` then calling d will have two 
contradictory behaviours; the one installed by C in the function pointer 
and the one specified by D.__call__

We can ensure correct behaviour by setting the function pointer to NULL 
or a forwarding function (depending on the implementation) if __call__ 
has been overridden. This would be enforced at class creation/readying time.


More information about the Python-Dev mailing list