Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit498a4d6

Browse files
committed
gh-111696: Add type.__fullyqualname__ attribute
Add PyType_GetFullyQualName() function with documentation and tests.
1 parentd9fd33a commit498a4d6

File tree

7 files changed

+152
-31
lines changed

7 files changed

+152
-31
lines changed

‎Doc/c-api/type.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,13 @@ Type Objects
185185
186186
..versionadded::3.11
187187
188+
..c:function:: PyObject*PyType_GetFullyQualName(PyTypeObject *type)
189+
190+
Return the type's fully qualified name. Equivalent to getting the
191+
type's:attr:`__fullyqualname__ <class.__fullyqualname__>` attribute.
192+
193+
..versionadded::3.13
194+
188195
..c:function::void*PyType_GetSlot(PyTypeObject *type, int slot)
189196
190197
Return the function pointer stored in the given slot. If the

‎Doc/library/stdtypes.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5496,6 +5496,15 @@ types, where they are relevant. Some of these are not reported by the
54965496
..versionadded::3.3
54975497

54985498

5499+
..attribute::class.__fullyqualname__
5500+
5501+
The fully qualified name of the class instance:
5502+
``f"{class.__module__}.{class.__qualname__}"``, or ``class.__qualname__`` if
5503+
``class.__module__`` is not a string or is equal to ``"builtins"``.
5504+
5505+
..versionadded::3.13
5506+
5507+
54995508
..attribute::definition.__type_params__
55005509

55015510
The:ref:`type parameters<type-params>` of generic classes, functions,

‎Doc/whatsnew/3.13.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ Other Language Changes
125125
equivalent of the:option:`-X frozen_modules <-X>` command-line option.
126126
(Contributed by Yilei Yang in:gh:`111374`.)
127127

128+
* Add:attr:`__fullyqualname__ <class.__fullyqualname__>` read-only attribute
129+
to types: the fully qualified type name.
130+
(Contributed by Victor Stinner in:gh:`111696`.)
131+
132+
128133
New Modules
129134
===========
130135

@@ -1181,6 +1186,11 @@ New Features
11811186
:exc:`KeyError` if the key missing.
11821187
(Contributed by Stefan Behnel and Victor Stinner in:gh:`111262`.)
11831188

1189+
* Add:c:func:`PyType_GetFullyQualName` function: get the type's fully
1190+
qualified name. It is equivalent to getting the type's
1191+
:attr:`__fullyqualname__ <class.__fullyqualname__>` attribute.
1192+
(Contributed by Victor Stinner in:gh:`111696`.)
1193+
11841194

11851195
Porting to Python 3.13
11861196
----------------------

‎Include/cpython/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ PyAPI_FUNC(const char *) _PyType_Name(PyTypeObject *);
271271
PyAPI_FUNC(PyObject*)_PyType_Lookup(PyTypeObject*,PyObject*);
272272
PyAPI_FUNC(PyObject*)PyType_GetModuleByDef(PyTypeObject*,PyModuleDef*);
273273
PyAPI_FUNC(PyObject*)PyType_GetDict(PyTypeObject*);
274+
PyAPI_FUNC(PyObject*)PyType_GetFullyQualName(PyTypeObject*);
274275

275276
PyAPI_FUNC(int)PyObject_Print(PyObject*,FILE*,int);
276277
PyAPI_FUNC(void)_Py_BreakPoint(void);

‎Lib/test/test_builtin.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2430,6 +2430,7 @@ def test_new_type(self):
24302430
self.assertEqual(A.__name__,'A')
24312431
self.assertEqual(A.__qualname__,'A')
24322432
self.assertEqual(A.__module__,__name__)
2433+
self.assertEqual(A.__fullyqualname__,f'{__name__}.A')
24332434
self.assertEqual(A.__bases__, (object,))
24342435
self.assertIs(A.__base__,object)
24352436
x=A()
@@ -2443,6 +2444,7 @@ def ham(self):
24432444
self.assertEqual(C.__name__,'C')
24442445
self.assertEqual(C.__qualname__,'C')
24452446
self.assertEqual(C.__module__,__name__)
2447+
self.assertEqual(C.__fullyqualname__,f'{__name__}.C')
24462448
self.assertEqual(C.__bases__, (B,int))
24472449
self.assertIs(C.__base__,int)
24482450
self.assertIn('spam',C.__dict__)
@@ -2464,10 +2466,11 @@ def test_type_nokwargs(self):
24642466
deftest_type_name(self):
24652467
fornamein'A','\xc4','\U0001f40d','B.A','42','':
24662468
withself.subTest(name=name):
2467-
A=type(name, (), {})
2469+
A=type(name, (), {'__qualname__':f'Test.{name}'})
24682470
self.assertEqual(A.__name__,name)
2469-
self.assertEqual(A.__qualname__,name)
2471+
self.assertEqual(A.__qualname__,f"Test.{name}")
24702472
self.assertEqual(A.__module__,__name__)
2473+
self.assertEqual(A.__fullyqualname__,f'{__name__}.Test.{name}')
24712474
withself.assertRaises(ValueError):
24722475
type('A\x00B', (), {})
24732476
withself.assertRaises(UnicodeEncodeError):
@@ -2482,6 +2485,7 @@ def test_type_name(self):
24822485
self.assertEqual(C.__name__,name)
24832486
self.assertEqual(C.__qualname__,'C')
24842487
self.assertEqual(C.__module__,__name__)
2488+
self.assertEqual(C.__fullyqualname__,f'{__name__}.C')
24852489

