mirror of https://github.com/python/cpython
gh-111696, PEP 737: Add PyType_GetFullyQualifiedName() function (#116815)
Rewrite tests on type names in Python, they were written in C.
This commit is contained in:
parent
b54d7c87aa
commit
19c3a2ff91
|
@ -185,6 +185,14 @@ Type Objects
|
||||||
|
|
||||||
.. versionadded:: 3.11
|
.. versionadded:: 3.11
|
||||||
|
|
||||||
|
.. c:function:: PyObject* PyType_GetFullyQualifiedName(PyTypeObject *type)
|
||||||
|
|
||||||
|
Return the type's fully qualified name. Equivalent to
|
||||||
|
``f"{type.__module__}.{type.__qualname__}"``, or ``type.__qualname__`` if
|
||||||
|
``type.__module__`` is not a string or is equal to ``"builtins"``.
|
||||||
|
|
||||||
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
.. c:function:: void* PyType_GetSlot(PyTypeObject *type, int slot)
|
.. c:function:: void* PyType_GetSlot(PyTypeObject *type, int slot)
|
||||||
|
|
||||||
Return the function pointer stored in the given slot. If the
|
Return the function pointer stored in the given slot. If the
|
||||||
|
|
|
@ -677,6 +677,7 @@ function,PyType_FromSpecWithBases,3.3,,
|
||||||
function,PyType_GenericAlloc,3.2,,
|
function,PyType_GenericAlloc,3.2,,
|
||||||
function,PyType_GenericNew,3.2,,
|
function,PyType_GenericNew,3.2,,
|
||||||
function,PyType_GetFlags,3.2,,
|
function,PyType_GetFlags,3.2,,
|
||||||
|
function,PyType_GetFullyQualifiedName,3.13,,
|
||||||
function,PyType_GetModule,3.10,,
|
function,PyType_GetModule,3.10,,
|
||||||
function,PyType_GetModuleState,3.10,,
|
function,PyType_GetModuleState,3.10,,
|
||||||
function,PyType_GetName,3.11,,
|
function,PyType_GetName,3.11,,
|
||||||
|
|
|
@ -1658,6 +1658,12 @@ New Features
|
||||||
between native integer types and Python :class:`int` objects.
|
between native integer types and Python :class:`int` objects.
|
||||||
(Contributed by Steve Dower in :gh:`111140`.)
|
(Contributed by Steve Dower in :gh:`111140`.)
|
||||||
|
|
||||||
|
* Add :c:func:`PyType_GetFullyQualifiedName` function to get the type's fully
|
||||||
|
qualified name. Equivalent to ``f"{type.__module__}.{type.__qualname__}"``,
|
||||||
|
or ``type.__qualname__`` if ``type.__module__`` is not a string or is equal
|
||||||
|
to ``"builtins"``.
|
||||||
|
(Contributed by Victor Stinner in :gh:`111696`.)
|
||||||
|
|
||||||
|
|
||||||
Porting to Python 3.13
|
Porting to Python 3.13
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
@ -522,6 +522,9 @@ PyAPI_FUNC(void *) PyType_GetModuleState(PyTypeObject *);
|
||||||
PyAPI_FUNC(PyObject *) PyType_GetName(PyTypeObject *);
|
PyAPI_FUNC(PyObject *) PyType_GetName(PyTypeObject *);
|
||||||
PyAPI_FUNC(PyObject *) PyType_GetQualName(PyTypeObject *);
|
PyAPI_FUNC(PyObject *) PyType_GetQualName(PyTypeObject *);
|
||||||
#endif
|
#endif
|
||||||
|
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030D0000
|
||||||
|
PyAPI_FUNC(PyObject *) PyType_GetFullyQualifiedName(PyTypeObject *);
|
||||||
|
#endif
|
||||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030C0000
|
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030C0000
|
||||||
PyAPI_FUNC(PyObject *) PyType_FromMetaclass(PyTypeObject*, PyObject*, PyType_Spec*, PyObject*);
|
PyAPI_FUNC(PyObject *) PyType_FromMetaclass(PyTypeObject*, PyObject*, PyType_Spec*, PyObject*);
|
||||||
PyAPI_FUNC(void *) PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls);
|
PyAPI_FUNC(void *) PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls);
|
||||||
|
|
|
@ -1100,21 +1100,75 @@ class CAPITest(unittest.TestCase):
|
||||||
del d.extra
|
del d.extra
|
||||||
self.assertIsNone(d.extra)
|
self.assertIsNone(d.extra)
|
||||||
|
|
||||||
def test_get_type_module_name(self):
|
def test_get_type_name(self):
|
||||||
|
class MyType:
|
||||||
|
pass
|
||||||
|
|
||||||
|
from _testcapi import get_type_name, get_type_qualname, get_type_fullyqualname
|
||||||
|
from _testinternalcapi import get_type_module_name
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
ht = _testcapi.get_heaptype_for_name()
|
ht = _testcapi.get_heaptype_for_name()
|
||||||
for cls, expected in {
|
for cls, fullname, modname, qualname, name in (
|
||||||
int: 'builtins',
|
(int,
|
||||||
OrderedDict: 'collections',
|
'int',
|
||||||
ht: '_testcapi',
|
'builtins',
|
||||||
}.items():
|
'int',
|
||||||
with self.subTest(repr(cls)):
|
'int'),
|
||||||
modname = _testinternalcapi.get_type_module_name(cls)
|
(OrderedDict,
|
||||||
self.assertEqual(modname, expected)
|
'collections.OrderedDict',
|
||||||
|
'collections',
|
||||||
|
'OrderedDict',
|
||||||
|
'OrderedDict'),
|
||||||
|
(ht,
|
||||||
|
'_testcapi.HeapTypeNameType',
|
||||||
|
'_testcapi',
|
||||||
|
'HeapTypeNameType',
|
||||||
|
'HeapTypeNameType'),
|
||||||
|
(MyType,
|
||||||
|
f'{__name__}.CAPITest.test_get_type_name.<locals>.MyType',
|
||||||
|
__name__,
|
||||||
|
'CAPITest.test_get_type_name.<locals>.MyType',
|
||||||
|
'MyType'),
|
||||||
|
):
|
||||||
|
with self.subTest(cls=repr(cls)):
|
||||||
|
self.assertEqual(get_type_fullyqualname(cls), fullname)
|
||||||
|
self.assertEqual(get_type_module_name(cls), modname)
|
||||||
|
self.assertEqual(get_type_qualname(cls), qualname)
|
||||||
|
self.assertEqual(get_type_name(cls), name)
|
||||||
|
|
||||||
|
# override __module__
|
||||||
ht.__module__ = 'test_module'
|
ht.__module__ = 'test_module'
|
||||||
modname = _testinternalcapi.get_type_module_name(ht)
|
self.assertEqual(get_type_fullyqualname(ht), 'test_module.HeapTypeNameType')
|
||||||
self.assertEqual(modname, 'test_module')
|
self.assertEqual(get_type_module_name(ht), 'test_module')
|
||||||
|
self.assertEqual(get_type_qualname(ht), 'HeapTypeNameType')
|
||||||
|
self.assertEqual(get_type_name(ht), 'HeapTypeNameType')
|
||||||
|
|
||||||
|
# override __name__ and __qualname__
|
||||||
|
MyType.__name__ = 'my_name'
|
||||||
|
MyType.__qualname__ = 'my_qualname'
|
||||||
|
self.assertEqual(get_type_fullyqualname(MyType), f'{__name__}.my_qualname')
|
||||||
|
self.assertEqual(get_type_module_name(MyType), __name__)
|
||||||
|
self.assertEqual(get_type_qualname(MyType), 'my_qualname')
|
||||||
|
self.assertEqual(get_type_name(MyType), 'my_name')
|
||||||
|
|
||||||
|
# override also __module__
|
||||||
|
MyType.__module__ = 'my_module'
|
||||||
|
self.assertEqual(get_type_fullyqualname(MyType), 'my_module.my_qualname')
|
||||||
|
self.assertEqual(get_type_module_name(MyType), 'my_module')
|
||||||
|
self.assertEqual(get_type_qualname(MyType), 'my_qualname')
|
||||||
|
self.assertEqual(get_type_name(MyType), 'my_name')
|
||||||
|
|
||||||
|
# PyType_GetFullyQualifiedName() ignores the module if it's "builtins"
|
||||||
|
# or "__main__" of it is not a string
|
||||||
|
MyType.__module__ = 'builtins'
|
||||||
|
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
|
||||||
|
MyType.__module__ = '__main__'
|
||||||
|
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
|
||||||
|
MyType.__module__ = 123
|
||||||
|
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@requires_limited_api
|
@requires_limited_api
|
||||||
class TestHeapTypeRelative(unittest.TestCase):
|
class TestHeapTypeRelative(unittest.TestCase):
|
||||||
|
|
|
@ -706,6 +706,7 @@ SYMBOL_NAMES = (
|
||||||
"PyType_GenericAlloc",
|
"PyType_GenericAlloc",
|
||||||
"PyType_GenericNew",
|
"PyType_GenericNew",
|
||||||
"PyType_GetFlags",
|
"PyType_GetFlags",
|
||||||
|
"PyType_GetFullyQualifiedName",
|
||||||
"PyType_GetModule",
|
"PyType_GetModule",
|
||||||
"PyType_GetModuleState",
|
"PyType_GetModuleState",
|
||||||
"PyType_GetName",
|
"PyType_GetName",
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Add :c:func:`PyType_GetFullyQualifiedName` function to get the type's fully
|
||||||
|
qualified name. Equivalent to ``f"{type.__module__}.{type.__qualname__}"``, or
|
||||||
|
``type.__qualname__`` if ``type.__module__`` is not a string or is equal to
|
||||||
|
``"builtins"``. Patch by Victor Stinner.
|
|
@ -2496,3 +2496,5 @@
|
||||||
[typedef.PyCFunctionFastWithKeywords]
|
[typedef.PyCFunctionFastWithKeywords]
|
||||||
added = '3.13'
|
added = '3.13'
|
||||||
# "abi-only" since 3.10. (Same story as PyCFunctionFast.)
|
# "abi-only" since 3.10. (Same story as PyCFunctionFast.)
|
||||||
|
[function.PyType_GetFullyQualifiedName]
|
||||||
|
added = '3.13'
|
||||||
|
|
|
@ -597,83 +597,31 @@ get_heaptype_for_name(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
return PyType_FromSpec(&HeapTypeNameType_Spec);
|
return PyType_FromSpec(&HeapTypeNameType_Spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
test_get_type_name(PyObject *self, PyObject *Py_UNUSED(ignored))
|
get_type_name(PyObject *self, PyObject *type)
|
||||||
{
|
{
|
||||||
PyObject *tp_name = PyType_GetName(&PyLong_Type);
|
assert(PyType_Check(type));
|
||||||
assert(strcmp(PyUnicode_AsUTF8(tp_name), "int") == 0);
|
return PyType_GetName((PyTypeObject *)type);
|
||||||
Py_DECREF(tp_name);
|
|
||||||
|
|
||||||
tp_name = PyType_GetName(&PyModule_Type);
|
|
||||||
assert(strcmp(PyUnicode_AsUTF8(tp_name), "module") == 0);
|
|
||||||
Py_DECREF(tp_name);
|
|
||||||
|
|
||||||
PyObject *HeapTypeNameType = PyType_FromSpec(&HeapTypeNameType_Spec);
|
|
||||||
if (HeapTypeNameType == NULL) {
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
tp_name = PyType_GetName((PyTypeObject *)HeapTypeNameType);
|
|
||||||
assert(strcmp(PyUnicode_AsUTF8(tp_name), "HeapTypeNameType") == 0);
|
|
||||||
Py_DECREF(tp_name);
|
|
||||||
|
|
||||||
PyObject *name = PyUnicode_FromString("test_name");
|
|
||||||
if (name == NULL) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (PyObject_SetAttrString(HeapTypeNameType, "__name__", name) < 0) {
|
|
||||||
Py_DECREF(name);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
tp_name = PyType_GetName((PyTypeObject *)HeapTypeNameType);
|
|
||||||
assert(strcmp(PyUnicode_AsUTF8(tp_name), "test_name") == 0);
|
|
||||||
Py_DECREF(name);
|
|
||||||
Py_DECREF(tp_name);
|
|
||||||
|
|
||||||
done:
|
|
||||||
Py_DECREF(HeapTypeNameType);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
test_get_type_qualname(PyObject *self, PyObject *Py_UNUSED(ignored))
|
get_type_qualname(PyObject *self, PyObject *type)
|
||||||
{
|
{
|
||||||
PyObject *tp_qualname = PyType_GetQualName(&PyLong_Type);
|
assert(PyType_Check(type));
|
||||||
assert(strcmp(PyUnicode_AsUTF8(tp_qualname), "int") == 0);
|
return PyType_GetQualName((PyTypeObject *)type);
|
||||||
Py_DECREF(tp_qualname);
|
|
||||||
|
|
||||||
tp_qualname = PyType_GetQualName(&PyODict_Type);
|
|
||||||
assert(strcmp(PyUnicode_AsUTF8(tp_qualname), "OrderedDict") == 0);
|
|
||||||
Py_DECREF(tp_qualname);
|
|
||||||
|
|
||||||
PyObject *HeapTypeNameType = PyType_FromSpec(&HeapTypeNameType_Spec);
|
|
||||||
if (HeapTypeNameType == NULL) {
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
tp_qualname = PyType_GetQualName((PyTypeObject *)HeapTypeNameType);
|
|
||||||
assert(strcmp(PyUnicode_AsUTF8(tp_qualname), "HeapTypeNameType") == 0);
|
|
||||||
Py_DECREF(tp_qualname);
|
|
||||||
|
|
||||||
PyObject *spec_name = PyUnicode_FromString(HeapTypeNameType_Spec.name);
|
|
||||||
if (spec_name == NULL) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (PyObject_SetAttrString(HeapTypeNameType,
|
|
||||||
"__qualname__", spec_name) < 0) {
|
|
||||||
Py_DECREF(spec_name);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
tp_qualname = PyType_GetQualName((PyTypeObject *)HeapTypeNameType);
|
|
||||||
assert(strcmp(PyUnicode_AsUTF8(tp_qualname),
|
|
||||||
"_testcapi.HeapTypeNameType") == 0);
|
|
||||||
Py_DECREF(spec_name);
|
|
||||||
Py_DECREF(tp_qualname);
|
|
||||||
|
|
||||||
done:
|
|
||||||
Py_DECREF(HeapTypeNameType);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
get_type_fullyqualname(PyObject *self, PyObject *type)
|
||||||
|
{
|
||||||
|
assert(PyType_Check(type));
|
||||||
|
return PyType_GetFullyQualifiedName((PyTypeObject *)type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
test_get_type_dict(PyObject *self, PyObject *Py_UNUSED(ignored))
|
test_get_type_dict(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
{
|
{
|
||||||
|
@ -3317,8 +3265,9 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"test_buildvalue_N", test_buildvalue_N, METH_NOARGS},
|
{"test_buildvalue_N", test_buildvalue_N, METH_NOARGS},
|
||||||
{"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS},
|
{"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS},
|
||||||
{"get_heaptype_for_name", get_heaptype_for_name, METH_NOARGS},
|
{"get_heaptype_for_name", get_heaptype_for_name, METH_NOARGS},
|
||||||
{"test_get_type_name", test_get_type_name, METH_NOARGS},
|
{"get_type_name", get_type_name, METH_O},
|
||||||
{"test_get_type_qualname", test_get_type_qualname, METH_NOARGS},
|
{"get_type_qualname", get_type_qualname, METH_O},
|
||||||
|
{"get_type_fullyqualname", get_type_fullyqualname, METH_O},
|
||||||
{"test_get_type_dict", test_get_type_dict, METH_NOARGS},
|
{"test_get_type_dict", test_get_type_dict, METH_NOARGS},
|
||||||
{"_test_thread_state", test_thread_state, METH_VARARGS},
|
{"_test_thread_state", test_thread_state, METH_VARARGS},
|
||||||
#ifndef MS_WINDOWS
|
#ifndef MS_WINDOWS
|
||||||
|
|
|
@ -1201,6 +1201,41 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
|
||||||
return PyDict_SetItem(dict, &_Py_ID(__module__), value);
|
return PyDict_SetItem(dict, &_Py_ID(__module__), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyType_GetFullyQualifiedName(PyTypeObject *type)
|
||||||
|
{
|
||||||
|
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
|
||||||
|
return PyUnicode_FromString(type->tp_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *qualname = type_qualname(type, NULL);
|
||||||
|
if (qualname == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *module = type_module(type, NULL);
|
||||||
|
if (module == NULL) {
|
||||||
|
Py_DECREF(qualname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *result;
|
||||||
|
if (PyUnicode_Check(module)
|
||||||
|
&& !_PyUnicode_Equal(module, &_Py_ID(builtins))
|
||||||
|
&& !_PyUnicode_Equal(module, &_Py_ID(__main__)))
|
||||||
|
{
|
||||||
|
result = PyUnicode_FromFormat("%U.%U", module, qualname);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = Py_NewRef(qualname);
|
||||||
|
}
|
||||||
|
Py_DECREF(module);
|
||||||
|
Py_DECREF(qualname);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
type_abstractmethods(PyTypeObject *type, void *context)
|
type_abstractmethods(PyTypeObject *type, void *context)
|
||||||
{
|
{
|
||||||
|
@ -1708,28 +1743,31 @@ type_repr(PyObject *self)
|
||||||
return PyUnicode_FromFormat("<class at %p>", type);
|
return PyUnicode_FromFormat("<class at %p>", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *mod, *name, *rtn;
|
PyObject *mod = type_module(type, NULL);
|
||||||
|
if (mod == NULL) {
|
||||||
mod = type_module(type, NULL);
|
|
||||||
if (mod == NULL)
|
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
else if (!PyUnicode_Check(mod)) {
|
|
||||||
Py_SETREF(mod, NULL);
|
|
||||||
}
|
}
|
||||||
name = type_qualname(type, NULL);
|
else if (!PyUnicode_Check(mod)) {
|
||||||
|
Py_CLEAR(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *name = type_qualname(type, NULL);
|
||||||
if (name == NULL) {
|
if (name == NULL) {
|
||||||
Py_XDECREF(mod);
|
Py_XDECREF(mod);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mod != NULL && !_PyUnicode_Equal(mod, &_Py_ID(builtins)))
|
PyObject *result;
|
||||||
rtn = PyUnicode_FromFormat("<class '%U.%U'>", mod, name);
|
if (mod != NULL && !_PyUnicode_Equal(mod, &_Py_ID(builtins))) {
|
||||||
else
|
result = PyUnicode_FromFormat("<class '%U.%U'>", mod, name);
|
||||||
rtn = PyUnicode_FromFormat("<class '%s'>", type->tp_name);
|
}
|
||||||
|
else {
|
||||||
|
result = PyUnicode_FromFormat("<class '%s'>", type->tp_name);
|
||||||
|
}
|
||||||
Py_XDECREF(mod);
|
Py_XDECREF(mod);
|
||||||
Py_DECREF(name);
|
Py_DECREF(name);
|
||||||
return rtn;
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
|
@ -637,6 +637,7 @@ EXPORT_FUNC(PyType_FromSpecWithBases)
|
||||||
EXPORT_FUNC(PyType_GenericAlloc)
|
EXPORT_FUNC(PyType_GenericAlloc)
|
||||||
EXPORT_FUNC(PyType_GenericNew)
|
EXPORT_FUNC(PyType_GenericNew)
|
||||||
EXPORT_FUNC(PyType_GetFlags)
|
EXPORT_FUNC(PyType_GetFlags)
|
||||||
|
EXPORT_FUNC(PyType_GetFullyQualifiedName)
|
||||||
EXPORT_FUNC(PyType_GetModule)
|
EXPORT_FUNC(PyType_GetModule)
|
||||||
EXPORT_FUNC(PyType_GetModuleState)
|
EXPORT_FUNC(PyType_GetModuleState)
|
||||||
EXPORT_FUNC(PyType_GetName)
|
EXPORT_FUNC(PyType_GetName)
|
||||||
|
|
Loading…
Reference in New Issue