Skip to content

gh-117431: Adapt bytes and bytearray .startswith() and .endswith() to Argument Clinic #117495

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 3, 2024
Merged
8 changes: 6 additions & 2 deletions Include/internal/pycore_bytes_methods.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ extern PyObject *_Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *args
extern PyObject *_Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *args);
extern PyObject *_Py_bytes_count(const char *str, Py_ssize_t len, PyObject *args);
extern int _Py_bytes_contains(const char *str, Py_ssize_t len, PyObject *arg);
extern PyObject *_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *args);
extern PyObject *_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *args);
extern PyObject *_Py_bytes_startswith(const char *str, Py_ssize_t len,
PyObject *subobj, Py_ssize_t start,
Py_ssize_t end);
extern PyObject *_Py_bytes_endswith(const char *str, Py_ssize_t len,
PyObject *subobj, Py_ssize_t start,
Py_ssize_t end);

/* The maketrans() static method. */
extern PyObject* _Py_bytes_maketrans(Py_buffer *frm, Py_buffer *to);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Improve the performance of the following :class:`bytes` and
:class:`bytearray` methods by adapting them to the :c:macro:`METH_FASTCALL`
calling convention:

* :meth:`!endswith`
* :meth:`!startswith`
50 changes: 42 additions & 8 deletions Objects/bytearrayobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1186,16 +1186,52 @@ bytearray_contains(PyObject *self, PyObject *arg)
return _Py_bytes_contains(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), arg);
}

/*[clinic input]
@text_signature "($self, prefix[, start[, end]], /)"
bytearray.startswith

prefix as subobj: object
A bytes or a tuple of bytes to try.
start: slice_index(accept={int, NoneType}, c_default='0') = None
Optional start position. Default: start of the bytearray.
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
Optional stop position. Default: end of the bytearray.
/

Return True if the bytearray starts with the specified prefix, False otherwise.
[clinic start generated code]*/

static PyObject *
bytearray_startswith(PyByteArrayObject *self, PyObject *args)
bytearray_startswith_impl(PyByteArrayObject *self, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=a3d9b6d44d3662a6 input=76385e0b376b45c1]*/
{
return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
subobj, start, end);
}

/*[clinic input]
@text_signature "($self, suffix[, start[, end]], /)"
bytearray.endswith

suffix as subobj: object
A bytes or a tuple of bytes to try.
start: slice_index(accept={int, NoneType}, c_default='0') = None
Optional start position. Default: start of the bytearray.
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
Optional stop position. Default: end of the bytearray.
/

Return True if the bytearray ends with the specified suffix, False otherwise.
[clinic start generated code]*/

static PyObject *
bytearray_endswith(PyByteArrayObject *self, PyObject *args)
bytearray_endswith_impl(PyByteArrayObject *self, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=e75ea8c227954caa input=9b8baa879aa3d74b]*/
{
return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self),
subobj, start, end);
}