24862490
A=type('C', (), {})
24872491
withself.assertRaises(ValueError):
@@ -2494,18 +2498,27 @@ def test_type_name(self):
24942498
A.__name__=b'A'
24952499
self.assertEqual(A.__name__,'C')
24962500

2501+
# if __module__ is not a string, ignore it silently
2502+
classD:
2503+
pass
2504+
self.assertEqual(D.__fullyqualname__,f'{__name__}.{D.__qualname__}')
2505+
D.__module__=123
2506+
self.assertEqual(D.__fullyqualname__,D.__qualname__)
2507+
24972508
deftest_type_qualname(self):
24982509
A=type('A', (), {'__qualname__':'B.C'})
24992510
self.assertEqual(A.__name__,'A')
25002511
self.assertEqual(A.__qualname__,'B.C')
25012512
self.assertEqual(A.__module__,__name__)
2513+
self.assertEqual(A.__fullyqualname__,f'{__name__}.B.C')
25022514
withself.assertRaises(TypeError):
25032515
type('A', (), {'__qualname__':b'B'})
25042516
self.assertEqual(A.__qualname__,'B.C')
25052517

25062518
A.__qualname__='D.E'
25072519
self.assertEqual(A.__name__,'A')
25082520
self.assertEqual(A.__qualname__,'D.E')
2521+
self.assertEqual(A.__fullyqualname__,f'{__name__}.D.E')
25092522
withself.assertRaises(TypeError):
25102523
A.__qualname__=b'B'
25112524
self.assertEqual(A.__qualname__,'D.E')

‎Modules/_testcapimodule.c

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -573,19 +573,19 @@ static PyObject *
573573
test_get_type_name(PyObject*self,PyObject*Py_UNUSED(ignored))
574574
{
575575
PyObject*tp_name=PyType_GetName(&PyLong_Type);
576-
assert(strcmp(PyUnicode_AsUTF8(tp_name),"int")==0);
576+
assert(PyUnicode_EqualToUTF8(tp_name,"int"));
577577
Py_DECREF(tp_name);
578578

579579
tp_name=PyType_GetName(&PyModule_Type);
580-
assert(strcmp(PyUnicode_AsUTF8(tp_name),"module")==0);
580+
assert(PyUnicode_EqualToUTF8(tp_name,"module"));
581581
Py_DECREF(tp_name);
582582

583583
PyObject*HeapTypeNameType=PyType_FromSpec(&HeapTypeNameType_Spec);
584584
if (HeapTypeNameType==NULL) {
585585
Py_RETURN_NONE;
586586
}
587587
tp_name=PyType_GetName((PyTypeObject*)HeapTypeNameType);
588-
assert(strcmp(PyUnicode_AsUTF8(tp_name),"HeapTypeNameType")==0);
588+
assert(PyUnicode_EqualToUTF8(tp_name,"HeapTypeNameType"));
589589
Py_DECREF(tp_name);
590590

591591
PyObject*name=PyUnicode_FromString("test_name");
@@ -597,7 +597,7 @@ test_get_type_name(PyObject *self, PyObject *Py_UNUSED(ignored))
597597
gotodone;
598598
}
599599
tp_name=PyType_GetName((PyTypeObject*)HeapTypeNameType);
600-
assert(strcmp(PyUnicode_AsUTF8(tp_name),"test_name")==0);
600+
assert(PyUnicode_EqualToUTF8(tp_name,"test_name"));
601601
Py_DECREF(name);
602602
Py_DECREF(tp_name);
603603

