Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Doc/library/base64.rst
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,20 @@ Refer to the documentation of the individual functions for more information.
.. versionadded:: 3.4


.. function:: z85encode(s)
.. function:: z85encode(s, pad=False)

Encode the :term:`bytes-like object` *s* using Z85 (as used in ZeroMQ)
and return the encoded :class:`bytes`. See `Z85 specification
<https://rfc.zeromq.org/spec/32/>`_ for more information.

If *pad* is true, the input is padded with ``b'\0'`` so its length is a
multiple of 4 bytes before encoding.

.. versionadded:: 3.13

.. versionchanged:: next
The *pad* parameter was added.


.. function:: z85decode(s)

Expand Down
4 changes: 2 additions & 2 deletions Lib/base64.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,9 +508,9 @@ def b85decode(b):
)
_z85_encode_translation = bytes.maketrans(_b85alphabet, _z85alphabet)

def z85encode(s):
def z85encode(s, pad=False):
"""Encode bytes-like object b in z85 format and return a bytes object."""
return b85encode(s).translate(_z85_encode_translation)
return b85encode(s, pad).translate(_z85_encode_translation)

def z85decode(s):
"""Decode the z85-encoded bytes-like object or ASCII string b
Expand Down
16 changes: 16 additions & 0 deletions Lib/test/test_base64.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ def test_z85encode(self):

tests = {
b'': b'',
b'\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B': b'HelloWorld',
b'www.python.org': b'CxXl-AcVLsz/dgCA+t',
bytes(range(255)): b"""009c61o!#m2NH?C3>iWS5d]J*6CRx17-skh9337x"""
b"""ar.{NbQB=+c[cR@eg&FcfFLssg=mfIi5%2YjuU>)kTv.7l}6Nnnj=AD"""
Expand Down Expand Up @@ -840,6 +841,21 @@ def test_b85_padding(self):
eq(base64.b85decode(b'czAet'), b"xxxx")
eq(base64.b85decode(b'czAetcmMzZ'), b"xxxxx\x00\x00\x00")

def test_z85_padding(self):
eq = self.assertEqual

eq(base64.z85encode(b"x", pad=True), b'CMmZz')
eq(base64.z85encode(b"xx", pad=True), b'CZ6h*')
eq(base64.z85encode(b"xxx", pad=True), b'CZaDk')
eq(base64.z85encode(b"xxxx", pad=True), b'CZaET')
eq(base64.z85encode(b"xxxxx", pad=True), b'CZaETCMmZz')

eq(base64.z85decode(b'CMmZz'), b"x\x00\x00\x00")
eq(base64.z85decode(b'CZ6h*'), b"xx\x00\x00")
eq(base64.z85decode(b'CZaDk'), b"xxx\x00")
eq(base64.z85decode(b'CZaET'), b"xxxx")
eq(base64.z85decode(b'CZaETCMmZz'), b"xxxxx\x00\x00\x00")

def test_a85decode_errors(self):
illegal = (set(range(32)) | set(range(118, 256))) - set(b' \t\n\r\v')
for c in illegal:
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/test_ctypes/test_struct_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,21 @@ class S(Structure):
self.check_struct(S)
self.assertEqual(S.largeField.bit_size, size * 8)

def test_bitfield_overflow_error_message(self):
with self.assertRaisesRegex(
ValueError,
r"bit field 'x' overflows its type \(2 \+ 7 > 8\)",
):
CField(
name="x",
type=c_byte,
byte_size=1,
byte_offset=0,
index=0,
_internal_use=True,
bit_size=7,
bit_offset=2,
)

# __set__ and __get__ should raise a TypeError in case their self
# argument is not a ctype instance.
Expand Down
32 changes: 32 additions & 0 deletions Lib/test/test_free_threading/test_gc.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,38 @@ def mutator_thread():
with threading_helper.start_threads(gcs + mutators):
pass

def test_freeze_object_in_brc_queue(self):
# GH-142975: Freezing objects in the BRC queue could result in some
# objects having a zero refcount without being deallocated.

class Weird:
# We need a destructor to trigger the check for object resurrection
def __del__(self):
pass

# This is owned by the main thread, so the subthread will have to increment
# this object's reference count.
weird = Weird()

def evil():
gc.freeze()

# Decrement the reference count from this thread, which will trigger the
# slow path during resurrection and add our weird object to the BRC queue.
nonlocal weird
del weird

# Collection will merge the object's reference count and make it zero.
gc.collect()

# Unfreeze the object, making it visible to the GC.
gc.unfreeze()
gc.collect()

thread = Thread(target=evil)
thread.start()
thread.join()


if __name__ == "__main__":
unittest.main()
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ Lisandro Dalcin
Darren Dale
Andrew Dalke
Lars Damerow
Hauke Dämpfling
Evan Dandrea
Eric Daniel
Scott David Daniels
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix crash after unfreezing all objects tracked by the garbage collector on
the :term:`free threaded <free threading>` build.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add padding support to :func:`base64.z85encode` via the ``pad`` parameter.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a possible reference leak in ctypes when constructing results with multiple output parameters on error.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix the ctypes bitfield overflow error message to report the correct offset and size calculation.
1 change: 1 addition & 0 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -4557,6 +4557,7 @@ _build_result(PyObject *result, PyObject *callargs,
v = PyTuple_GET_ITEM(callargs, i);
v = PyObject_CallMethodNoArgs(v, &_Py_ID(__ctypes_from_outparam__));
if (v == NULL || numretvals == 1) {
Py_XDECREF(tup);
Py_DECREF(callargs);
return v;
}
Expand Down
4 changes: 2 additions & 2 deletions Modules/_ctypes/cfield.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ PyCField_new_impl(PyTypeObject *type, PyObject *name, PyObject *proto,
if ((bitfield_size + bit_offset) > byte_size * 8) {
PyErr_Format(
PyExc_ValueError,
"bit field %R overflows its type (%zd + %zd >= %zd)",
name, bit_offset, byte_size*8);
"bit field %R overflows its type (%zd + %zd > %zd)",
name, bit_offset, bitfield_size, byte_size * 8);
goto error;
}
}
Expand Down
6 changes: 5 additions & 1 deletion Python/gc_free_threading.c
Original file line number Diff line number Diff line change
Expand Up @@ -906,7 +906,11 @@ gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, gc_mark_args_t *ar
static void
queue_untracked_obj_decref(PyObject *op, struct collection_state *state)
{
if (!_PyObject_GC_IS_TRACKED(op)) {
assert(Py_REFCNT(op) == 0);
// gh-142975: We have to treat frozen objects as untracked in this function
// or else they might be picked up in a future collection, which breaks the
// assumption that all incoming objects have a non-zero reference count.
if (!_PyObject_GC_IS_TRACKED(op) || gc_is_frozen(op)) {
// GC objects with zero refcount are handled subsequently by the
// GC as if they were cyclic trash, but we have to handle dead
// non-GC objects here. Add one to the refcount so that we can
Expand Down
Loading