503 Commits
Author SHA1 Message Date
Jelle ZijlstraandJelle Zijlstra 95f94d04d8 gh-159739: Reject the minimum dynamic formatting width (#159780)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
2026-10-20 10:20:20 -07:00
Cody MaloneyandClaude Opus 5.5 6576fb46db gh-87613: Use AC vectorcall for bytes and bytearray (#158891)
* gh-87613: Use AC vectorcall for bytes and bytearray

Speeds up common construction of both objects. Enables optimizing
construction off uniquely referenced temporary objects.

Previously the argument tuples / dictionaries required by the call
protocol resulted in arguments never being unique temporaries. With
vectorcall they can be. Avoid copying by adopting the existing
allocation when possible.

Benchmark
Run on my linux dev machine: PGO+LTO, clang 23, --with-tail-call-interp

(note: AI Written, but I think sufficiently representative)
```python
"""bytes() and bytearray() construction.

temp/ passes a unique temporary (adopted, no copy); named/ binds the same
source to a name first so it is always copied.
"""
import pyperf

runner = pyperf.Runner()

runner.timeit('call/bytes()', 'bytes()')
runner.timeit('call/bytes(int)', 'bytes(16)')
runner.timeit('call/bytes(bytes)', "bytes(b'abcdefghabcdefgh')")
runner.timeit("call/bytes(str, 'ascii')", "bytes('abcdefgh', 'ascii')")
runner.timeit('call/bytearray()', 'bytearray()')
runner.timeit('call/bytearray(int)', 'bytearray(16)')
runner.timeit('call/bytearray(bytes)', "bytearray(b'abcdefghabcdefgh')")
runner.timeit("call/bytearray(str, 'ascii')",
              "bytearray('abcdefgh', 'ascii')")

for size_name, size in (('16B', 16), ('4KiB', 4096), ('1MiB', 1 << 20)):
    # Slicing in the statement makes a fresh unique temporary.
    setup = f"b = b'x' * {size}; ba = bytearray(b)"
    runner.timeit(f'temp/bytes(bytearray) {size_name}',
                  'bytes(ba[1:])', setup)
    runner.timeit(f'named/bytes(bytearray) {size_name}',
                  'x = ba[1:]; bytes(x); del x', setup)
    runner.timeit(f'temp/bytearray(bytes) {size_name}',
                  'bytearray(b[1:])', setup)
    runner.timeit(f'named/bytearray(bytes) {size_name}',
                  'x = b[1:]; bytearray(x); del x', setup)
    runner.timeit(f'temp/bytearray(bytearray) {size_name}',
                  'bytearray(ba[1:])', setup)
    runner.timeit(f'named/bytearray(bytearray) {size_name}',
                  'x = ba[1:]; bytearray(x); del x', setup)
```

| Benchmark                       | main    | patch                  |
|---------------------------------|:-------:|:----------------------:|
| temp/bytearray(bytearray) 1MiB  | 515 us  | 14.9 us: 34.53x faster |
| temp/bytearray(bytes) 1MiB      | 508 us  | 14.8 us: 34.27x faster |
| temp/bytes(bytearray) 1MiB      | 507 us  | 14.9 us: 34.05x faster |
| temp/bytearray(bytes) 4KiB      | 144 ns  | 79.5 ns: 1.81x faster  |
| temp/bytearray(bytearray) 4KiB  | 155 ns  | 94.7 ns: 1.63x faster  |
| temp/bytearray(bytes) 16B       | 56.4 ns | 36.1 ns: 1.56x faster  |
| temp/bytes(bytearray) 4KiB      | 150 ns  | 96.4 ns: 1.55x faster  |
| temp/bytearray(bytearray) 16B   | 64.1 ns | 47.3 ns: 1.35x faster  |
| call/bytes()                    | 14.7 ns | 11.1 ns: 1.32x faster  |
| call/bytes(int)                 | 31.5 ns | 24.3 ns: 1.29x faster  |
| call/bytes(str, 'ascii')        | 36.9 ns | 28.7 ns: 1.28x faster  |
| call/bytes(bytes)               | 34.3 ns | 27.3 ns: 1.26x faster  |
| temp/bytes(bytearray) 16B       | 59.4 ns | 49.5 ns: 1.20x faster  |
| call/bytearray(str, 'ascii')    | 45.6 ns | 39.1 ns: 1.17x faster  |
| call/bytearray(bytes)           | 44.3 ns | 38.9 ns: 1.14x faster  |
| call/bytearray(int)             | 41.7 ns | 37.3 ns: 1.12x faster  |
| named/bytearray(bytes) 16B      | 59.6 ns | 54.4 ns: 1.10x faster  |
| named/bytes(bytearray) 16B      | 61.3 ns | 56.2 ns: 1.09x faster  |
| named/bytearray(bytearray) 16B  | 65.3 ns | 60.9 ns: 1.07x faster  |
| named/bytes(bytearray) 4KiB     | 154 ns  | 148 ns: 1.04x faster   |
| call/bytearray()                | 22.1 ns | 21.4 ns: 1.03x faster  |
| named/bytearray(bytearray) 4KiB | 156 ns  | 151 ns: 1.03x faster   |
| named/bytearray(bytes) 4KiB     | 146 ns  | 143 ns: 1.02x faster   |
| Geometric mean                  | (ref)   | 1.77x faster           |

Benchmark hidden because not significant (3): named/bytes(bytearray) 1MiB,
named/bytearray(bytes) 1MiB, named/bytearray(bytearray) 1MiB

* Remove unique temporary adoption optimizations

PyUnstable_Object_IsUniqueReferencedTemporary() can't tell when a C
caller still holds a borrowed reference to the argument (for example
bisect.insort(lst, bytearray(b'abc')[1:], key=bytes)), so adopting the
buffer empties an object still in use. Leave the optimization for a
follow-up and keep this PR to the vectorcall change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 22:39:11 +02:00
Michael J. Sullivan 3646ea571f gh-159430: Add _PyObject_LookupSpecial error checks in bytes/weakref (GH-159432)
_PyObject_LookupSpecial needs a PyErr_Occurred() check to handle
failures while during the lookup, or else it can fail with a "returned
a result with an exception set" error.

Add some missing ones in bytes and weakref/
2026-10-15 17:14:02 +02:00
Victor Stinner 182f323154 gh-158585: Adjust _PyBytes_FormatEx() allocation (#158823)
Count properly the number of preallocated bytes when growing the
buffer.
2026-10-05 03:09:25 +00:00
Victor Stinner 30ce3d462d gh-158585: Simplify PyBytesWriter_Finish() (#158822)
PyBytesWriter_Finish() doesn't need to check the size. It's known to
be valid.

PyBytesWriter_Create() now only calls byteswriter_resize() if the
size is larger than the small buffer.

Remove byteswriter_data() function: call static inline
_PyBytesWriter_GetData() function directly.
2026-10-05 04:47:04 +02:00
Victor Stinner 8dba039708 gh-158585: Optimize bytes object creation (#158664)
Add bytes_alloc() helper function. bytes_alloc() and
_PyBytes_FromStringAndSize() have less conditional branches than
previous code, and so should be a little bit faster.

* Rename existing bytes_alloc() to bytes_type_alloc().
* Replace _PyBytes_FromSize(calloc=1) with _PyBytes_FromSizeZero().
* Add set_ob_shash_unsafe(): similar to set_ob_shash() but don't use
  an atomic operation on Free Threading. Use this new function
  on newly allocated bytes objects and in bytes_resize_inplace().
* Replace PyBytes_FromStringAndSize(NULL, size) with
  bytes_alloc(size).
* Use bytes_alloc() in PyBytes_FromString() and _PyBytes_Repeat().
* Add _PyBytes_FromStringAndSize(): similar to
  PyBytes_FromStringAndSize(), but str must not be NULL.
* Replace PyBytes_FromStringAndSize() with
  _PyBytes_FromStringAndSize().
* Test that PyBytes_FromString() and PyBytes_FromStringAndSize()
  return singletons for 0 or 1 bytes.
* Fix test_tracemalloc.test_set_traceback_limit().
2026-10-04 21:01:31 +00:00
Victor Stinner 9d22a5334b gh-158583: Fix uninitialized memory read in bytes.fromhex() (#158584) 2026-10-03 23:51:37 +00:00
Victor Stinner 6e7738bb5e gh-158585: Fix _PyBytes_Resize() if hash value is already computed (#158589)
_PyBytes_Resize() now creates a new bytes object if the hash value
was already computed.

bytes_resize_inplace() no longer sets the hash value to -1, since
this function must not be called if the hash value was already
computed.

Add tests on PyUnicode_Resize().
2026-10-03 23:35:46 +02:00
Victor Stinner 1a85213940 gh-158585: Adjust allocation in bytes_fromformat() (#158621)
Take in account preallocated bytes when calling
PyBytesWriter_GrowAndUpdatePointer().

Use sprintf() return value instead of calling strlen().
2026-10-03 00:34:38 +02:00
Victor Stinner ee782e6143 gh-158585: Optimize PyBytesWriter_FinishWithSize() (#158586)
Add specialized bytes_resize_inplace() and inline
PyBytesWriter_Discard() in PyBytesWriter_FinishWithSize().

No longer copy the buffer if _PyObject_IsUniquelyReferenced() is false.

Add also bytes_get_char() function.
2026-10-02 03:06:58 +00:00
Victor StinnerandClaude Opus 4.6 24ea85a4c2 gh-156939: Fix two bugs in PyBytesWriter (#158404)
Fix PyBytesWriter_Format() error handling: restore the writer to its
previous state on error. Add an unit test injecting MemoryError.

Fix PyBytesWriter_FinishWithSize() to return an empty bytearray when
use_bytearray is set and size is 0, instead of always returning an
empty bytes object.

Fix test_capi.test_bytes: always check writer finish result type, to
make sure that _PyBytesWriter_CreateByteArray() always create
bytearray objects.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-29 14:50:06 +02:00
Victor StinnerandVyron Vasileiadis b4be3a5804 gh-158334: Fix bytes.translate() when the input is returned unchanged (#158344)
Do no read output_start pointer after PyBytesWriter_Finish() since
the pointer became a dangling pointer.

Co-authored-by: Vyron Vasileiadis <hi@fedonman.com>
2026-09-29 12:17:17 +02:00
Victor StinnerandCody Maloney 969af80daf gh-158219: Fix bytearray constructor to not use hashed object (#158329)
* Add _PyBytes_GET_CACHED_HASH() static inline function.
* _PyBytes_IsMutable() makes sure that the hash value is not cached
  yet.
* bytearray_reinit_from_bytes() checks that the bytes object is
  mutable.

Co-authored-by: Cody Maloney <cmaloney@theoreticalchaos.com>
2026-09-29 11:31:54 +02:00
Victor Stinner a64b7ecba4 gh-157710: Avoid resize in PyUnicodeWriter_Finish() for singleton (#157862)
PyBytesWriter_FinishWithSize() and PyUnicodeWriter_Finish() now
discard the output string instead of resizing it if the single
byte/character singleton is used.
2026-09-20 16:25:20 +02:00
Victor Stinner e2ff498cc0 gh-157242: Add byteswriter_check_consistency() (#157723)
Add byteswriter_check_consistency() to check that the bytes/bytearray
object can be mutated. It is called by all PyBytesWriter functions.

* Add test_thread to PyBytesWriter tests.
* _PyBytes_IsMutable() now checks _PyObject_IsUniquelyReferenced().
* Clarify thread safety in PyBytesWriter documentation.
2026-09-18 02:43:38 +00:00
Victor Stinner 3b1057d4b5 gh-156939: Clear newly allocated bytes in PyBytesWriter_Resize() (#157455)
Adjust the logic to set newly allocated bytes to a known byte pattern
(PyBytesWrite_NEW_BYTE). Only copy 'size' bytes from the small buffer
to the new bytes/bytearray object.
2026-09-15 15:10:07 +00:00
Victor Stinner 2d1007f931 gh-156939: Detect buffer overflow in bytes and bytearray (#157529)
When Python is built in debug mode, bytes an bytearray destructors
now check if the trailing null byte has been overridden to detect
overflow. Add bytes_dealloc() to implement the check.

Add _PyBytes_CheckOverflow() to share code
2026-09-15 16:34:17 +02:00
Victor Stinner 58cdff72de gh-156939: Detect PyBytesWriter buffer overflow earlier (#157385)
Check the canary byte in all PyBytesWriter methods, not only in
PyBytesWriter_Finish().

Add a discard() method to the _testcapi wrapper.
2026-09-13 05:47:24 +02:00
Victor Stinner 12a1de1a4e gh-156939: Detect buffer overflow in PyBytesWriter in debug mode (#156943)
Reserve one byte in PyBytesWriter used as a canary byte: set it to a
special value. PyBytesWriter_Finish() checks if the canary byte has
been overriden to detect a buffer overflow. Add tests for this feature.

Update buffer overflow check in fcntl: allocate extra guard bytes in
the writer and then truncate these bytes.
2026-09-13 00:28:39 +00:00
Victor Stinner 121e27c484 gh-157242: Add _PyBytes_IsMutable() assertion (#157371)
Elaborate on mutability in _PyBytes_Resize() and PyBytesWriter
documentation.

Check _PyBytes_IsMutable() in functions which require a mutable bytes
object like PyBytesWriter_Resize().
2026-09-12 20:22:01 +00:00
Victor Stinner bab97c599f gh-157242: Allow negative grow in PyBytesWriter_Grow() (#157375)
PyBytesWriter_Resize() doesn't need to call byteswriter_resize() if
the buffer is already large enough.
2026-09-12 20:12:45 +00:00
Victor Stinner 575fe3914f gh-155742: Use PyBytesWriter in bytes.translate() (#157346)
Replace soft deprecated _PyBytes_Resize() with PyBytesWriter.
Enhance test_bytes.test_translate().
2026-09-12 17:49:29 +00:00
Victor Stinner 63d4398be8 gh-157242: Add more PyBytesWriter tests (#157352)
* Fix PyBytesWriter_Grow() check on the size argument.
* Fix undefined behavior in _PyBytes_ResizeKeepOnError(): raise OverflowError
  on integer overflow.
* Add write(), grow() and get_data() methods to the _testcapi wrapper of
  PyBytesWriter.
* Remove the string argument from _testcapi.PyBytesWriter() and
  resize() method.
* Add tests on PyBytesWriter_Resize() invalid sizes.
2026-09-12 05:30:56 +00:00
47029802a6 gh-128213: fast path for bytes creation from list and tuple (#132590)
Co-authored-by: Ben Hsing <blhsing@gmail.com>
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
2026-09-12 09:09:44 +05:30
Victor StinnerandMaurycy Pawłowski-Wieroński f5dd52df16 gh-157242: Fix PyBytesWriter_Resize() on MemoryError (#157243)
If PyBytesWriter_Resize() fails, leave the writer unchanged.

Add a new internal _PyBytes_ResizeKeepOnError() function similar to
_PyBytes_Resize() but leaves the bytes object unchanged on error.

Co-authored-by: Maurycy Pawłowski-Wieroński <maurycy@maurycy.com>
2026-09-11 23:22:35 +00:00
fd569ea941 gh-156995: Fix bytearray.take_bytes() corrupting shared single-byte singletons (#156996)
Co-authored-by: Cody Maloney <cmaloney@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
2026-09-10 11:10:18 +02:00
Victor Stinner 4e530b82f3 gh-129813: Check size in PyBytesWriter_FinishWithSize() (#157226)
Reject size larger than the allocated size.

Check also negative size in PyBytesWriter_FinishWithSize() to always
raise ValueError. Previously, the function raised SystemError or
ValueError depending on the code path.

Replace _PyBytesWriter_GetAllocated() optimization with
_PyBytesWriter_ResizeToAllocated() to update the writer size to its
allocated size.

test_capi: only run the 3 "example" tests in BytesWriterTest. There
is no need to rerun the same tests in ByteArrayWriterTest.
2026-09-09 20:59:57 +02:00
Victor Stinner 5141621d44 gh-129813: Fix typo in byteswriter_resize() (#156938)
The intent is to test "resize and overallocate", even if in practice
"resize & overallocate" is the same (both variables are either 0 or
1). Use "&&" to better describe the intent of the test.
2026-09-05 13:22:55 +02:00
Victor Stinner 5ef7fa169f gh-155742: Get singleton in PyBytesWriter_FinishWithSize() (#155795)
If the result size of 1 byte, return the singleton rather than
creating a new bytes string.
2026-08-31 19:20:11 +02:00
Steve StaggandCody Maloney d5c1b29658 gh-153419: Fix several issues around bytearray __init__ (#153498)
Introduce a bytearray_new() function to ensure that
ob_bytes_object is always set on a bytearray.

Resizing a bytearray to 0 length now explicitly sets
the ob_bytes_object to the empty constant immortal.

Add a check in the 'bytearray init from string'
fast path to ensure there are no active exports.

This fixes asserts/crashes on the following:
 - bytearray(1).__init__()
 - bytearray().__new__(bytearray).append(1)
 - a = bytearray(); b = memoryview(a); a.__init__('x', 'ascii')

Co-authored-by: Cody Maloney <cmaloney@users.noreply.github.com>
2026-07-24 15:25:11 +00:00
Serhiy Storchaka e1e06be119 gh-150285: Fix too long docstrings in builtins (GH-150293)
* gh-150285: Fix too long docstrings in builtins

* Revert bytes and bytearray class multiline docstrings to make IDLE happy.
2026-05-24 15:03:32 +03:00
sobolevn 46afba7b93 gh-149816: Fix a race condition in _PyBytes_FromList with free-threading (#149909) 2026-05-16 08:37:34 +00:00
Neko Asakura d2d24e46d3 gh-100239: expose sq_repeat helpers for BINARY_OP_EXTEND (#148791) 2026-05-08 11:12:20 +00:00
Mark Shannon 600f4dbd54 GH-145668: Add FOR_ITER specialization for virtual iterators. Specialize GET_ITER. (GH-147967)
* Add FOR_ITER_VIRTUAL to specialize FOR_ITER for virtual iterators
* Add GET_ITER_SELF to specialize GET_ITER for iterators (including generators)
* Add GET_ITER_VIRTUAL to specialize GET_ITER for iterables as virtual iterators
* Add new (internal) _tp_iteritem function slot to PyTypeObject
* Put limited RESUME at start of genexpr for free-threading. Fix up exception handling in genexpr
2026-04-16 15:22:22 +01:00
Pieter Eendebak 1f6a09fb36 gh-100239: Specialize more binary operations using BINARY_OP_EXTEND (GH-128956) 2026-04-16 09:22:41 +01:00
Serhiy Storchaka 473d2a35ce gh-147944: Increase range of bytes_per_sep (GH-147946)
Accepted range for the bytes_per_sep argument of bytes.hex(),
bytearray.hex(), memoryview.hex(), and binascii.b2a_hex()
is now increased, so passing sys.maxsize and -sys.maxsize is now
valid.
2026-04-01 08:33:30 +00:00
Stan Ulbrych 62a6e898e0 gh-147856: Allow the 'count' argument of bytes.replace() to be a keyword (#147943) 2026-03-31 19:27:52 +02:00
Serhiy Storchaka 2520617361 gh-142037: Fix a refleak introduced in GH-142081 (GH-144256) 2026-01-26 21:15:21 +00:00
Serhiy Storchaka 012c498035 gh-142037: Improve error messages for printf-style formatting (GH-142081)
This affects string formatting as well as bytes and bytearray formatting.

* For errors in the format string, always include the position of the
  start of the format unit.
* For errors related to the formatted arguments, always include the number
  or the name of the formatted argument.
* Suggest more probable causes of errors in the format string (stray %,
  unsupported format, unexpected character).
* Provide more information when the number of arguments does not match
  the number of format units.
* Raise more specific errors when access of arguments by name is mixed with
  sequential access and when * is used with a mapping.
* Add tests for some uncovered cases.
2026-01-24 11:13:50 +00:00
Gregory P. Smith a966d94e76 gh-144157: Optimize bytes.translate() by deferring change detection (GH-144158)
Optimize bytes.translate() by deferring change detection

Move the equality check out of the hot loop to allow better compiler
optimization. Instead of checking each byte during translation, perform
a single memcmp at the end to determine if the input can be returned
unchanged.

This allows compilers to unroll and pipeline the loops, resulting in ~2x
throughput improvement for medium-to-large inputs (tested on an AMD zen2).
No change observed on small inputs.

It will also be faster for bytes subclasses as those do not need change
detection.
2026-01-22 09:21:07 -08:00
732224e113 gh-139871: Add bytearray.take_bytes([n]) to efficiently extract bytes (GH-140128)
Update `bytearray` to contain a `bytes` and provide a zero-copy path to
"extract" the `bytes`. This allows making several code paths more efficient.

This does not move any codepaths to make use of this new API. The documentation
changes include common code patterns which can be made more efficient with
this API.

---

When just changing `bytearray` to contain `bytes` I ran pyperformance on a
`--with-lto --enable-optimizations --with-static-libpython` build and don't see
any major speedups or slowdowns with this; all seems to be in the noise of
my machine (Generally changes under 5% or benchmarks that don't touch
bytes/bytearray).


Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Maurycy Pawłowski-Wieroński <5383+maurycy@users.noreply.github.com>
2025-11-13 13:19:44 +00:00
Stan Ulbrych d6c89a2df2 gh-140939: Fix memory leak in _PyBytes_FormatEx error path (#140957) 2025-11-06 11:20:57 +05:30
Sergey Miryanov 32c264982e gh-140061: Use _PyObject_IsUniquelyReferenced() to check if objects are uniquely referenced (gh-140062)
The previous `Py_REFCNT(x) == 1` checks can have data races in the free
threaded build. `_PyObject_IsUniquelyReferenced(x)` is a more conservative
check that is safe in the free threaded build and is identical to
`Py_REFCNT(x) == 1` in the default GIL-enabled build.
2025-10-15 09:48:21 -04:00
Victor Stinner c9a79a02a8 gh-139156: Use PyBytesWriter in _PyUnicode_EncodeCharmap() (#139251)
Replace PyBytes_FromStringAndSize() and _PyBytes_Resize() with the
PyBytesWriter API.

Add _PyBytesWriter_GetSize() and _PyBytesWriter_GetData() static
inline functions.
2025-09-24 16:15:34 +02:00
Victor Stinner dd45179fa0 gh-129813, PEP 782: Remove the private _PyBytesWriter API (#139264)
It is now replaced with the new public PyBytesWriter API (PEP 782).
2025-09-23 15:29:55 +00:00
Victor Stinner 9f7bbafffe gh-129813, PEP 782: Optimize byteswriter_resize() (#139101)
There is no need to copy the small_buffer in PyBytesWriter_Create().
2025-09-18 09:52:16 +00:00
Victor Stinner c72ffe71f1 gh-129813, PEP 782: Set invalid bytes in PyBytesWriter (#139054)
Initialize the buffer with 0xFF byte pattern when creating a writer
object, but also when resizing/growing the writer.
2025-09-17 17:58:32 +02:00
Serhiy Storchaka a1cf6e92b6 gh-71679: Share the repr implementation between bytes and bytearray (GH-138181)
This allows to use the smart quotes algorithm in the bytearray's repr.
2025-09-17 11:10:29 +03:00
Victor Stinner 4e00e2504f gh-129813, PEP 782: Add PyBytesWriter.overallocate (#138941)
Disable overallocation in _PyBytes_FormatEx() at the last write.
2025-09-16 00:15:32 +02:00
Victor Stinner 7c6efc3a4f gh-129813, PEP 782: Init small_buffer in PyBytesWriter_Create() (#138924)
Fill small_buffer with 0xFF byte pattern to detect the usage of
uninitialized bytes in debug build.
2025-09-15 16:23:11 +02:00