Skip to content
Merged
38 changes: 28 additions & 10 deletions Doc/library/contextvars.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,32 @@ Context Variables
to restore the variable to its previous value via the
:meth:`ContextVar.reset` method.

For convenience, the token object can be used as a context manager
to avoid calling :meth:`ContextVar.reset` manually::

var = ContextVar('var', default='default value')

with var.set('new value'):
assert var.get() == 'new value'

assert var.get() == 'default value'

It is a shorthand for::

var = ContextVar('var', default='default value')

token = var.set('new value')
try:
assert var.get() == 'new value'
finally:
var.reset(token)

assert var.get() == 'default value'

.. versionadded:: 3.14

Added support for using tokens as context managers.

.. method:: reset(token)

Reset the context variable to the value it had before the
Expand All @@ -101,16 +127,8 @@ Context Variables
the value of the variable to what it was before the corresponding
*set*.

The token supports :ref:`context manager protocol <context-managers>`
to restore the corresponding context variable value at the exit from
:keyword:`with` block::

var = ContextVar('var', default='default value')

with var.set('new value'):
assert var.get() == 'new value'

assert var.get() == 'default value'
Tokens support the :ref:`context manager protocol <context-managers>`
to automatically reset context variables. See :meth:`ContextVar.set`.

.. versionadded:: 3.14

Expand Down
13 changes: 0 additions & 13 deletions Doc/library/pickle.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,6 @@ files.

The :mod:`pickle` module differs from :mod:`marshal` in several significant ways:

* The :mod:`pickle` module keeps track of the objects it has already serialized,
so that later references to the same object won't be serialized again.
:mod:`marshal` doesn't do this.

This has implications both for recursive objects and object sharing. Recursive
objects are objects that contain references to themselves. These are not
handled by marshal, and in fact, attempting to marshal recursive objects will
crash your Python interpreter. Object sharing happens when there are multiple
references to the same object in different places in the object hierarchy being
serialized. :mod:`pickle` stores such objects only once, and ensures that all
other references point to the master copy. Shared objects remain shared, which
can be very important for mutable objects.

* :mod:`marshal` cannot be used to serialize user-defined classes and their
instances. :mod:`pickle` can save and restore class instances transparently,
however the class definition must be importable and live in the same module as
Expand Down
7 changes: 7 additions & 0 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ Special read-only attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. index::
single: __builtins__ (function attribute)
single: __closure__ (function attribute)
single: __globals__ (function attribute)
pair: global; namespace
Expand All @@ -556,6 +557,12 @@ Special read-only attributes
* - Attribute
- Meaning

* - .. attribute:: function.__builtins__
- A reference to the :class:`dictionary <dict>` that holds the function's
builtins namespace.

.. versionadded:: 3.10

* - .. attribute:: function.__globals__
- A reference to the :class:`dictionary <dict>` that holds the function's
:ref:`global variables <naming>` -- the global namespace of the module
Expand Down
1 change: 1 addition & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,7 @@ mimetypes
* Add ``application/dicom`` MIME type for ``.dcm`` extension. (Contributed by Benedikt Johannes in :gh:`144217`.)
* Add ``application/node`` MIME type for ``.cjs`` extension. (Contributed by John Franey in :gh:`140937`.)
* Add ``application/toml``. (Contributed by Gil Forcada in :gh:`139959`.)
* Add ``image/jxl``. (Contributed by Foolbar in :gh:`144213`.)
* Rename ``application/x-texinfo`` to ``application/texinfo``.
(Contributed by Charlie Lin in :gh:`140165`.)
* Changed the MIME type for ``.ai`` files to ``application/pdf``.
Expand Down
1 change: 1 addition & 0 deletions Lib/mimetypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ def _default_mime_types():
'.jpeg' : 'image/jpeg',
'.jpm' : 'image/jpm',
'.jpx' : 'image/jpx',
'.jxl' : 'image/jxl',
'.heic' : 'image/heic',
'.heif' : 'image/heif',
'.png' : 'image/png',
Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,23 @@ def test_empty(self):
a += a
self.assertEqual(len(a), 0)

def test_fromlist_reentrant_index_mutation(self):

class Evil:
def __init__(self, lst):
self.lst = lst
def __index__(self):
self.lst.clear()
return "not an int"

for typecode in ('I', 'L', 'Q'):
with self.subTest(typecode=typecode):
lst = []
lst.append(Evil(lst))
a = array.array(typecode)
with self.assertRaises(TypeError):
a.fromlist(lst)