@@ -611,19 +611,19 @@ static PyObject *
611611
test_get_type_qualname(PyObject*self,PyObject*Py_UNUSED(ignored))
612612
{
613613
PyObject*tp_qualname=PyType_GetQualName(&PyLong_Type);
614-
assert(strcmp(PyUnicode_AsUTF8(tp_qualname),"int")==0);
614+
assert(PyUnicode_EqualToUTF8(tp_qualname,"int"));
615615
Py_DECREF(tp_qualname);
616616

617617
tp_qualname=PyType_GetQualName(&PyODict_Type);
618-
assert(strcmp(PyUnicode_AsUTF8(tp_qualname),"OrderedDict")==0);
618+
assert(PyUnicode_EqualToUTF8(tp_qualname,"OrderedDict"));
619619
Py_DECREF(tp_qualname);
620620

621621
PyObject*HeapTypeNameType=PyType_FromSpec(&HeapTypeNameType_Spec);
622622
if (HeapTypeNameType==NULL) {
623623
Py_RETURN_NONE;
624624
}
625625
tp_qualname=PyType_GetQualName((PyTypeObject*)HeapTypeNameType);
626-
assert(strcmp(PyUnicode_AsUTF8(tp_qualname),"HeapTypeNameType")==0);
626+
assert(PyUnicode_EqualToUTF8(tp_qualname,"HeapTypeNameType"));
627627
Py_DECREF(tp_qualname);
628628

629629
PyObject*spec_name=PyUnicode_FromString(HeapTypeNameType_Spec.name);
@@ -636,8 +636,7 @@ test_get_type_qualname(PyObject *self, PyObject *Py_UNUSED(ignored))
636636
gotodone;
637637
}
638638
tp_qualname=PyType_GetQualName((PyTypeObject*)HeapTypeNameType);
639-
assert(strcmp(PyUnicode_AsUTF8(tp_qualname),
640-
"_testcapi.HeapTypeNameType")==0);
639+
assert(PyUnicode_EqualToUTF8(tp_qualname,"_testcapi.HeapTypeNameType"));
641640
Py_DECREF(spec_name);
642641
Py_DECREF(tp_qualname);
643642

@@ -646,6 +645,42 @@ test_get_type_qualname(PyObject *self, PyObject *Py_UNUSED(ignored))
646645
Py_RETURN_NONE;
647646
}
648647

