Skip to content

Commit

Permalink
bpo-46613: Add PyType_GetModuleByDef to the public API (GH-31081)
Browse files Browse the repository at this point in the history
* Make PyType_GetModuleByDef public (remove underscore)

Co-authored-by: Victor Stinner <vstinner@python.org>
  • Loading branch information
encukou and vstinner committed Feb 11, 2022
1 parent e0bcfd0 commit 2049469
Show file tree
Hide file tree
Showing 21 changed files with 56 additions and 36 deletions.
17 changes: 17 additions & 0 deletions Doc/c-api/type.rst
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ Type Objects
``Py_TYPE(self)`` may be a *subclass* of the intended class, and subclasses
are not necessarily defined in the same module as their superclass.
See :c:type:`PyCMethod` to get the class that defines the method.
See :c:func:`PyType_GetModuleByDef` for cases when ``PyCMethod`` cannot
be used.
.. versionadded:: 3.9
Expand All @@ -166,6 +168,21 @@ Type Objects
.. versionadded:: 3.9
.. c:function:: PyObject* PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
Find the first superclass whose module was created from
the given :c:type:`PyModuleDef` *def*, and return that module.
If no module is found, raises a :py:class:`TypeError` and returns ``NULL``.
This function is intended to be used together with
:c:func:`PyModule_GetState()` to get module state from slot methods (such as
:c:member:`~PyTypeObject.tp_init` or :c:member:`~PyNumberMethods.nb_add`)
and other places where a method's defining class cannot be passed using the
:c:type:`PyCMethod` calling convention.
.. versionadded:: 3.11
Creating Heap-Allocated Types
.............................
Expand Down
6 changes: 3 additions & 3 deletions Doc/howto/clinic.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1249,15 +1249,15 @@ The ``defining_class`` converter is not compatible with ``__init__`` and ``__new
methods, which cannot use the ``METH_METHOD`` convention.

It is not possible to use ``defining_class`` with slot methods. In order to
fetch the module state from such methods, use ``_PyType_GetModuleByDef`` to
look up the module and then :c:func:`PyModule_GetState` to fetch the module
fetch the module state from such methods, use :c:func:`PyType_GetModuleByDef`
to look up the module and then :c:func:`PyModule_GetState` to fetch the module
state. Example from the ``setattro`` slot method in
``Modules/_threadmodule.c``::

static int
local_setattro(localobject *self, PyObject *name, PyObject *v)
{
PyObject *module = _PyType_GetModuleByDef(Py_TYPE(self), &thread_module);
PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module);
thread_module_state *state = get_thread_state(module);
...
}
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,11 @@ New Features

(Contributed by Christian Heimes in :issue:`45459`.)

* Added the :c:data:`PyType_GetModuleByDef` function, used to get the module
in which a method was defined, in cases where this information is not
available directly (via :c:type:`PyCMethod`).
(Contributed by Petr Viktorin in :issue:`46613`.)


Porting to Python 3.11
----------------------
Expand Down
2 changes: 1 addition & 1 deletion Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *
PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *);
PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *);
struct PyModuleDef;
PyAPI_FUNC(PyObject *) _PyType_GetModuleByDef(PyTypeObject *, struct PyModuleDef *);
PyAPI_FUNC(PyObject *) PyType_GetModuleByDef(PyTypeObject *, struct PyModuleDef *);

struct _Py_Identifier;
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_capi.py
Original file line number Diff line number Diff line change
Expand Up @@ -1071,15 +1071,15 @@ def test_state_access(self):
increment_count(1, 2, 3)

def test_get_module_bad_def(self):
# _PyType_GetModuleByDef fails gracefully if it doesn't
# PyType_GetModuleByDef fails gracefully if it doesn't
# find what it's looking for.
# see bpo-46433
instance = self.module.StateAccessType()
with self.assertRaises(TypeError):
instance.getmodulebydef_bad_def()

