Skip to content

Commit c0f3d2a

Browse files
authored
Merge branch 'main' into buffered-gzip-writes
2 parents 5ef4674 + 405eacc commit c0f3d2a

File tree

8 files changed

+347
-24
lines changed

8 files changed

+347
-24
lines changed

Doc/library/bisect.rst

+12-10
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ method to determine whether a value has been found. Instead, the
2424
functions only call the :meth:`__lt__` method and will return an insertion
2525
point between values in an array.
2626

27+
.. _bisect functions:
28+
2729
The following functions are provided:
2830

2931

@@ -55,7 +57,7 @@ The following functions are provided:
5557
.. function:: bisect_right(a, x, lo=0, hi=len(a), *, key=None)
5658
bisect(a, x, lo=0, hi=len(a), *, key=None)
5759
58-
Similar to :func:`bisect_left`, but returns an insertion point which comes
60+
Similar to :py:func:`~bisect.bisect_left`, but returns an insertion point which comes
5961
after (to the right of) any existing entries of *x* in *a*.
6062

6163
The returned insertion point *ip* partitions the array *a* into two slices
@@ -70,7 +72,7 @@ The following functions are provided:
7072

7173
Insert *x* in *a* in sorted order.
7274

73-
This function first runs :func:`bisect_left` to locate an insertion point.
75+
This function first runs :py:func:`~bisect.bisect_left` to locate an insertion point.
7476
Next, it runs the :meth:`insert` method on *a* to insert *x* at the
7577
appropriate position to maintain sort order.
7678

@@ -87,10 +89,10 @@ The following functions are provided:
8789
.. function:: insort_right(a, x, lo=0, hi=len(a), *, key=None)
8890
insort(a, x, lo=0, hi=len(a), *, key=None)
8991
90-
Similar to :func:`insort_left`, but inserting *x* in *a* after any existing
92+
Similar to :py:func:`~bisect.insort_left`, but inserting *x* in *a* after any existing
9193
entries of *x*.
9294

93-
This function first runs :func:`bisect_right` to locate an insertion point.
95+
This function first runs :py:func:`~bisect.bisect_right` to locate an insertion point.
9496
Next, it runs the :meth:`insert` method on *a* to insert *x* at the
9597
appropriate position to maintain sort order.
9698

@@ -120,7 +122,7 @@ thoughts in mind:
120122
they are used. Consequently, if the search functions are used in a loop,
121123
the key function may be called again and again on the same array elements.
122124
If the key function isn't fast, consider wrapping it with
123-
:func:`functools.cache` to avoid duplicate computations. Alternatively,
125+
:py:func:`functools.cache` to avoid duplicate computations. Alternatively,
124126
consider searching an array of precomputed keys to locate the insertion
125127
point (as shown in the examples section below).
126128

@@ -140,7 +142,7 @@ thoughts in mind:
140142
Searching Sorted Lists
141143
----------------------
142144

143-
The above :func:`bisect` functions are useful for finding insertion points but
145+
The above `bisect functions`_ are useful for finding insertion points but
144146
can be tricky or awkward to use for common searching tasks. The following five
145147
functions show how to transform them into the standard lookups for sorted
146148
lists::
@@ -186,8 +188,8 @@ Examples
186188

187189
.. _bisect-example:
188190

189-
The :func:`bisect` function can be useful for numeric table lookups. This
190-
example uses :func:`bisect` to look up a letter grade for an exam score (say)
191+
The :py:func:`~bisect.bisect` function can be useful for numeric table lookups. This
192+
example uses :py:func:`~bisect.bisect` to look up a letter grade for an exam score (say)
191193
based on a set of ordered numeric breakpoints: 90 and up is an 'A', 80 to 89 is
192194
a 'B', and so on::
193195

@@ -198,8 +200,8 @@ a 'B', and so on::
198200
>>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
199201
['F', 'A', 'C', 'C', 'B', 'A', 'A']
200202

201-
The :func:`bisect` and :func:`insort` functions also work with lists of
202-
tuples. The *key* argument can serve to extract the field used for ordering
203+
The :py:func:`~bisect.bisect` and :py:func:`~bisect.insort` functions also work with
204+
lists of tuples. The *key* argument can serve to extract the field used for ordering
203205
records in a table::
204206

205207
>>> from collections import namedtuple