648+
staticPyObject*
649+
test_get_type_fullyqualname(PyObject*self,PyObject*Py_UNUSED(ignored))
650+
{
651+
PyObject*name=PyType_GetFullyQualName(&PyLong_Type);
652+
assert(PyUnicode_EqualToUTF8(name,"int"));
653+
Py_DECREF(name);
654+
655+
name=PyType_GetFullyQualName(&PyODict_Type);
656+
assert(PyUnicode_EqualToUTF8(name,"collections.OrderedDict"));
657+
Py_DECREF(name);
658+
659+
PyObject*HeapTypeNameType=PyType_FromSpec(&HeapTypeNameType_Spec);
660+
if (HeapTypeNameType==NULL) {
661+
Py_RETURN_NONE;
662+
}
663+
name=PyType_GetFullyQualName((PyTypeObject*)HeapTypeNameType);
664+
assert(PyUnicode_EqualToUTF8(name,"_testcapi.HeapTypeNameType"));
665+
Py_DECREF(name);
666+
667+
PyObject*new_name=PyUnicode_FromString("override_name");
668+
if (new_name==NULL) {
669+
gotodone;
670+
}
671+
672+
intres=PyObject_SetAttrString(HeapTypeNameType,
673+
"__fullyqualname__",new_name);
674+
Py_DECREF(new_name);
675+
assert(res<0);
676+
assert(PyErr_ExceptionMatches(PyExc_AttributeError));
677+
PyErr_Clear();
678+
679+
done:
680+
Py_DECREF(HeapTypeNameType);
681+
Py_RETURN_NONE;
682+
}
683+
649684
staticPyObject*
650685
test_get_type_dict(PyObject*self,PyObject*Py_UNUSED(ignored))
651686
{
@@ -3212,6 +3247,7 @@ static PyMethodDef TestMethods[] = {
32123247
{"test_get_statictype_slots",test_get_statictype_slots,METH_NOARGS},
32133248
{"test_get_type_name",test_get_type_name,METH_NOARGS},
32143249
{"test_get_type_qualname",test_get_type_qualname,METH_NOARGS},
3250+
{"test_get_type_fullyqualname",test_get_type_fullyqualname,METH_NOARGS},
32153251
{"test_get_type_dict",test_get_type_dict,METH_NOARGS},
32163252
{"_test_thread_state",test_thread_state,METH_VARARGS},
32173253
#ifndefMS_WINDOWS

‎Objects/typeobject.c

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,58 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
11231123
returnPyDict_SetItem(dict,&_Py_ID(__module__),value);
11241124
}
11251125

1126+
1127+
staticPyObject*
1128+
type_fullyqualname(PyTypeObject*type,intis_repr)
1129+
{
1130+
// type is a static type and PyType_Ready() was not called on it yet?
1131+
if (type->tp_name==NULL) {
1132+
PyErr_SetString(PyExc_TypeError,"static type not initialized");
1133+
returnNULL;
1134+
}
1135+
1136+
if (!(type->tp_flags&Py_TPFLAGS_HEAPTYPE)) {
1137+
returnPyUnicode_FromString(type->tp_name);
1138+
}
1139+
1140+
PyObject*qualname=type_qualname(type,NULL);
1141+
if (qualname==NULL) {
1142+
returnNULL;
1143+
}
1144+
1145+
PyObject*module=type_module(type,NULL);
1146+
if (module==NULL) {
1147+
if (is_repr) {
1148+
// type_repr() ignores type_module() errors
1149+
PyErr_Clear();
1150+
returnqualname;
1151+
}
1152+
1153+
Py_DECREF(qualname);
1154+
returnNULL;
1155+
}
1156+
1157+
PyObject*result;
1158+
if (PyUnicode_Check(module)
1159+
&& !_PyUnicode_Equal(module,&_Py_ID(builtins)))
1160+
{
1161+
result=PyUnicode_FromFormat("%U.%U",module,qualname);
1162+
}
1163+
else {
1164+
result=Py_NewRef(qualname);
1165+
}
1166+
Py_DECREF(module);
1167+
Py_DECREF(qualname);
1168+
returnresult;
1169+
}
1170+
1171+
staticPyObject*
1172+
type_get_fullyqualname(PyTypeObject*type,void*context)
1173+
{
1174+
returntype_fullyqualname(type,0);
1175+
}
1176+
1177+
11261178
staticPyObject*
11271179
type_abstractmethods(PyTypeObject*type,void*context)
11281180
{
@@ -1583,6 +1635,7 @@ type___subclasscheck___impl(PyTypeObject *self, PyObject *subclass)
15831635
staticPyGetSetDeftype_getsets[]= {
15841636
{"__name__", (getter)type_name, (setter)type_set_name,NULL},
15851637
{"__qualname__", (getter)type_qualname, (setter)type_set_qualname,NULL},
1638+
{"__fullyqualname__", (getter)type_get_fullyqualname,NULL,NULL},
15861639
{"__bases__", (getter)type_get_bases, (setter)type_set_bases,NULL},
15871640
{"__mro__", (getter)type_get_mro,NULL,NULL},
15881641
{"__module__", (getter)type_module, (setter)type_set_module,NULL},
@@ -1600,33 +1653,18 @@ static PyObject *
16001653
type_repr(PyTypeObject*type)
16011654
{
16021655
if (type->tp_name==NULL) {
1603-
// type_repr() called before the type is fully initialized
1604-
// by PyType_Ready().
1656+
//Iftype_repr() is called before the type is fully initialized
1657+
// by PyType_Ready(), just format the type memory address.
16051658
returnPyUnicode_FromFormat("<class at %p>",type);
16061659
}
16071660

1608-
PyObject*mod,*name,*rtn;
1609-
1610-
mod=type_module(type,NULL);
1611-
if (mod==NULL)
1612-
PyErr_Clear();
1613-
elseif (!PyUnicode_Check(mod)) {
1614-
Py_SETREF(mod,NULL);
1615-
}
1616-
name=type_qualname(type,NULL);
1661+
PyObject*name=type_fullyqualname(type,1);
16171662
if (name==NULL) {
1618-
Py_XDECREF(mod);
16191663
returnNULL;
16201664
}
1621-
1622-
if (mod!=NULL&& !_PyUnicode_Equal(mod,&_Py_ID(builtins)))
1623-
rtn=PyUnicode_FromFormat("<class '%U.%U'>",mod,name);
1624-
else
1625-
rtn=PyUnicode_FromFormat("<class '%s'>",type->tp_name);
1626-
1627-
Py_XDECREF(mod);
1665+
PyObject*result=PyUnicode_FromFormat("<class '%U'>",name);
16281666
Py_DECREF(name);
1629-
returnrtn;
1667+
returnresult;
16301668
}
16311669

16321670
staticPyObject*
@@ -4540,6 +4578,13 @@ PyType_GetQualName(PyTypeObject *type)
45404578
returntype_qualname(type,NULL);
45414579
}
45424580

4581+
PyObject*
4582+
PyType_GetFullyQualName(PyTypeObject*type)
4583+
{
4584+
returntype_get_fullyqualname(type,NULL);
4585+
}
4586+
4587+
45434588
void*
45444589
PyType_GetSlot(PyTypeObject*type,intslot)
45454590
{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp