Skip to content

Commit

Permalink
pythongh-117709: Add vectorcall support for str() with positional-onl…
Browse files Browse the repository at this point in the history
…y arguments (python#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]>
  • Loading branch information
3 people authored and diegorusso committed Apr 17, 2024
1 parent 8218ad9 commit 31a0d26
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Lib/test/test_str.py
Original file line number Diff line number Diff line change
Expand Up @@ -2651,6 +2651,24 @@ def test_check_encoding_errors(self):
proc = assert_python_failure('-X', 'dev', '-c', code)
self.assertEqual(proc.rc, 10, proc)

def test_str_invalid_call(self):
check = lambda *a, **kw: self.assertRaises(TypeError, str, *a, **kw)

# too many args
check(1, "", "", 1)

# no such kw arg
check(test=1)

# 'encoding' must be str
check(1, encoding=1)
check(1, 1)

# 'errors' must be str
check(1, errors=1)
check(1, "", errors=1)
check(1, 1, 1)


class StringModuleTest(unittest.TestCase):
def test_formatter_parser(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Speed up calls to :func:`str` with positional-only argument,
by using the :pep:`590` ``vectorcall`` calling convention.
Patch by Erlend Aasland.
51 changes: 51 additions & 0 deletions Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -14617,6 +14617,56 @@ unicode_new_impl(PyTypeObject *type, PyObject *x, const char *encoding,
return unicode;
}

static const char *
arg_as_utf8(PyObject *obj, const char *name)
{
if (!PyUnicode_Check(obj)) {
PyErr_Format(PyExc_TypeError,
"str() argument '%s' must be str, not %T",
name, obj);
return NULL;
}
return _PyUnicode_AsUTF8NoNUL(obj);
}

static PyObject *
unicode_vectorcall(PyObject *type, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
assert(Py_Is(_PyType_CAST(type), &PyUnicode_Type));

Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) != 0) {
// Fallback to unicode_new()
PyObject *tuple = _PyTuple_FromArray(args, nargs);
if (tuple == NULL) {
return NULL;
}
PyObject *dict = _PyStack_AsDict(args + nargs, kwnames);
if (dict == NULL) {
Py_DECREF(tuple);
return NULL;
}
PyObject *ret = unicode_new(_PyType_CAST(type), tuple, dict);
Py_DECREF(tuple);
Py_DECREF(dict);
return ret;
}
if (!_PyArg_CheckPositional("str", nargs, 0, 3)) {
return NULL;
}
if (nargs == 0) {
return unicode_get_empty();
}
PyObject *object = args[0];
if (nargs == 1) {
return PyObject_Str(object);
}
const char *encoding = arg_as_utf8(args[1], "encoding");
const char *errors = (nargs == 3) ? arg_as_utf8(args[2], "errors") : NULL;
return PyUnicode_FromEncodedObject(object, encoding, errors);
}

static PyObject *
unicode_subtype_new(PyTypeObject *type, PyObject *unicode)
{
Expand Down Expand Up @@ -14758,6 +14808,7 @@ PyTypeObject PyUnicode_Type = {
0, /* tp_alloc */
unicode_new, /* tp_new */
PyObject_Del, /* tp_free */
.tp_vectorcall = unicode_vectorcall,
};

/* Initialize the Unicode implementation */
Expand Down

0 comments on commit 31a0d26

Please sign in to comment.