Skip to content

Commit 0d6e4d5

Browse files
committed
pythongh-101964: Increase tp_name truncation limit to 250 chars in error messages
Consistently use %.250s instead of %.100s or %.200s for tp_name format specifiers in Objects/object.c. This addresses the issue of truncated type names in error messages for C++ extension modules with deep namespace hierarchies (e.g., via pybind11). The new 250-character limit is based on real-world analysis showing that qualified type names can commonly exceed 200 characters in large codebases with deeply nested namespaces. https://claude.ai/code/session_01Kq4NcPJjTUGjVHm1cDJTb4
1 parent 7ca9e7a commit 0d6e4d5

2 files changed

Lines changed: 23 additions & 19 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Increase the truncation limit for type names (``tp_name``) in error messages
2+
from 100 or 200 characters to 250 characters consistently across
3+
:file:`Objects/object.c`. This improves debugging for C++ extension modules
4+
with long namespace-qualified class names.

Objects/object.c

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,7 +1078,7 @@ do_richcompare(PyThreadState *tstate, PyObject *v, PyObject *w, int op)
10781078
break;
10791079
default:
10801080
_PyErr_Format(tstate, PyExc_TypeError,
1081-
"'%s' not supported between instances of '%.100s' and '%.100s'",
1081+
"'%s' not supported between instances of '%.250s' and '%.250s'",
10821082
opstrings[op],
10831083
Py_TYPE(v)->tp_name,
10841084
Py_TYPE(w)->tp_name);
@@ -1144,7 +1144,7 @@ PyObject_RichCompareBool(PyObject *v, PyObject *w, int op)
11441144
Py_hash_t
11451145
PyObject_HashNotImplemented(PyObject *v)
11461146
{
1147-
PyErr_Format(PyExc_TypeError, "unhashable type: '%.200s'",
1147+
PyErr_Format(PyExc_TypeError, "unhashable type: '%.250s'",
11481148
Py_TYPE(v)->tp_name);
11491149
return -1;
11501150
}
@@ -1306,7 +1306,7 @@ PyObject_GetAttr(PyObject *v, PyObject *name)
13061306
PyTypeObject *tp = Py_TYPE(v);
13071307
if (!PyUnicode_Check(name)) {
13081308
PyErr_Format(PyExc_TypeError,
1309-
"attribute name must be string, not '%.200s'",
1309+
"attribute name must be string, not '%.250s'",
13101310
Py_TYPE(name)->tp_name);
13111311
return NULL;
13121312
}
@@ -1324,7 +1324,7 @@ PyObject_GetAttr(PyObject *v, PyObject *name)
13241324
}
13251325
else {
13261326
PyErr_Format(PyExc_AttributeError,
1327-
"'%.100s' object has no attribute '%U'",
1327+
"'%.250s' object has no attribute '%U'",
13281328
tp->tp_name, name);
13291329
}
13301330

@@ -1341,7 +1341,7 @@ PyObject_GetOptionalAttr(PyObject *v, PyObject *name, PyObject **result)
13411341

13421342
if (!PyUnicode_Check(name)) {
13431343
PyErr_Format(PyExc_TypeError,
1344-
"attribute name must be string, not '%.200s'",
1344+
"attribute name must be string, not '%.250s'",
13451345
Py_TYPE(name)->tp_name);
13461346
*result = NULL;
13471347
return -1;
@@ -1468,7 +1468,7 @@ PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
14681468

14691469
if (!PyUnicode_Check(name)) {
14701470
PyErr_Format(PyExc_TypeError,
1471-
"attribute name must be string, not '%.200s'",
1471+
"attribute name must be string, not '%.250s'",
14721472
Py_TYPE(name)->tp_name);
14731473
return -1;
14741474
}
@@ -1494,14 +1494,14 @@ PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
14941494
_PyObject_ASSERT(name, Py_REFCNT(name) >= 1);
14951495
if (tp->tp_getattr == NULL && tp->tp_getattro == NULL)
14961496
PyErr_Format(PyExc_TypeError,
1497-
"'%.100s' object has no attributes "
1497+
"'%.250s' object has no attributes "
14981498
"(%s .%U)",
14991499
tp->tp_name,
15001500
value==NULL ? "del" : "assign to",
15011501
name);
15021502
else
15031503
PyErr_Format(PyExc_TypeError,
1504-
"'%.100s' object has only read-only attributes "
1504+
"'%.250s' object has only read-only attributes "
15051505
"(%s .%U)",
15061506
tp->tp_name,
15071507
value==NULL ? "del" : "assign to",
@@ -1582,7 +1582,7 @@ PyObject *
15821582
_PyObject_NextNotImplemented(PyObject *self)
15831583
{
15841584
PyErr_Format(PyExc_TypeError,
1585-
"'%.200s' object is not iterable",
1585+
"'%.250s' object is not iterable",
15861586
Py_TYPE(self)->tp_name);
15871587
return NULL;
15881588
}
@@ -1683,7 +1683,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
16831683
}
16841684

16851685
PyErr_Format(PyExc_AttributeError,
1686-
"'%.100s' object has no attribute '%U'",
1686+
"'%.250s' object has no attribute '%U'",
16871687
tp->tp_name, name);
16881688

16891689
_PyObject_SetAttributeErrorContext(obj, name);
@@ -1786,7 +1786,7 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj,
17861786
}
17871787

