-
-
Notifications
You must be signed in to change notification settings - Fork 31.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PyType_FromSpec refuses to create classes with tp_new #103968
Comments
…stom tp_new. (That's a mouthful of an edge case!)
…p_new. (GH-103972) (That's a mouthful of an edge case!) Co-authored-by: Barney Gale <barney.gale@gmail.com>
Deprecation period started. |
The 3.12 docs now say "The tp_new of the metaclass is ignored. which may result in incomplete initialization." statements, but it isn't clear if this is a part of the versionchanged in 3.12 behavior, or if this was always the case. Basically, we need explicit instructions on what code finding itself in the situation that protobuf protocolbuffers/protobuf#12186 finds itself in is supposed to do to reconcile the situation. 3.12's "What's New" does not directly address this today. It just talks about tp_new being ignored as if that is a behavior change rather than if it was always ignored without being documented as such or what should be done to get the previous behavior. |
Late discussion on #60074 which created the TypeError that this issue properly made into a DeprecationWarning has suggestions for a workaround but they definitely qualify as hacks. Deeper understanding of why that tp_new was set and what it's goal was and when it was ever called or not in the past is likely required. |
(marking deferred-blocker as we should at least address the documentation on this during the 3.12 beta phase before release) |
Previously, the metaclass was ignored entirely, so its The old behaviour was dangerous. Consider:
If you control the metaclass and know that The hack -- unsetting metaclass I'll add explicit porting instructions to What's New, including the hack. |
I looked at protobuf. protobuf uses
I didn't find out how to get a hold of protobuf's containers, but here's a small reproducer: C extension reproducermymod.c: #include <assert.h>
#include <Python.h>
static PyType_Slot MyType_slots[] = {
{0, NULL},
};
PyType_Spec MyType_spec = {
"mymod.MyType", sizeof(PyObject), 0,
Py_TPFLAGS_DEFAULT, MyType_slots};
PyObject *
get_mytype(PyObject *self, PyObject *unused)
{
PyObject *abc = PyImport_ImportModule("collections.abc");
assert(abc); // XXX error handling
PyObject *mutable_mapping = PyObject_GetAttrString(abc, "MutableMapping");
assert(mutable_mapping); // XXX error handling
PyObject *MyType = PyType_FromSpecWithBases(&MyType_spec, mutable_mapping);
assert(MyType); // XXX error handling
Py_DECREF(abc);
Py_DECREF(mutable_mapping);
return MyType;
}
PyObject *
reproducer(PyObject *self, PyObject *unused)
{
return PyRun_String("\
t = get_mytype() \n\
for base in t.mro(): \n\
print(getattr(base, '_abc_impl', None), base) \n\
", Py_file_input, PyModule_GetDict(self), NULL);
}
PyMethodDef mod_methods[] = {
{"get_mytype", get_mytype, METH_NOARGS, NULL},
{"reproducer", reproducer, METH_NOARGS, NULL},
{NULL},
};
PyModuleDef mod = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "mymod",
.m_methods = mod_methods,
};
PyObject *
PyInit_mymod(void) {
return PyModuleDef_Init(&mod);
} Hacky non-distutils compilation/run (Linux, installed Python): V=3.11
gcc $(python$V-config --cflags --ldflags) -lpython$V$(python$V-config --abiflags) -shared mymod.c -o mymod.so
python$V -c 'import mymod; mymod.reproducer()' Result:
Note that In this particular case, it appears the bug is harmless -- the type for which I don't know what the correct general solution is, but IMO, the current behaviour is worth deprecating. |
It would be safe, and useful in some cases, to allow For What's New, here's the text I have now: Since
|
…NULL (pythonGH-105386) (cherry picked from commit 2b90796) Co-authored-by: Petr Viktorin <encukou@gmail.com>
Protobuf is using MutableMapping as a mixin. The goal is that we can define only a core set of methods (
It looks like the primary purpose of We do not want that functionality in this case. We do not want people to be able to call I suspect that anyone who uses But this seems to be what happens if you mix in an ABC in pure Python: from collections.abc import MutableMapping
class MyMapping(MutableMapping):
pass
class OtherMapping:
pass
# This works -- MyMapping has become an ABC!
MyMapping.register(OtherMapping)
assert isinstance(OtherMapping(), MyMapping) The simplest fix would be to stop inheriting from from collections.abc import MutableMapping
class ScalarMapContainer:
# ...
MutableMapping.register(ScalarMapContainer)
# Mixin the methods manually.
ScalarMapContainer.__contains__ = MutableMapping.__contains__
ScalarMapContainer.keys = MutableMapping.keys
ScalarMapContainer.items = MutableMapping.items
ScalarMapContainer.values = MutableMapping.values
# ... |
Is there anything left to do here for 3.12? |
No, short of reverting the warning. |
…etaclasses (pythonGH-105698) (cherry picked from commit af5cf1e) Co-authored-by: Petr Viktorin <encukou@gmail.com>
Just saw the warning while testing
After reading the discussion here, it could probably be Protobuf but no way to tell for the warning alone. Happy to open a new issue if this isn't the right place. |
@cdce8p - If you can reproduce this on 3.12.0rc1 please open a new issue with reproducer details (and ideally a link to the protobuf code in question). |
In Python 3.12, it is no longer possible for C types to inherit from abc.ABC since it has a custom tp_new. So we must find an alternate solution to implement collections.abc protocols. This attempts to do that by still using the methods from collection.abc but mixing in the methods manually and registering the type. Idea from: python/cpython#103968 (comment)
In Python 3.12, it is no longer possible for C types to inherit from abc.ABC since it has a custom tp_new. So we must find an alternate solution to implement collections.abc protocols. This attempts to do that by still using the methods from collection.abc but mixing in the methods manually and registering the type. Idea from: python/cpython#103968 (comment)
In Python 3.12, it is no longer possible for C types to inherit from abc.ABC since it has a custom tp_new. So we must find an alternate solution to implement collections.abc protocols. This attempts to do that by still using the methods from collection.abc but mixing in the methods manually and registering the type. Idea from: python/cpython#103968 (comment)
Leaving a note here for others that may run into this issue with cpython/Modules/_decimal/_decimal.c Lines 6008 to 6013 in 3c99969
|
I can report that for Protobuf we ended up doing what I proposed above in #103968 (comment), which is to mix in the ABC without using inheritance. We explicitly register our class as a virtual subclass: protocolbuffers/protobuf#15999 |
As reported in #60074, since
PyType_FromMetaclass
was added, other functions from thePyType_FromSpec
family refuse to create classes whose metaclass has a non-defaulttp_new
. IMO this is the correct default behaviour -- we don't have the arguments to calltp_new
with, and skipping it may be dangerous.Nevertheless, it's a backwards-incompatible change. It should only be made after a deprecation period.
I'm working on a fix.
Linked PRs
The text was updated successfully, but these errors were encountered: