diff --git a/Include/internal/pycore_bytesobject.h b/Include/internal/pycore_bytesobject.h index 9f519d3ca95e92..99dd6bf1b912dd 100644 --- a/Include/internal/pycore_bytesobject.h +++ b/Include/internal/pycore_bytesobject.h @@ -147,6 +147,15 @@ PyAPI_FUNC(void*) _PyBytesWriter_WriteBytes(_PyBytesWriter *writer, PyAPI_FUNC(PyBytesWriter*) _PyBytesWriter_CreateByteArray( Py_ssize_t size); + +struct PyBytesWriter { + char small_buffer[256]; + PyObject *obj; + Py_ssize_t size; + int use_bytearray; + int overallocate; +}; + #ifdef __cplusplus } #endif diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index ab1b1722b7c254..b6d68f2372850a 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -1072,7 +1072,11 @@ def fromisocalendar(cls, year, week, day): @classmethod def strptime(cls, date_string, format): - """Parse string according to the given date format (like time.strptime()).""" + """Parse string according to the given date format (like time.strptime()). + + For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes + """ import _strptime return _strptime._strptime_datetime_date(cls, date_string, format) @@ -1109,6 +1113,8 @@ def strftime(self, format): Format using strftime(). Example: "%d/%m/%Y, %H:%M:%S" + For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes """ return _wrap_strftime(self, format, self.timetuple()) @@ -1456,8 +1462,13 @@ def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold return self @classmethod + def strptime(cls, date_string, format): - """Parse string according to the given time format (like time.strptime()).""" + """Parse string according to the given time format (like time.strptime()). + + For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes + """ import _strptime return _strptime._strptime_datetime_time(cls, date_string, format) @@ -1650,6 +1661,9 @@ def fromisoformat(cls, time_string): def strftime(self, format): """Format using strftime(). The date part of the timestamp passed to underlying strftime should not be used. + + For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes """ # The year must be >= 1000 else Python's strftime implementation # can raise a bogus exception. @@ -2198,7 +2212,11 @@ def __str__(self): @classmethod def strptime(cls, date_string, format): - """Parse string according to the given date and time format (like time.strptime()).""" + """Parse string according to the given time format (like time.strptime()). + + For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes + """ import _strptime return _strptime._strptime_datetime_datetime(cls, date_string, format) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 8f1ddf330f3940..329be68d5b8285 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -3468,12 +3468,15 @@ datetime.date.strptime / Parse string according to the given date format (like time.strptime()). + +For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes [clinic start generated code]*/ static PyObject * datetime_date_strptime_impl(PyTypeObject *type, PyObject *string, PyObject *format) -/*[clinic end generated code: output=454d473bee2d5161 input=001904ab34f594a1]*/ +/*[clinic end generated code: output=454d473bee2d5161 input=31d57bb789433e99]*/ { PyObject *result; @@ -3608,11 +3611,14 @@ datetime.date.strftime Format using strftime(). Example: "%d/%m/%Y, %H:%M:%S". + +For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes [clinic start generated code]*/ static PyObject * datetime_date_strftime_impl(PyObject *self, PyObject *format) -/*[clinic end generated code: output=6529b70095e16778 input=72af55077e606ed8]*/ +/*[clinic end generated code: output=6529b70095e16778 input=b6fd4a2ded27b557]*/ { /* This method can be inherited, and needs to call the * timetuple() method appropriate to self's class. @@ -4711,12 +4717,15 @@ datetime.time.strptime / Parse string according to the given time format (like time.strptime()). + +For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes [clinic start generated code]*/ static PyObject * datetime_time_strptime_impl(PyTypeObject *type, PyObject *string, PyObject *format) -/*[clinic end generated code: output=ae05a9bc0241d3bf input=6d0f263a5f94d78d]*/ +/*[clinic end generated code: output=ae05a9bc0241d3bf input=82ba425ecacc54aa]*/ { PyObject *result; @@ -4891,11 +4900,14 @@ datetime.time.strftime Format using strftime(). The date part of the timestamp passed to underlying strftime should not be used. + +For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes [clinic start generated code]*/ static PyObject * datetime_time_strftime_impl(PyDateTime_Time *self, PyObject *format) -/*[clinic end generated code: output=10f65af20e2a78c7 input=541934a2860f7db5]*/ +/*[clinic end generated code: output=10f65af20e2a78c7 input=c4a5bbecd798654b]*/ { PyObject *result; PyObject *tuple; @@ -5787,12 +5799,15 @@ datetime.datetime.strptime / Parse string according to the given date and time format (like time.strptime()). + +For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes [clinic start generated code]*/ static PyObject * datetime_datetime_strptime_impl(PyTypeObject *type, PyObject *string, PyObject *format) -/*[clinic end generated code: output=af2c2d024f3203f5 input=d7597c7f5327117b]*/ +/*[clinic end generated code: output=af2c2d024f3203f5 input=ef7807589f1d50e7]*/ { PyObject *result; diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 25be21111b95ee..d0fe7ad61547da 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -1026,9 +1026,6 @@ static PyObject * _io__Buffered_read1_impl(buffered *self, Py_ssize_t n) /*[clinic end generated code: output=bcc4fb4e54d103a3 input=3d0ad241aa52b36c]*/ { - Py_ssize_t have, r; - PyObject *res = NULL; - CHECK_INITIALIZED(self) if (n < 0) { n = self->buffer_size; @@ -1036,48 +1033,53 @@ _io__Buffered_read1_impl(buffered *self, Py_ssize_t n) CHECK_CLOSED(self, "read of closed file") - if (n == 0) - return PyBytes_FromStringAndSize(NULL, 0); + if (n == 0) { + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } /* Return up to n bytes. If at least one byte is buffered, we only return buffered bytes. Otherwise, we do one raw read. */ - have = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t); + Py_ssize_t have = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t); if (have > 0) { n = Py_MIN(have, n); - res = _bufferedreader_read_fast(self, n); + PyObject *res = _bufferedreader_read_fast(self, n); assert(res != Py_None); return res; } - res = PyBytes_FromStringAndSize(NULL, n); - if (res == NULL) - return NULL; + if (!ENTER_BUFFERED(self)) { - Py_DECREF(res); return NULL; } + /* Flush the write buffer if necessary */ if (self->writable) { - PyObject *r = buffered_flush_and_rewind_unlocked(self); - if (r == NULL) { + PyObject *res = buffered_flush_and_rewind_unlocked(self); + if (res == NULL) { LEAVE_BUFFERED(self) - Py_DECREF(res); return NULL; } - Py_DECREF(r); + Py_DECREF(res); } _bufferedreader_reset_buf(self); - r = _bufferedreader_raw_read(self, PyBytes_AS_STRING(res), n); + + PyBytesWriter *writer = PyBytesWriter_Create(n); + if (writer == NULL) { + return NULL; + } + + Py_ssize_t r = _bufferedreader_raw_read(self, + PyBytesWriter_GetData(writer), n); LEAVE_BUFFERED(self) if (r == -1) { - Py_DECREF(res); + PyBytesWriter_Discard(writer); return NULL; } - if (r == -2) + if (r == -2) { r = 0; - if (n > r) - _PyBytes_Resize(&res, r); - return res; + } + + return PyBytesWriter_FinishWithSize(writer, r); } static PyObject * diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index c0b6c6425184af..9992d48a1d8fc7 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -739,7 +739,7 @@ _io_FileIO_readall_impl(fileio *self) /*[clinic end generated code: output=faa0292b213b4022 input=10d8b2ec403302dc]*/ { Py_off_t pos, end; - PyObject *result; + PyBytesWriter *writer; Py_ssize_t bytes_read = 0; Py_ssize_t n; size_t bufsize; @@ -794,10 +794,10 @@ _io_FileIO_readall_impl(fileio *self) } } - - result = PyBytes_FromStringAndSize(NULL, bufsize); - if (result == NULL) + writer = PyBytesWriter_Create(bufsize); + if (writer == NULL) { return NULL; + } while (1) { if (bytes_read >= (Py_ssize_t)bufsize) { @@ -806,18 +806,18 @@ _io_FileIO_readall_impl(fileio *self) PyErr_SetString(PyExc_OverflowError, "unbounded read returned more bytes " "than a Python bytes object can hold"); - Py_DECREF(result); + PyBytesWriter_Discard(writer); return NULL; } - if (PyBytes_GET_SIZE(result) < (Py_ssize_t)bufsize) { - if (_PyBytes_Resize(&result, bufsize) < 0) + if (PyBytesWriter_GetSize(writer) < (Py_ssize_t)bufsize) { + if (PyBytesWriter_Resize(writer, bufsize) < 0) return NULL; } } n = _Py_read(self->fd, - PyBytes_AS_STRING(result) + bytes_read, + (char*)PyBytesWriter_GetData(writer) + bytes_read, bufsize - bytes_read); if (n == 0) @@ -827,20 +827,16 @@ _io_FileIO_readall_impl(fileio *self) PyErr_Clear(); if (bytes_read > 0) break; - Py_DECREF(result); + PyBytesWriter_Discard(writer); Py_RETURN_NONE; } - Py_DECREF(result); + PyBytesWriter_Discard(writer); return NULL; } bytes_read += n; } - if (PyBytes_GET_SIZE(result) > bytes_read) { - if (_PyBytes_Resize(&result, bytes_read) < 0) - return NULL; - } - return result; + return PyBytesWriter_FinishWithSize(writer, bytes_read); } /*[clinic input] @@ -866,10 +862,6 @@ static PyObject * _io_FileIO_read_impl(fileio *self, PyTypeObject *cls, Py_ssize_t size) /*[clinic end generated code: output=bbd749c7c224143e input=752d1ad3db8564a5]*/ { - char *ptr; - Py_ssize_t n; - PyObject *bytes; - if (self->fd < 0) return err_closed(); if (!self->readable) { @@ -884,16 +876,17 @@ _io_FileIO_read_impl(fileio *self, PyTypeObject *cls, Py_ssize_t size) size = _PY_READ_MAX; } - bytes = PyBytes_FromStringAndSize(NULL, size); - if (bytes == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (writer == NULL) { return NULL; - ptr = PyBytes_AS_STRING(bytes); + } + char *ptr = PyBytesWriter_GetData(writer); - n = _Py_read(self->fd, ptr, size); + Py_ssize_t n = _Py_read(self->fd, ptr, size); if (n == -1) { - /* copy errno because Py_DECREF() can indirectly modify it */ + // copy errno because PyBytesWriter_Discard() can indirectly modify it int err = errno; - Py_DECREF(bytes); + PyBytesWriter_Discard(writer); if (err == EAGAIN) { PyErr_Clear(); Py_RETURN_NONE; @@ -901,14 +894,7 @@ _io_FileIO_read_impl(fileio *self, PyTypeObject *cls, Py_ssize_t size) return NULL; } - if (n != size) { - if (_PyBytes_Resize(&bytes, n) < 0) { - Py_CLEAR(bytes); - return NULL; - } - } - - return (PyObject *) bytes; + return PyBytesWriter_FinishWithSize(writer, n); } /*[clinic input] diff --git a/Modules/_winapi.c b/Modules/_winapi.c index b4cfbebcb1bb5e..2aebe44c70921d 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -1920,7 +1920,6 @@ static PyObject * _winapi_PeekNamedPipe_impl(PyObject *module, HANDLE handle, int size) /*[clinic end generated code: output=d0c3e29e49d323dd input=c7aa53bfbce69d70]*/ { - PyObject *buf = NULL; DWORD nread, navail, nleft; BOOL ret; @@ -1930,20 +1929,26 @@ _winapi_PeekNamedPipe_impl(PyObject *module, HANDLE handle, int size) } if (size) { - buf = PyBytes_FromStringAndSize(NULL, size); - if (!buf) + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (writer == NULL) { return NULL; + } + char *buf = PyBytesWriter_GetData(writer); + Py_BEGIN_ALLOW_THREADS - ret = PeekNamedPipe(handle, PyBytes_AS_STRING(buf), size, &nread, + ret = PeekNamedPipe(handle, buf, size, &nread, &navail, &nleft); Py_END_ALLOW_THREADS if (!ret) { - Py_DECREF(buf); + PyBytesWriter_Discard(writer); return PyErr_SetExcFromWindowsErr(PyExc_OSError, 0); } - if (_PyBytes_Resize(&buf, nread)) + + PyObject *res = PyBytesWriter_FinishWithSize(writer, nread); + if (res == NULL) { return NULL; - return Py_BuildValue("NII", buf, navail, nleft); + } + return Py_BuildValue("NII", res, navail, nleft); } else { Py_BEGIN_ALLOW_THREADS diff --git a/Modules/clinic/_datetimemodule.c.h b/Modules/clinic/_datetimemodule.c.h index 7c4bd5503ed56b..ee621c150c31e4 100644 --- a/Modules/clinic/_datetimemodule.c.h +++ b/Modules/clinic/_datetimemodule.c.h @@ -371,7 +371,10 @@ PyDoc_STRVAR(datetime_date_strptime__doc__, "strptime($type, string, format, /)\n" "--\n" "\n" -"Parse string according to the given date format (like time.strptime())."); +"Parse string according to the given date format (like time.strptime()).\n" +"\n" +"For a list of supported format codes, see the documentation:\n" +" https://docs.python.org/3/library/datetime.html#format-codes"); #define DATETIME_DATE_STRPTIME_METHODDEF \ {"strptime", _PyCFunction_CAST(datetime_date_strptime), METH_FASTCALL|METH_CLASS, datetime_date_strptime__doc__}, @@ -412,7 +415,10 @@ PyDoc_STRVAR(datetime_date_strftime__doc__, "\n" "Format using strftime().\n" "\n" -"Example: \"%d/%m/%Y, %H:%M:%S\"."); +"Example: \"%d/%m/%Y, %H:%M:%S\".\n" +"\n" +"For a list of supported format codes, see the documentation:\n" +" https://docs.python.org/3/library/datetime.html#format-codes"); #define DATETIME_DATE_STRFTIME_METHODDEF \ {"strftime", _PyCFunction_CAST(datetime_date_strftime), METH_FASTCALL|METH_KEYWORDS, datetime_date_strftime__doc__}, @@ -847,7 +853,10 @@ PyDoc_STRVAR(datetime_time_strptime__doc__, "strptime($type, string, format, /)\n" "--\n" "\n" -"Parse string according to the given time format (like time.strptime())."); +"Parse string according to the given time format (like time.strptime()).\n" +"\n" +"For a list of supported format codes, see the documentation:\n" +" https://docs.python.org/3/library/datetime.html#format-codes"); #define DATETIME_TIME_STRPTIME_METHODDEF \ {"strptime", _PyCFunction_CAST(datetime_time_strptime), METH_FASTCALL|METH_CLASS, datetime_time_strptime__doc__}, @@ -970,7 +979,10 @@ PyDoc_STRVAR(datetime_time_strftime__doc__, "\n" "Format using strftime().\n" "\n" -"The date part of the timestamp passed to underlying strftime should not be used."); +"The date part of the timestamp passed to underlying strftime should not be used.\n" +"\n" +"For a list of supported format codes, see the documentation:\n" +" https://docs.python.org/3/library/datetime.html#format-codes"); #define DATETIME_TIME_STRFTIME_METHODDEF \ {"strftime", _PyCFunction_CAST(datetime_time_strftime), METH_FASTCALL|METH_KEYWORDS, datetime_time_strftime__doc__}, @@ -1569,7 +1581,10 @@ PyDoc_STRVAR(datetime_datetime_strptime__doc__, "strptime($type, string, format, /)\n" "--\n" "\n" -"Parse string according to the given date and time format (like time.strptime())."); +"Parse string according to the given date and time format (like time.strptime()).\n" +"\n" +"For a list of supported format codes, see the documentation:\n" +" https://docs.python.org/3/library/datetime.html#format-codes"); #define DATETIME_DATETIME_STRPTIME_METHODDEF \ {"strptime", _PyCFunction_CAST(datetime_datetime_strptime), METH_FASTCALL|METH_CLASS, datetime_datetime_strptime__doc__}, @@ -2075,4 +2090,4 @@ datetime_datetime___reduce__(PyObject *self, PyObject *Py_UNUSED(ignored)) { return datetime_datetime___reduce___impl((PyDateTime_DateTime *)self); } -/*[clinic end generated code: output=0b8403bc58982e60 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=69658acff6a43ac4 input=a9049054013a1b77]*/ diff --git a/Modules/sha3module.c b/Modules/sha3module.c index 47fe5e57e6ddd9..14c543b86415d5 100644 --- a/Modules/sha3module.c +++ b/Modules/sha3module.c @@ -509,14 +509,19 @@ _sha3_shake_128_digest_impl(SHA3object *self, Py_ssize_t length) if (length == 0) { return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } - CHECK_HACL_UINT32_T_LENGTH(length); - PyObject *digest = PyBytes_FromStringAndSize(NULL, length); - uint8_t *buffer = (uint8_t *)PyBytes_AS_STRING(digest); + + PyBytesWriter *writer = PyBytesWriter_Create(length); + if (writer == NULL) { + return NULL; + } + uint8_t *buffer = (uint8_t *)PyBytesWriter_GetData(writer); + HASHLIB_ACQUIRE_LOCK(self); (void)Hacl_Hash_SHA3_squeeze(self->hash_state, buffer, (uint32_t)length); HASHLIB_RELEASE_LOCK(self); - return digest; + + return PyBytesWriter_Finish(writer); } @@ -540,8 +545,8 @@ _sha3_shake_128_hexdigest_impl(SHA3object *self, Py_ssize_t length) if (length == 0) { return Py_GetConstant(Py_CONSTANT_EMPTY_STR); } - CHECK_HACL_UINT32_T_LENGTH(length); + uint8_t *buffer = PyMem_Malloc(length); if (buffer == NULL) { return PyErr_NoMemory(); @@ -550,6 +555,7 @@ _sha3_shake_128_hexdigest_impl(SHA3object *self, Py_ssize_t length) HASHLIB_ACQUIRE_LOCK(self); (void)Hacl_Hash_SHA3_squeeze(self->hash_state, buffer, (uint32_t)length); HASHLIB_RELEASE_LOCK(self); + PyObject *digest = _Py_strhex((const char *)buffer, length); PyMem_Free(buffer); return digest; diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index ee10f13b7bb04c..9bb1f84190a93f 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -836,6 +836,11 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, if (v == NULL) goto error; + if (fmtcnt == 0) { + /* last write: disable writer overallocation */ + writer->overallocate = 0; + } + sign = 0; fill = ' '; switch (c) { @@ -1056,6 +1061,10 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, assert((res - before) == alloc); #endif } /* '%' */ + + /* If overallocation was disabled, ensure that it was the last + write. Otherwise, we missed an optimization */ + assert(writer->overallocate || fmtcnt == 0 || use_bytearray); } /* until end */ if (argidx < arglen && !dict) { @@ -3746,14 +3755,6 @@ _PyBytes_Repeat(char* dest, Py_ssize_t len_dest, // --- PyBytesWriter API ----------------------------------------------------- -struct PyBytesWriter { - char small_buffer[256]; - PyObject *obj; - Py_ssize_t size; - int use_bytearray; -}; - - static inline char* byteswriter_data(PyBytesWriter *writer) { @@ -3802,7 +3803,8 @@ byteswriter_resize(PyBytesWriter *writer, Py_ssize_t size, int overallocate) return 0; } - if (overallocate && !writer->use_bytearray) { + overallocate &= writer->overallocate; + if (overallocate) { if (size <= (PY_SSIZE_T_MAX - size / OVERALLOCATE_FACTOR)) { size += size / OVERALLOCATE_FACTOR; } @@ -3867,6 +3869,7 @@ byteswriter_create(Py_ssize_t size, int use_bytearray) writer->obj = NULL; writer->size = 0; writer->use_bytearray = use_bytearray; + writer->overallocate = !use_bytearray; if (size >= 1) { if (byteswriter_resize(writer, size, 0) < 0) {