# Machine format codes.
#
Expand Down
35 changes: 35 additions & 0 deletions Lib/test/test_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4447,6 +4447,41 @@ def test_bytearray_release_buffer_read_flag(self):
with self.assertRaises(SystemError):
obj.__buffer__(inspect.BufferFlags.WRITE)

@support.cpython_only
@unittest.skipIf(_testcapi is None, "requires _testcapi")
def test_bytearray_alignment(self):
# gh-140557: pointer alignment of buffers including empty allocation
# should be at least to `size_t`.
align = struct.calcsize("N")
cases = [
bytearray(),
bytearray(1),
bytearray(b"0123456789abcdef"),
bytearray(16),
]
ptrs = [_testcapi.buffer_pointer_as_int(array) for array in cases]
self.assertEqual([ptr % align for ptr in ptrs], [0]*len(ptrs))

@support.cpython_only
@unittest.skipIf(_testcapi is None, "requires _testcapi")
def test_array_alignment(self):
# gh-140557: pointer alignment of buffers including empty allocation
# should match the maximum array alignment.
align = max(struct.calcsize(fmt) for fmt in ARRAY)
cases = [array.array(fmt) for fmt in ARRAY]
# Empty arrays
self.assertEqual(
[_testcapi.buffer_pointer_as_int(case) % align for case in cases],
[0] * len(cases),
)
for case in cases:
case.append(0)
# Allocated arrays
self.assertEqual(
[_testcapi.buffer_pointer_as_int(case) % align for case in cases],
[0] * len(cases),
)

@support.cpython_only
def test_pybuffer_size_from_format(self):
# basic tests
Expand Down
25 changes: 24 additions & 1 deletion Lib/test/test_ctypes/test_incomplete.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ctypes
import unittest
from ctypes import Structure, POINTER, pointer, c_char_p
from ctypes import Structure, POINTER, pointer, c_char_p, c_int

# String-based "incomplete pointers" were implemented in ctypes 0.6.3 (2003, when
# ctypes was an external project). They made obsolete by the current
Expand Down Expand Up @@ -50,6 +50,29 @@ class cell(Structure):
lpcell.set_type(cell)
self.assertIs(POINTER(cell), lpcell)

def test_set_type_updates_format(self):
# gh-142966: set_type should update StgInfo.format
# to match the element type's format
with self.assertWarns(DeprecationWarning):
lp = POINTER("node")

class node(Structure):
_fields_ = [("value", c_int)]

# Get the expected format before set_type
node_format = memoryview(node()).format
expected_format = "&" + node_format

lp.set_type(node)

# Create instance to check format via memoryview
n = node(42)
p = lp(n)
actual_format = memoryview(p).format

# After set_type, the pointer's format should be "&<element_format>"
self.assertEqual(actual_format, expected_format)


if __name__ == '__main__':
unittest.main()
10 changes: 10 additions & 0 deletions Lib/test/test_ctypes/test_pointers.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,16 @@ class Cls(Structure):
self.assertEqual(len(ws_typ), 0, ws_typ)
self.assertEqual(len(ws_ptr), 0, ws_ptr)

def test_pointer_proto_missing_argtypes_error(self):
class BadType(ctypes._Pointer):
# _type_ is intentionally missing
pass

func = ctypes.pythonapi.Py_GetVersion
func.argtypes = (BadType,)

with self.assertRaises(ctypes.ArgumentError):
func(object())

class PointerTypeCacheTestCase(unittest.TestCase):
# dummy tests to check warnings and base behavior
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_mimetypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ def check_extensions():
("image/jp2", ".jp2"),
("image/jpeg", ".jpg"),
("image/jpm", ".jpm"),
("image/jxl", ".jxl"),
("image/t38", ".t38"),
("image/tiff", ".tiff"),
("image/tiff-fx", ".tfx"),
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,7 @@ Per Lindqvist
Eric Lindvall
Gregor Lingl
Everett Lipman
Jake Lishman
Mirko Liss
Alexander Liu
Hui Liu
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:class:`bytearray` buffers now have the same alignment
when empty as when allocated. Unaligned buffers can still be created by slicing.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix :func:`!ctypes.POINTER.set_type` not updating the format string to match the type.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:class:`array.array` buffers now have the same alignment when empty as when
allocated. Unaligned buffers can still be created by slicing.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fixed a crash in ctypes when using a deprecated ``POINTER(str)`` type in
``argtypes``. Instead of aborting, ctypes now raises a proper Python
exception when the pointer target type is unresolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a crash in :meth:`array.array.fromlist` when an element's :meth:`~object.__index__` method mutates
the input list during conversion.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Mime type ``image/jxl`` is now supported by :mod:`mimetypes`.
51 changes: 26 additions & 25 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1258,11 +1258,30 @@ PyCPointerType_SetProto(ctypes_state *st, PyObject *self, StgInfo *stginfo, PyOb
return -1;
}
Py_XSETREF(stginfo->proto, Py_NewRef(proto));