/*[clinic input]
Expand Down Expand Up @@ -2203,8 +2239,7 @@ bytearray_methods[] = {
{"count", (PyCFunction)bytearray_count, METH_VARARGS,
_Py_count__doc__},
BYTEARRAY_DECODE_METHODDEF
{"endswith", (PyCFunction)bytearray_endswith, METH_VARARGS,
_Py_endswith__doc__},
BYTEARRAY_ENDSWITH_METHODDEF
STRINGLIB_EXPANDTABS_METHODDEF
BYTEARRAY_EXTEND_METHODDEF
{"find", (PyCFunction)bytearray_find, METH_VARARGS,
Expand Down Expand Up @@ -2249,8 +2284,7 @@ bytearray_methods[] = {
BYTEARRAY_RSTRIP_METHODDEF
BYTEARRAY_SPLIT_METHODDEF
BYTEARRAY_SPLITLINES_METHODDEF
{"startswith", (PyCFunction)bytearray_startswith, METH_VARARGS ,
_Py_startswith__doc__},
BYTEARRAY_STARTSWITH_METHODDEF
BYTEARRAY_STRIP_METHODDEF
{"swapcase", stringlib_swapcase, METH_NOARGS,
_Py_swapcase__doc__},
Expand Down
51 changes: 16 additions & 35 deletions Objects/bytes_methods.c
Original file line number Diff line number Diff line change
Expand Up @@ -771,66 +771,47 @@ tailmatch(const char *str, Py_ssize_t len, PyObject *substr,

static PyObject *
_Py_bytes_tailmatch(const char *str, Py_ssize_t len,
const char *function_name, PyObject *args,
const char *function_name, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end,
int direction)
{
Py_ssize_t start = 0;
Py_ssize_t end = PY_SSIZE_T_MAX;
PyObject *subobj = NULL;
int result;

if (!stringlib_parse_args_finds(function_name, args, &subobj, &start, &end))
return NULL;
if (PyTuple_Check(subobj)) {
Py_ssize_t i;
for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) {
result = tailmatch(str, len, PyTuple_GET_ITEM(subobj, i),
start, end, direction);
if (result == -1)
PyObject *item = PyTuple_GET_ITEM(subobj, i);
int result = tailmatch(str, len, item, start, end, direction);
if (result < 0) {
return NULL;
}
else if (result) {
Py_RETURN_TRUE;
}
}
Py_RETURN_FALSE;
}
result = tailmatch(str, len, subobj, start, end, direction);
int result = tailmatch(str, len, subobj, start, end, direction);
if (result == -1) {
if (PyErr_ExceptionMatches(PyExc_TypeError))
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Format(PyExc_TypeError,
"%s first arg must be bytes or a tuple of bytes, "
"not %s",
function_name, Py_TYPE(subobj)->tp_name);
}
return NULL;
}
else
return PyBool_FromLong(result);
return PyBool_FromLong(result);
}

PyDoc_STRVAR_shared(_Py_startswith__doc__,
"B.startswith(prefix[, start[, end]]) -> bool\n\
\n\
Return True if B starts with the specified prefix, False otherwise.\n\
With optional start, test B beginning at that position.\n\
With optional end, stop comparing B at that position.\n\
prefix can also be a tuple of bytes to try.");

PyObject *
_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *args)
_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
{
return _Py_bytes_tailmatch(str, len, "startswith", args, -1);
return _Py_bytes_tailmatch(str, len, "startswith", subobj, start, end, -1);
}

PyDoc_STRVAR_shared(_Py_endswith__doc__,
"B.endswith(suffix[, start[, end]]) -> bool\n\
\n\
Return True if B ends with the specified suffix, False otherwise.\n\
With optional start, test B beginning at that position.\n\
With optional end, stop comparing B at that position.\n\
suffix can also be a tuple of bytes to try.");

PyObject *
_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *args)
_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
{
return _Py_bytes_tailmatch(str, len, "endswith", args, +1);
return _Py_bytes_tailmatch(str, len, "endswith", subobj, start, end, +1);
}
50 changes: 42 additions & 8 deletions Objects/bytesobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2285,16 +2285,52 @@ bytes_removesuffix_impl(PyBytesObject *self, Py_buffer *suffix)
return PyBytes_FromStringAndSize(self_start, self_len);
}

/*[clinic input]
@text_signature "($self, prefix[, start[, end]], /)"
bytes.startswith

prefix as subobj: object
A bytes or a tuple of bytes to try.
start: slice_index(accept={int, NoneType}, c_default='0') = None
Optional start position. Default: start of the bytes.
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
Optional stop position. Default: end of the bytes.
/

Return True if the bytes starts with the specified prefix, False otherwise.
[clinic start generated code]*/

static PyObject *
bytes_startswith(PyBytesObject *self, PyObject *args)
bytes_startswith_impl(PyBytesObject *self, PyObject *subobj,
Py_ssize_t start, Py_ssize_t end)
/*[clinic end generated code: output=b1e8da1cbd528e8c input=8a4165df8adfa6c9]*/
{
return _Py_bytes_startswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
return _Py_bytes_startswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
subobj, start, end);
}

/*[clinic input]
@text_signature "($self, suffix[, start[, end]], /)"
bytes.endswith

suffix as subobj: object
A bytes or a tuple of bytes to try.
start: slice_index(accept={int, NoneType}, c_default='0') = None
Optional start position. Default: start of the bytes.
end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
Optional stop position. Default: end of the bytes.
/

Return True if the bytes ends with the specified suffix, False otherwise.
[clinic start generated code]*/

static PyObject *
bytes_endswith(PyBytesObject *self, PyObject *args)
bytes_endswith_impl(PyBytesObject *self, PyObject *subobj, Py_ssize_t start,
Py_ssize_t end)
/*[clinic end generated code: output=038b633111f3629d input=b5c3407a2a5c9aac]*/
{
return _Py_bytes_endswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
return _Py_bytes_endswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
subobj, start, end);
}


Expand Down Expand Up @@ -2491,8 +2527,7 @@ bytes_methods[] = {
{"count", (PyCFunction)bytes_count, METH_VARARGS,
_Py_count__doc__},
BYTES_DECODE_METHODDEF
{"endswith", (PyCFunction)bytes_endswith, METH_VARARGS,
_Py_endswith__doc__},
BYTES_ENDSWITH_METHODDEF
STRINGLIB_EXPANDTABS_METHODDEF
{"find", (PyCFunction)bytes_find, METH_VARARGS,
_Py_find__doc__},
Expand Down Expand Up @@ -2532,8 +2567,7 @@ bytes_methods[] = {
BYTES_RSTRIP_METHODDEF
BYTES_SPLIT_METHODDEF
BYTES_SPLITLINES_METHODDEF
{"startswith", (PyCFunction)bytes_startswith, METH_VARARGS,
_Py_startswith__doc__},
BYTES_STARTSWITH_METHODDEF
BYTES_STRIP_METHODDEF
{"swapcase", stringlib_swapcase, METH_NOARGS,
_Py_swapcase__doc__},
Expand Down
104 changes: 103 additions & 1 deletion Objects/clinic/bytearrayobject.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading