Skip to content

Commit cc0e902

Browse files
cmaloneyclaude
andcommitted
gh-87613: Name the subclass in Argument Clinic __new__ argument errors
tp_new is inherited, so argument errors from a subclass named the base type. While `tp_name` would be slightly faster to use it includes the module name like `_thread.RLock()` which adds confusion. Use `_PyType_Name(type)` which returns `RLock()`. Include a test to check. _PyArg_NoPositional() only short-circuits when args is NULL, which tp_new never gets; use _PyArg_NoPositionalStack() to keep the name lookup off the success path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 6c695bf commit cc0e902

26 files changed

Lines changed: 115 additions & 98 deletions

‎Lib/test/clinic.test.c‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4968,7 +4968,7 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs)
49684968

49694969
if ((Py_IS_TYPE(self, base_tp) ||
49704970
Py_TYPE(self)->tp_new == base_tp->tp_new) &&
4971-
!_PyArg_NoPositional("Test", args)) {
4971+
!_PyArg_NoPositionalStack("Test", PyTuple_GET_SIZE(args))) {
49724972
goto exit;
49734973
}
49744974
if ((Py_IS_TYPE(self, base_tp) ||
@@ -5017,7 +5017,7 @@ Test_vectorcall(PyObject *type, PyObject *const *args,
50175017

50185018
static int
50195019
Test___init___impl(TestObj *self)
5020-
/*[clinic end generated code: output=ce04b9d7beacd894 input=645fe693f6c6b9b6]*/
5020+
/*[clinic end generated code: output=62017ea6333c9634 input=645fe693f6c6b9b6]*/
50215021

50225022

50235023
/*[clinic input]
@@ -5042,11 +5042,11 @@ Test(PyTypeObject *type, PyObject *args, PyObject *kwargs)
50425042
PyTypeObject *base_tp = TestType;
50435043

50445044
if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
5045-
!_PyArg_NoPositional("Test", args)) {
5045+
!_PyArg_NoPositionalStack(_PyType_Name(_PyType_CAST(type)), PyTuple_GET_SIZE(args))) {
50465046
goto exit;
50475047
}
50485048
if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
5049-
!_PyArg_NoKeywords("Test", kwargs)) {
5049+
!_PyArg_NoKeywords(_PyType_Name(_PyType_CAST(type)), kwargs)) {
50505050
goto exit;
50515051
}
50525052
return_value = Test_impl(type);
@@ -5057,7 +5057,7 @@ Test(PyTypeObject *type, PyObject *args, PyObject *kwargs)
50575057

50585058
static PyObject *
50595059
Test_impl(PyTypeObject *type)
5060-
/*[clinic end generated code: output=68a117adc057940f input=6fe98a19f097907f]*/
5060+
/*[clinic end generated code: output=deab914e0af675ba input=6fe98a19f097907f]*/
50615061

50625062

50635063
/*[clinic input]
@@ -5248,7 +5248,7 @@ Test(PyTypeObject *type, PyObject *args, PyObject *kwargs)
52485248
PyObject *__clinic_args = NULL;
52495249

52505250
if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
5251-
!_PyArg_NoKeywords("Test", kwargs)) {
5251+
!_PyArg_NoKeywords(_PyType_Name(_PyType_CAST(type)), kwargs)) {
52525252
goto exit;
52535253
}
52545254
__clinic_args = Py_NewRef(args);
@@ -5263,7 +5263,7 @@ Test(PyTypeObject *type, PyObject *args, PyObject *kwargs)
52635263

52645264
static PyObject *
52655265
Test_impl(PyTypeObject *type, PyObject *args)
5266-
/*[clinic end generated code: output=e6fba0c8951882fd input=8ce30adb836aeacb]*/
5266+
/*[clinic end generated code: output=9e97b89fb3f12f76 input=8ce30adb836aeacb]*/
52675267

52685268

52695269
/*[clinic input]

‎Lib/test/test_clinic.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5806,6 +5806,17 @@ def test_vc_new_base_subclass(self):
58065806
self.assertIsInstance(obj, Sub)
58075807
self.assertIsInstance(obj, ac_tester.VcNewBase)
58085808

5809+
def test_new_errors_name_subclass(self):
5810+
# tp_new is inherited, so its errors name the subclass; the
5811+
# vectorcall only sees the base type.
5812+
Sub = type('Sub', (float,), {})
5813+
with self.assertRaisesRegex(TypeError, r'^float expected at most 1'):
5814+
float(1, 2)
5815+
with self.assertRaisesRegex(TypeError, r'^Sub expected at most 1'):
5816+
Sub(1, 2)
5817+
with self.assertRaisesRegex(TypeError, r'^Sub\(\) takes no keyword'):
5818+
Sub(x=1)
5819+
58095820
def test_vc_kwonly(self):
58105821
# keyword-only 'b': vectorcall has no kwnames==NULL fast path,
58115822
# so every call goes through the helper.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Generate subclass names in Argument Clinic ``__new__`` error messages rather
2+
than hardcoding the base type.

‎Modules/_sqlite/clinic/row.c.h‎

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Modules/clinic/_bz2module.c.h‎

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Modules/clinic/_collectionsmodule.c.h‎

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Modules/clinic/_csv.c.h‎

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Modules/clinic/_queuemodule.c.h‎

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Modules/clinic/_ssl.c.h‎

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Modules/clinic/_testclinic_kwds.c.h‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)