Skip to content

Commit 044dc49

Browse files
erlend-aaslandcorona10vstinner
authored
gh-117709: Add vectorcall support for str() with positional-only arguments (#117746)
Fall back to tp_call() for cases when arguments are passed by name. Co-authored-by: Donghee Na <[email protected]> Co-authored-by: Victor Stinner <[email protected]>
1 parent 671cb22 commit 044dc49

File tree

3 files changed

+72
-0
lines changed

3 files changed

+72
-0
lines changed

Lib/test/test_str.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2651,6 +2651,24 @@ def test_check_encoding_errors(self):
26512651
proc = assert_python_failure('-X', 'dev', '-c', code)
26522652
self.assertEqual(proc.rc, 10, proc)
26532653

2654+
def test_str_invalid_call(self):
2655+
check = lambda *a, **kw: self.assertRaises(TypeError, str, *a, **kw)
2656+
2657+
# too many args
2658+
check(1, "", "", 1)
2659+
2660+
# no such kw arg
2661+
check(test=1)
2662+
2663+
# 'encoding' must be str
2664+
check(1, encoding=1)
2665+
check(1, 1)
2666+
2667+
# 'errors' must be str
2668+
check(1, errors=1)
2669+
check(1, "", errors=1)
2670+
check(1, 1, 1)
2671+
26542672

26552673
class StringModuleTest(unittest.TestCase):
26562674
def test_formatter_parser(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Speed up calls to :func:`str` with positional-only argument,
2+
by using the :pep:`590` ``vectorcall`` calling convention.
3+
Patch by Erlend Aasland.

Objects/unicodeobject.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14617,6 +14617,56 @@ unicode_new_impl(PyTypeObject *type, PyObject *x, const char *encoding,
1461714617
return unicode;
1461814618
}
1461914619

14620+
static const char *
14621+
arg_as_utf8(PyObject *obj, const char *name)
14622+
{
14623+
if (!PyUnicode_Check(obj)) {
14624+
PyErr_Format(PyExc_TypeError,
14625+
"str() argument '%s' must be str, not %T",
14626+
name, obj);
14627+
return NULL;
14628+
}
14629+
return _PyUnicode_AsUTF8NoNUL(obj);
14630+
}
14631+
14632+
static PyObject *
14633+
unicode_vectorcall(PyObject *type, PyObject *const *args,
14634+
size_t nargsf, PyObject *kwnames)
14635+
{
14636+
assert(Py_Is(_PyType_CAST(type), &PyUnicode_Type));
14637+
14638+
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
14639+
if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) != 0) {
14640+
// Fallback to unicode_new()
14641+
PyObject *tuple = _PyTuple_FromArray(args, nargs);
14642+
if (tuple == NULL) {
14643+
return NULL;
14644+
}
14645+
PyObject *dict = _PyStack_AsDict(args + nargs, kwnames);
14646+
if (dict == NULL) {
14647+
Py_DECREF(tuple);
14648+
return NULL;
14649+
}
14650+
PyObject *ret = unicode_new(_PyType_CAST(type), tuple, dict);
14651+
Py_DECREF(tuple);
14652+
Py_DECREF(dict);
14653+
return ret;
14654+
}
14655+
if (!_PyArg_CheckPositional("str", nargs, 0, 3)) {
14656+
return NULL;
14657+
}
14658+
if (nargs == 0) {
14659+
return unicode_get_empty();
14660+
}
14661+
PyObject *object = args[0];
14662+
if (nargs == 1) {
14663+
return PyObject_Str(object);
14664+
}
14665+
const char *encoding = arg_as_utf8(args[1], "encoding");
14666+
const char *errors = (nargs == 3) ? arg_as_utf8(args[2], "errors") : NULL;
14667+
return PyUnicode_FromEncodedObject(object, encoding, errors);
14668+
}
14669+
1462014670
static PyObject *
1462114671
unicode_subtype_new(PyTypeObject *type, PyObject *unicode)
1462214672
{
@@ -14758,6 +14808,7 @@ PyTypeObject PyUnicode_Type = {
1475814808
0, /* tp_alloc */
1475914809
unicode_new, /* tp_new */
1476014810
PyObject_Del, /* tp_free */
14811+
.tp_vectorcall = unicode_vectorcall,
1476114812
};
1476214813

1476314814
/* Initialize the Unicode implementation */

0 commit comments

Comments
 (0)