// Set the format string for the pointer type based on element type.
// If info->format is NULL, this is a pointer to an incomplete type.
// We create a generic format string 'pointer to bytes' in this case.
char *new_format = NULL;
STGINFO_LOCK(info);
if (info->pointer_type == NULL) {
Py_XSETREF(info->pointer_type, Py_NewRef(self));
}
const char *current_format = info->format ? info->format : "B";
if (info->shape != NULL) {
// pointer to an array: the shape needs to be prefixed
new_format = _ctypes_alloc_format_string_with_shape(
info->ndim, info->shape, "&", current_format);
} else {
new_format = _ctypes_alloc_format_string("&", current_format);
}
PyMem_Free(stginfo->format);
stginfo->format = new_format;
STGINFO_UNLOCK();

if (new_format == NULL) {
return -1;
}
return 0;
}

Expand Down Expand Up @@ -1314,35 +1333,11 @@ PyCPointerType_init(PyObject *self, PyObject *args, PyObject *kwds)
return -1;
}
if (proto) {
const char *current_format;
if (PyCPointerType_SetProto(st, self, stginfo, proto) < 0) {
Py_DECREF(proto);
return -1;
}
StgInfo *iteminfo;
if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) {
Py_DECREF(proto);
return -1;
}
/* PyCPointerType_SetProto has verified proto has a stginfo. */
assert(iteminfo);
/* If iteminfo->format is NULL, then this is a pointer to an
incomplete type. We create a generic format string
'pointer to bytes' in this case. XXX Better would be to
fix the format string later...
*/
current_format = iteminfo->format ? iteminfo->format : "B";
if (iteminfo->shape != NULL) {
/* pointer to an array: the shape needs to be prefixed */
stginfo->format = _ctypes_alloc_format_string_with_shape(
iteminfo->ndim, iteminfo->shape, "&", current_format);
} else {
stginfo->format = _ctypes_alloc_format_string("&", current_format);
}
Py_DECREF(proto);
if (stginfo->format == NULL) {
return -1;
}
}

return 0;
Expand Down Expand Up @@ -1419,7 +1414,13 @@ PyCPointerType_from_param_impl(PyObject *type, PyTypeObject *cls,
/* If we expect POINTER(<type>), but receive a <type> instance, accept
it by calling byref(<type>).
*/
assert(typeinfo->proto);
if (typeinfo->proto == NULL) {
PyErr_SetString(
PyExc_TypeError,
"cannot convert argument: POINTER _type_ type is not set"
);
return NULL;
}
switch (PyObject_IsInstance(value, typeinfo->proto)) {
case 1:
Py_INCREF(value); /* _byref steals a refcount */
Expand Down
24 changes: 24 additions & 0 deletions Modules/_testcapi/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,27 @@ static PyTypeObject testBufType = {
.tp_members = testbuf_members
};

/* Get the pointer from a buffer-supporting object as a PyLong.
*
* Used to test alignment properties. */
static PyObject *
buffer_pointer_as_int(PyObject *Py_UNUSED(module), PyObject *obj)
{
PyObject *out;
Py_buffer view;
if (PyObject_GetBuffer(obj, &view, PyBUF_SIMPLE) != 0) {
return NULL;
}
out = PyLong_FromVoidPtr(view.buf);
PyBuffer_Release(&view);
return out;
}

static PyMethodDef test_methods[] = {
{"buffer_pointer_as_int", buffer_pointer_as_int, METH_O},
{NULL},
};

int
_PyTestCapi_Init_Buffer(PyObject *m) {
if (PyType_Ready(&testBufType) < 0) {
Expand All @@ -106,6 +127,9 @@ _PyTestCapi_Init_Buffer(PyObject *m) {
if (PyModule_AddObjectRef(m, "testBuf", (PyObject *)&testBufType)) {
return -1;
}
if (PyModule_AddFunctions(m, test_methods) < 0) {
return -1;
}

return 0;
}
Loading
Loading