17881788
PyErr_Format(PyExc_AttributeError,
1789-
"'%.100s' object has no attribute '%U'",
1789+
"'%.250s' object has no attribute '%U'",
17901790
tp->tp_name, name);
17911791

17921792
_PyObject_SetAttributeErrorContext(obj, name);
@@ -1814,7 +1814,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
18141814

18151815
if (!PyUnicode_Check(name)){
18161816
PyErr_Format(PyExc_TypeError,
1817-
"attribute name must be string, not '%.200s'",
1817+
"attribute name must be string, not '%.250s'",
18181818
Py_TYPE(name)->tp_name);
18191819
return NULL;
18201820
}
@@ -1909,7 +1909,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
19091909

19101910
if (!suppress) {
19111911
PyErr_Format(PyExc_AttributeError,
1912-
"'%.100s' object has no attribute '%U'",
1912+
"'%.250s' object has no attribute '%U'",
19131913
tp->tp_name, name);
19141914

19151915
_PyObject_SetAttributeErrorContext(obj, name);
@@ -1938,7 +1938,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
19381938
assert(!PyType_IsSubtype(tp, &PyType_Type));
19391939
if (!PyUnicode_Check(name)){
19401940
PyErr_Format(PyExc_TypeError,
1941-
"attribute name must be string, not '%.200s'",
1941+
"attribute name must be string, not '%.250s'",
19421942
Py_TYPE(name)->tp_name);
19431943
return -1;
19441944
}
@@ -1984,20 +1984,20 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
19841984
if (descr == NULL) {
19851985
if (tp->tp_setattro == PyObject_GenericSetAttr) {
19861986
PyErr_Format(PyExc_AttributeError,
1987-
"'%.100s' object has no attribute '%U' and no "
1987+
"'%.250s' object has no attribute '%U' and no "
19881988
"__dict__ for setting new attributes",
19891989
tp->tp_name, name);
19901990
}
19911991
else {
19921992
PyErr_Format(PyExc_AttributeError,
1993-
"'%.100s' object has no attribute '%U'",
1993+
"'%.250s' object has no attribute '%U'",
19941994
tp->tp_name, name);
19951995
}
19961996
_PyObject_SetAttributeErrorContext(obj, name);
19971997
}
19981998
else {
19991999
PyErr_Format(PyExc_AttributeError,
2000-
"'%.100s' object attribute '%U' is read-only",
2000+
"'%.250s' object attribute '%U' is read-only",
20012001
tp->tp_name, name);
20022002
}
20032003
goto done;
@@ -2017,7 +2017,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
20172017
error_check:
20182018
if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
20192019
PyErr_Format(PyExc_AttributeError,
2020-
"'%.100s' object has no attribute '%U'",
2020+
"'%.250s' object has no attribute '%U'",
20212021
tp->tp_name, name);
20222022
_PyObject_SetAttributeErrorContext(obj, name);
20232023
}
@@ -2132,7 +2132,7 @@ _dir_locals(void)
21322132
if (!PyList_Check(names)) {
21332133
PyErr_Format(PyExc_TypeError,
21342134
"dir(): expected keys() of locals to be a list, "
2135-
"not '%.200s'", Py_TYPE(names)->tp_name);
2135+
"not '%.250s'", Py_TYPE(names)->tp_name);
21362136
Py_DECREF(names);
21372137
return NULL;
21382138
}

0 commit comments

Comments
 (0)