def test_get_module_static_in_mro(self):
# Here, the class _PyType_GetModuleByDef is looking for
# Here, the class PyType_GetModuleByDef is looking for
# appears in the MRO after a static type (Exception).
# see bpo-46433
class Subclass(BaseException, self.module.StateAccessType):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Added function :c:func:`PyType_GetModuleByDef`, which allows accesss to
module state when a method's defining class is not available.
2 changes: 1 addition & 1 deletion Modules/_csv.c
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ static char *dialect_kws[] = {
static _csvstate *
_csv_state_from_type(PyTypeObject *type, const char *name)
{
PyObject *module = _PyType_GetModuleByDef(type, &_csvmodule);
PyObject *module = PyType_GetModuleByDef(type, &_csvmodule);
if (module == NULL) {
return NULL;
}
Expand Down
2 changes: 1 addition & 1 deletion Modules/_functoolsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ partial_call(partialobject *pto, PyObject *args, PyObject *kwargs);
static inline _functools_state *
get_functools_state_by_type(PyTypeObject *type)
{
PyObject *module = _PyType_GetModuleByDef(type, &_functools_module);
PyObject *module = PyType_GetModuleByDef(type, &_functools_module);
if (module == NULL) {
return NULL;
}
Expand Down
2 changes: 1 addition & 1 deletion Modules/_queuemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ simplequeue_get_state(PyObject *module)
}
static struct PyModuleDef queuemodule;
#define simplequeue_get_state_by_type(type) \
(simplequeue_get_state(_PyType_GetModuleByDef(type, &queuemodule)))
(simplequeue_get_state(PyType_GetModuleByDef(type, &queuemodule)))

typedef struct {
PyObject_HEAD
Expand Down
2 changes: 1 addition & 1 deletion Modules/_randommodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ get_random_state(PyObject *module)
static struct PyModuleDef _randommodule;

#define _randomstate_type(type) \
(get_random_state(_PyType_GetModuleByDef(type, &_randommodule)))
(get_random_state(PyType_GetModuleByDef(type, &_randommodule)))

typedef struct {
PyObject_HEAD
Expand Down
2 changes: 1 addition & 1 deletion Modules/_sqlite/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ extern struct PyModuleDef _sqlite3module;
static inline pysqlite_state *
pysqlite_get_state_by_type(PyTypeObject *tp)
{
PyObject *module = _PyType_GetModuleByDef(tp, &_sqlite3module);
PyObject *module = PyType_GetModuleByDef(tp, &_sqlite3module);
assert(module != NULL);
return pysqlite_get_state(module);
}
Expand Down
4 changes: 2 additions & 2 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2988,8 +2988,8 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
int result;

/* slower approach, walk MRO and get borrowed reference to module.
* _PyType_GetModuleByDef is required for SSLContext subclasses */
PyObject *module = _PyType_GetModuleByDef(type, &_sslmodule_def);
* PyType_GetModuleByDef is required for SSLContext subclasses */
PyObject *module = PyType_GetModuleByDef(type, &_sslmodule_def);
if (module == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"Cannot find internal module state");
Expand Down
2 changes: 1 addition & 1 deletion Modules/_ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ get_ssl_state(PyObject *module)
}

#define get_state_type(type) \
(get_ssl_state(_PyType_GetModuleByDef(type, &_sslmodule_def)))
(get_ssl_state(PyType_GetModuleByDef(type, &_sslmodule_def)))
#define get_state_ctx(c) (((PySSLContext *)(c))->state)
#define get_state_sock(s) (((PySSLSocket *)(s))->ctx->state)
#define get_state_obj(o) ((_sslmodulestate *)PyType_GetModuleState(Py_TYPE(o)))
Expand Down
2 changes: 1 addition & 1 deletion Modules/_struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ get_struct_state(PyObject *module)
static struct PyModuleDef _structmodule;

#define get_struct_state_structinst(self) \
(get_struct_state(_PyType_GetModuleByDef(Py_TYPE(self), &_structmodule)))
(get_struct_state(PyType_GetModuleByDef(Py_TYPE(self), &_structmodule)))
#define get_struct_state_iterinst(self) \
(get_struct_state(PyType_GetModule(Py_TYPE(self))))

Expand Down
12 changes: 6 additions & 6 deletions Modules/_testmultiphase.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,21 +136,21 @@ _testmultiphase.StateAccessType.get_defining_module
Return the module of the defining class.
Also tests that result of _PyType_GetModuleByDef matches defining_class's
Also tests that result of PyType_GetModuleByDef matches defining_class's
module.
[clinic start generated code]*/

static PyObject *
_testmultiphase_StateAccessType_get_defining_module_impl(StateAccessTypeObject *self,
PyTypeObject *cls)
/*[clinic end generated code: output=ba2a14284a5d0921 input=356f999fc16e0933]*/
/*[clinic end generated code: output=ba2a14284a5d0921 input=d2c7245c8a9d06f8]*/
{
PyObject *retval;
retval = PyType_GetModule(cls);
if (retval == NULL) {
return NULL;
}
assert(_PyType_GetModuleByDef(Py_TYPE(self), &def_meth_state_access) == retval);
assert(PyType_GetModuleByDef(Py_TYPE(self), &def_meth_state_access) == retval);
Py_INCREF(retval);
return retval;
}
Expand All @@ -160,15 +160,15 @@ _testmultiphase.StateAccessType.getmodulebydef_bad_def
cls: defining_class
Test that result of _PyType_GetModuleByDef with a bad def is NULL.
Test that result of PyType_GetModuleByDef with a bad def is NULL.
[clinic start generated code]*/

static PyObject *
_testmultiphase_StateAccessType_getmodulebydef_bad_def_impl(StateAccessTypeObject *self,
PyTypeObject *cls)
/*[clinic end generated code: output=64509074dfcdbd31 input=906047715ee293cd]*/
/*[clinic end generated code: output=64509074dfcdbd31 input=edaff09aa4788204]*/
{
_PyType_GetModuleByDef(Py_TYPE(self), &def_nonmodule); // should raise
PyType_GetModuleByDef(Py_TYPE(self), &def_nonmodule); // should raise
assert(PyErr_Occurred());
return NULL;
}
Expand Down
6 changes: 3 additions & 3 deletions Modules/_threadmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ local_new(PyTypeObject *type, PyObject *args, PyObject *kw)
}
}

PyObject *module = _PyType_GetModuleByDef(type, &thread_module);
PyObject *module = PyType_GetModuleByDef(type, &thread_module);
thread_module_state *state = get_thread_state(module);

localobject *self = (localobject *)type->tp_alloc(type, 0);
Expand Down Expand Up @@ -925,7 +925,7 @@ _ldict(localobject *self, thread_module_state *state)
static int
local_setattro(localobject *self, PyObject *name, PyObject *v)
{
PyObject *module = _PyType_GetModuleByDef(Py_TYPE(self), &thread_module);
PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module);
thread_module_state *state = get_thread_state(module);

PyObject *ldict = _ldict(self, state);
Expand Down Expand Up @@ -977,7 +977,7 @@ static PyType_Spec local_type_spec = {
static PyObject *
local_getattro(localobject *self, PyObject *name)
{
PyObject *module = _PyType_GetModuleByDef(Py_TYPE(self), &thread_module);
PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module);
thread_module_state *state = get_thread_state(module);

PyObject *ldict = _ldict(self, state);
Expand Down
2 changes: 1 addition & 1 deletion Modules/arraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ get_array_state(PyObject *module)
}

#define find_array_state_by_type(tp) \
(get_array_state(_PyType_GetModuleByDef(tp, &arraymodule)))
(get_array_state(PyType_GetModuleByDef(tp, &arraymodule)))
#define get_array_state_by_class(cls) \
(get_array_state(PyType_GetModule(cls)))

Expand Down
2 changes: 1 addition & 1 deletion Modules/cjkcodecs/multibytecodec.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static struct PyModuleDef _multibytecodecmodule;
static _multibytecodec_state *
_multibyte_codec_find_state_by_type(PyTypeObject *type)
{
PyObject *module = _PyType_GetModuleByDef(type, &_multibytecodecmodule);
PyObject *module = PyType_GetModuleByDef(type, &_multibytecodecmodule);
assert(module != NULL);
return _multibytecodec_get_state(module);
}
Expand Down
6 changes: 3 additions & 3 deletions Modules/clinic/_testmultiphase.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 2 additions & 6 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3714,13 +3714,9 @@ PyType_GetModuleState(PyTypeObject *type)

/* Get the module of the first superclass where the module has the
* given PyModuleDef.
* Implemented by walking the MRO, is relatively slow.
*
* This is internal API for experimentation within stdlib. Discussion:
* https://mail.python.org/archives/list/capi-sig@python.org/thread/T3P2QNLNLBRFHWSKYSTPMVEIL2EEKFJU/
*/
PyObject *
_PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
{
assert(PyType_Check(type));

Expand Down Expand Up @@ -3749,7 +3745,7 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)

PyErr_Format(
PyExc_TypeError,
"_PyType_GetModuleByDef: No superclass of '%s' has the given module",
"PyType_GetModuleByDef: No superclass of '%s' has the given module",
type->tp_name);
return NULL;
}
Expand Down
2 changes: 1 addition & 1 deletion Python/Python-tokenize.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ get_tokenize_state(PyObject *module) {
}

#define _tokenize_get_state_by_type(type) \
get_tokenize_state(_PyType_GetModuleByDef(type, &_tokenizemodule))
get_tokenize_state(PyType_GetModuleByDef(type, &_tokenizemodule))

#include "clinic/Python-tokenize.c.h"

Expand Down

0 comments on commit 2049469

Please sign in to comment.