Include/cpython/memoryobject.h

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ typedef struct {
2424
#define _Py_MEMORYVIEW_FORTRAN 0x004 /* Fortran contiguous layout */
2525
#define _Py_MEMORYVIEW_SCALAR 0x008 /* scalar: ndim = 0 */
2626
#define _Py_MEMORYVIEW_PIL 0x010 /* PIL-style layout */
27+
#define _Py_MEMORYVIEW_RESTRICTED 0x020 /* Disallow new references to the memoryview's buffer */
2728

2829
typedef struct {
2930
PyObject_VAR_HEAD

Include/internal/pycore_memoryobject.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ extern "C" {
99
#endif
1010

1111
PyObject *
12-
PyMemoryView_FromObjectAndFlags(PyObject *v, int flags);
12+
_PyMemoryView_FromBufferProc(PyObject *v, int flags,
13+
getbufferproc bufferproc);
1314

1415
#ifdef __cplusplus
1516
}

Lib/test/test_buffer.py

+170
Original file line numberDiff line numberDiff line change
@@ -4579,6 +4579,176 @@ def test_c_buffer(self):
45794579
buf.__release_buffer__(mv)
45804580
self.assertEqual(buf.references, 0)
45814581

4582+
def test_inheritance(self):
4583+
class A(bytearray):
4584+
def __buffer__(self, flags):
4585+
return super().__buffer__(flags)
4586+
4587+
a = A(b"hello")
4588+
mv = memoryview(a)
4589+
self.assertEqual(mv.tobytes(), b"hello")
4590+
4591+
def test_inheritance_releasebuffer(self):
4592+
rb_call_count = 0
4593+
class B(bytearray):
4594+
def __buffer__(self, flags):
4595+
return super().__buffer__(flags)
4596+
def __release_buffer__(self, view):
4597+
nonlocal rb_call_count
4598+
rb_call_count += 1
4599+
super().__release_buffer__(view)
4600+
4601+
b = B(b"hello")
4602+
with memoryview(b) as mv:
4603+
self.assertEqual(mv.tobytes(), b"hello")
4604+
self.assertEqual(rb_call_count, 0)
4605+
self.assertEqual(rb_call_count, 1)
4606+
4607+
def test_inherit_but_return_something_else(self):
4608+
class A(bytearray):
4609+
def __buffer__(self, flags):
4610+
return memoryview(b"hello")
4611+
4612+
a = A(b"hello")
4613+
with memoryview(a) as mv:
4614+
self.assertEqual(mv.tobytes(), b"hello")
4615+
4616+
rb_call_count = 0
4617+
rb_raised = False
4618+
class B(bytearray):
4619+
def __buffer__(self, flags):
4620+
return memoryview(b"hello")
4621+
def __release_buffer__(self, view):
4622+
nonlocal rb_call_count
4623+
rb_call_count += 1
4624+
try:
4625+
super().__release_buffer__(view)
4626+
except ValueError:
4627+
nonlocal rb_raised
4628+
rb_raised = True
4629+
4630+
b = B(b"hello")
4631+
with memoryview(b) as mv:
4632+
self.assertEqual(mv.tobytes(), b"hello")
4633+
self.assertEqual(rb_call_count, 0)
4634+
self.assertEqual(rb_call_count, 1)
4635+
self.assertIs(rb_raised, True)
4636+
4637+
def test_override_only_release(self):
4638+
class C(bytearray):
4639+
def __release_buffer__(self, buffer):
4640+
super().__release_buffer__(buffer)
4641+
4642+
c = C(b"hello")
4643+
with memoryview(c) as mv:
4644+
self.assertEqual(mv.tobytes(), b"hello")
4645+
4646+
def test_release_saves_reference(self):
4647+
smuggled_buffer = None
4648+
4649+
class C(bytearray):
4650+
def __release_buffer__(s, buffer: memoryview):
4651+
with self.assertRaises(ValueError):
4652+
memoryview(buffer)
4653+
with self.assertRaises(ValueError):
4654+
buffer.cast("b")
4655+
with self.assertRaises(ValueError):
4656+
buffer.toreadonly()
4657+
with self.assertRaises(ValueError):
4658+
buffer[:1]
4659+
with self.assertRaises(ValueError):
4660+
buffer.__buffer__(0)
4661+
nonlocal smuggled_buffer
4662+
smuggled_buffer = buffer
4663+
self.assertEqual(buffer.tobytes(), b"hello")
4664+
super().__release_buffer__(buffer)
4665+
4666+
c = C(b"hello")
4667+
with memoryview(c) as mv:
4668+
self.assertEqual(mv.tobytes(), b"hello")
4669+
c.clear()
4670+
with self.assertRaises(ValueError):
4671+
smuggled_buffer.tobytes()
4672+
4673+
def test_release_saves_reference_no_subclassing(self):
4674+
ba = bytearray(b"hello")
4675+
4676+
class C:
4677+
def __buffer__(self, flags):
4678+
return memoryview(ba)
4679+
4680+
def __release_buffer__(self, buffer):
4681+
self.buffer = buffer
4682+
4683+
c = C()
4684+
with memoryview(c) as mv:
4685+
self.assertEqual(mv.tobytes(), b"hello")
4686+
self.assertEqual(c.buffer.tobytes(), b"hello")
4687+
4688+
with self.assertRaises(BufferError):
4689+
ba.clear()
4690+
c.buffer.release()
4691+
ba.clear()
4692+
4693+
def test_multiple_inheritance_buffer_last(self):
4694+
class A:
4695+
def __buffer__(self, flags):
4696+
return memoryview(b"hello A")
4697+
4698+
class B(A, bytearray):
4699+
def __buffer__(self, flags):
4700+
return super().__buffer__(flags)
4701+
4702+
b = B(b"hello")
4703+
with memoryview(b) as mv:
4704+
self.assertEqual(mv.tobytes(), b"hello A")
4705+
4706+
class Releaser:
4707+
def __release_buffer__(self, buffer):
4708+
self.buffer = buffer
4709+
4710+
class C(Releaser, bytearray):
4711+
def __buffer__(self, flags):
4712+
return super().__buffer__(flags)
4713+
4714+
c = C(b"hello C")
4715+
with memoryview(c) as mv:
4716+
self.assertEqual(mv.tobytes(), b"hello C")
4717+
c.clear()
4718+
with self.assertRaises(ValueError):
4719+
c.buffer.tobytes()
4720+
4721+
def test_multiple_inheritance_buffer_last(self):
4722+
class A:
4723+
def __buffer__(self, flags):
4724+
raise RuntimeError("should not be called")
4725+
4726+
def __release_buffer__(self, buffer):
4727+
raise RuntimeError("should not be called")
4728+
4729+
class B(bytearray, A):
4730+
def __buffer__(self, flags):
4731+
return super().__buffer__(flags)
4732+
4733+
b = B(b"hello")
4734+
with memoryview(b) as mv:
4735+
self.assertEqual(mv.tobytes(), b"hello")
4736+
4737+
class Releaser:
4738+
buffer = None
4739+
def __release_buffer__(self, buffer):
4740+
self.buffer = buffer
4741+
4742+
class C(bytearray, Releaser):
4743+
def __buffer__(self, flags):
4744+
return super().__buffer__(flags)
4745+
4746+
c = C(b"hello")
4747+
with memoryview(c) as mv:
4748+
self.assertEqual(mv.tobytes(), b"hello")
4749+
c.clear()
4750+
self.assertIs(c.buffer, None)
4751+
45824752

45834753
if __name__ == "__main__":
45844754
unittest.main()

Modules/Setup

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ PYTHONPATH=$(COREPYTHONPATH)
166166
#_md5 md5module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_MD5.c -D_BSD_SOURCE -D_DEFAULT_SOURCE
167167
#_sha1 sha1module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_SHA1.c -D_BSD_SOURCE -D_DEFAULT_SOURCE
168168
#_sha2 sha2module.c -I$(srcdir)/Modules/_hacl/include Modules/_hacl/libHacl_Streaming_SHA2.a
169-
#_sha3 sha3module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_sha3.c -D_BSD_SOURCE -D_DEFAULT_SOURCE
169+
#_sha3 sha3module.c -I$(srcdir)/Modules/_hacl/include _hacl/Hacl_Hash_SHA3.c -D_BSD_SOURCE -D_DEFAULT_SOURCE
170170

171171
# text encodings and unicode
172172
#_codecs_cn cjkcodecs/_codecs_cn.c

Objects/bytearrayobject.c

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ static void
6161
bytearray_releasebuffer(PyByteArrayObject *obj, Py_buffer *view)
6262
{
6363
obj->ob_exports--;
64+
assert(obj->ob_exports >= 0);
6465
}
6566

6667
static int

Objects/memoryobject.c

+44-1
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,20 @@ PyTypeObject _PyManagedBuffer_Type = {
193193
return -1; \
194194
}
195195

196+
#define CHECK_RESTRICTED(mv) \
197+
if (((PyMemoryViewObject *)(mv))->flags & _Py_MEMORYVIEW_RESTRICTED) { \
198+
PyErr_SetString(PyExc_ValueError, \
199+
"cannot create new view on restricted memoryview"); \
200+
return NULL; \
201+
}
202+
203+
#define CHECK_RESTRICTED_INT(mv) \
204+
if (((PyMemoryViewObject *)(mv))->flags & _Py_MEMORYVIEW_RESTRICTED) { \
205+
PyErr_SetString(PyExc_ValueError, \
206+
"cannot create new view on restricted memoryview"); \
207+
return -1; \
208+
}
209+
196210
/* See gh-92888. These macros signal that we need to check the memoryview
197211
again due to possible read after frees. */
198212
#define CHECK_RELEASED_AGAIN(mv) CHECK_RELEASED(mv)
@@ -781,14 +795,15 @@ PyMemoryView_FromBuffer(const Py_buffer *info)
781795
using the given flags.
782796
If the object is a memoryview, the new memoryview must be registered
783797
with the same managed buffer. Otherwise, a new managed buffer is created. */
784-
PyObject *
798+
static PyObject *
785799
PyMemoryView_FromObjectAndFlags(PyObject *v, int flags)
786800
{
787801
_PyManagedBufferObject *mbuf;
788802

789803
if (PyMemoryView_Check(v)) {
790804
PyMemoryViewObject *mv = (PyMemoryViewObject *)v;
791805
CHECK_RELEASED(mv);
806+
CHECK_RESTRICTED(mv);
792807
return mbuf_add_view(mv->mbuf, &mv->view);
793808
}
794809
else if (PyObject_CheckBuffer(v)) {
@@ -806,6 +821,30 @@ PyMemoryView_FromObjectAndFlags(PyObject *v, int flags)
806821
Py_TYPE(v)->tp_name);
807822
return NULL;
808823
}
824+
825+
/* Create a memoryview from an object that implements the buffer protocol,
826+
using the given flags.
827+
If the object is a memoryview, the new memoryview must be registered
828+
with the same managed buffer. Otherwise, a new managed buffer is created. */
829+
PyObject *
830+
_PyMemoryView_FromBufferProc(PyObject *v, int flags, getbufferproc bufferproc)
831+
{
832+
_PyManagedBufferObject *mbuf = mbuf_alloc();
833+
if (mbuf == NULL)
834+
return NULL;
835+
836+
int res = bufferproc(v, &mbuf->master, flags);
837+
if (res < 0) {
838+
mbuf->master.obj = NULL;
839+
Py_DECREF(mbuf);
840+
return NULL;
841+
}
842+
843+
PyObject *ret = mbuf_add_view(mbuf, NULL);
844+
Py_DECREF(mbuf);
845+
return ret;
846+
}
847+
809848
/* Create a memoryview from an object that implements the buffer protocol.
810849
If the object is a memoryview, the new memoryview must be registered
811850
with the same managed buffer. Otherwise, a new managed buffer is created. */
@@ -1397,6 +1436,7 @@ memoryview_cast_impl(PyMemoryViewObject *self, PyObject *format,
13971436
Py_ssize_t ndim = 1;
13981437

13991438
CHECK_RELEASED(self);
1439+
CHECK_RESTRICTED(self);
14001440

14011441
if (!MV_C_CONTIGUOUS(self->flags)) {
14021442
PyErr_SetString(PyExc_TypeError,
@@ -1452,6 +1492,7 @@ memoryview_toreadonly_impl(PyMemoryViewObject *self)
14521492
/*[clinic end generated code: output=2c7e056f04c99e62 input=dc06d20f19ba236f]*/
14531493
{
14541494
CHECK_RELEASED(self);
1495+
CHECK_RESTRICTED(self);
14551496
/* Even if self is already readonly, we still need to create a new
14561497
* object for .release() to work correctly.
14571498
*/
@@ -1474,6 +1515,7 @@ memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags)
14741515
int baseflags = self->flags;
14751516

14761517
CHECK_RELEASED_INT(self);
1518+
CHECK_RESTRICTED_INT(self);
14771519

14781520
/* start with complete information */
14791521
*view = *base;
@@ -2535,6 +2577,7 @@ memory_subscript(PyMemoryViewObject *self, PyObject *key)
25352577
return memory_item(self, index);
25362578
}
25372579
else if (PySlice_Check(key)) {
2580+
CHECK_RESTRICTED(self);
25382581
PyMemoryViewObject *sliced;
25392582

25402583
sliced = (PyMemoryViewObject *)mbuf_add_view(self->mbuf, view);

0 commit comments

Comments
 (0)