mirror of https://github.com/python/cpython
gh-102500: Implement PEP 688 (#102521)
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
This commit is contained in:
parent
b17d32c114
commit
04f6733275
|
@ -593,6 +593,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__await__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__await__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bases__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bases__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bool__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bool__));
|
||||||
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__buffer__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__build_class__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__build_class__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__builtins__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__builtins__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bytes__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bytes__));
|
||||||
|
@ -692,6 +693,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__rdivmod__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__rdivmod__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__reduce__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__reduce__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__reduce_ex__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__reduce_ex__));
|
||||||
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__release_buffer__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__repr__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__repr__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__reversed__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__reversed__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__rfloordiv__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__rfloordiv__));
|
||||||
|
@ -1122,6 +1124,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(reducer_override));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(reducer_override));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(registry));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(registry));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(rel_tol));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(rel_tol));
|
||||||
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(release));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(reload));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(reload));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(repl));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(repl));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(replace));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(replace));
|
||||||
|
|
|
@ -81,6 +81,7 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(__await__)
|
STRUCT_FOR_ID(__await__)
|
||||||
STRUCT_FOR_ID(__bases__)
|
STRUCT_FOR_ID(__bases__)
|
||||||
STRUCT_FOR_ID(__bool__)
|
STRUCT_FOR_ID(__bool__)
|
||||||
|
STRUCT_FOR_ID(__buffer__)
|
||||||
STRUCT_FOR_ID(__build_class__)
|
STRUCT_FOR_ID(__build_class__)
|
||||||
STRUCT_FOR_ID(__builtins__)
|
STRUCT_FOR_ID(__builtins__)
|
||||||
STRUCT_FOR_ID(__bytes__)
|
STRUCT_FOR_ID(__bytes__)
|
||||||
|
@ -180,6 +181,7 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(__rdivmod__)
|
STRUCT_FOR_ID(__rdivmod__)
|
||||||
STRUCT_FOR_ID(__reduce__)
|
STRUCT_FOR_ID(__reduce__)
|
||||||
STRUCT_FOR_ID(__reduce_ex__)
|
STRUCT_FOR_ID(__reduce_ex__)
|
||||||
|
STRUCT_FOR_ID(__release_buffer__)
|
||||||
STRUCT_FOR_ID(__repr__)
|
STRUCT_FOR_ID(__repr__)
|
||||||
STRUCT_FOR_ID(__reversed__)
|
STRUCT_FOR_ID(__reversed__)
|
||||||
STRUCT_FOR_ID(__rfloordiv__)
|
STRUCT_FOR_ID(__rfloordiv__)
|
||||||
|
@ -610,6 +612,7 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(reducer_override)
|
STRUCT_FOR_ID(reducer_override)
|
||||||
STRUCT_FOR_ID(registry)
|
STRUCT_FOR_ID(registry)
|
||||||
STRUCT_FOR_ID(rel_tol)
|
STRUCT_FOR_ID(rel_tol)
|
||||||
|
STRUCT_FOR_ID(release)
|
||||||
STRUCT_FOR_ID(reload)
|
STRUCT_FOR_ID(reload)
|
||||||
STRUCT_FOR_ID(repl)
|
STRUCT_FOR_ID(repl)
|
||||||
STRUCT_FOR_ID(replace)
|
STRUCT_FOR_ID(replace)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef Py_INTERNAL_MEMORYOBJECT_H
|
||||||
|
#define Py_INTERNAL_MEMORYOBJECT_H
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Py_BUILD_CORE
|
||||||
|
# error "this header requires Py_BUILD_CORE define"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyMemoryView_FromObjectAndFlags(PyObject *v, int flags);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* !Py_INTERNAL_MEMORYOBJECT_H */
|
|
@ -587,6 +587,7 @@ extern "C" {
|
||||||
INIT_ID(__await__), \
|
INIT_ID(__await__), \
|
||||||
INIT_ID(__bases__), \
|
INIT_ID(__bases__), \
|
||||||
INIT_ID(__bool__), \
|
INIT_ID(__bool__), \
|
||||||
|
INIT_ID(__buffer__), \
|
||||||
INIT_ID(__build_class__), \
|
INIT_ID(__build_class__), \
|
||||||
INIT_ID(__builtins__), \
|
INIT_ID(__builtins__), \
|
||||||
INIT_ID(__bytes__), \
|
INIT_ID(__bytes__), \
|
||||||
|
@ -686,6 +687,7 @@ extern "C" {
|
||||||
INIT_ID(__rdivmod__), \
|
INIT_ID(__rdivmod__), \
|
||||||
INIT_ID(__reduce__), \
|
INIT_ID(__reduce__), \
|
||||||
INIT_ID(__reduce_ex__), \
|
INIT_ID(__reduce_ex__), \
|
||||||
|
INIT_ID(__release_buffer__), \
|
||||||
INIT_ID(__repr__), \
|
INIT_ID(__repr__), \
|
||||||
INIT_ID(__reversed__), \
|
INIT_ID(__reversed__), \
|
||||||
INIT_ID(__rfloordiv__), \
|
INIT_ID(__rfloordiv__), \
|
||||||
|
@ -1116,6 +1118,7 @@ extern "C" {
|
||||||
INIT_ID(reducer_override), \
|
INIT_ID(reducer_override), \
|
||||||
INIT_ID(registry), \
|
INIT_ID(registry), \
|
||||||
INIT_ID(rel_tol), \
|
INIT_ID(rel_tol), \
|
||||||
|
INIT_ID(release), \
|
||||||
INIT_ID(reload), \
|
INIT_ID(reload), \
|
||||||
INIT_ID(repl), \
|
INIT_ID(repl), \
|
||||||
INIT_ID(replace), \
|
INIT_ID(replace), \
|
||||||
|
|
|
@ -138,6 +138,8 @@ _Py_type_getattro(PyTypeObject *type, PyObject *name);
|
||||||
PyObject *_Py_slot_tp_getattro(PyObject *self, PyObject *name);
|
PyObject *_Py_slot_tp_getattro(PyObject *self, PyObject *name);
|
||||||
PyObject *_Py_slot_tp_getattr_hook(PyObject *self, PyObject *name);
|
PyObject *_Py_slot_tp_getattr_hook(PyObject *self, PyObject *name);
|
||||||
|
|
||||||
|
PyAPI_DATA(PyTypeObject) _PyBufferWrapper_Type;
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
_PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found);
|
_PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found);
|
||||||
PyObject *
|
PyObject *
|
||||||
|
|
|
@ -96,6 +96,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||||
string = &_Py_ID(__bool__);
|
string = &_Py_ID(__bool__);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
string = &_Py_ID(__buffer__);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
string = &_Py_ID(__build_class__);
|
string = &_Py_ID(__build_class__);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
@ -393,6 +396,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||||
string = &_Py_ID(__reduce_ex__);
|
string = &_Py_ID(__reduce_ex__);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
string = &_Py_ID(__release_buffer__);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
string = &_Py_ID(__repr__);
|
string = &_Py_ID(__repr__);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
@ -1683,6 +1689,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||||
string = &_Py_ID(rel_tol);
|
string = &_Py_ID(rel_tol);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
string = &_Py_ID(release);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
string = &_Py_ID(reload);
|
string = &_Py_ID(reload);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
|
|
@ -104,7 +104,7 @@ PyAPI_FUNC(void) PyBuffer_Release(Py_buffer *view);
|
||||||
/* Maximum number of dimensions */
|
/* Maximum number of dimensions */
|
||||||
#define PyBUF_MAX_NDIM 64
|
#define PyBUF_MAX_NDIM 64
|
||||||
|
|
||||||
/* Flags for getting buffers */
|
/* Flags for getting buffers. Keep these in sync with inspect.BufferFlags. */
|
||||||
#define PyBUF_SIMPLE 0
|
#define PyBUF_SIMPLE 0
|
||||||
#define PyBUF_WRITABLE 0x0001
|
#define PyBUF_WRITABLE 0x0001
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ __all__ = ["Awaitable", "Coroutine",
|
||||||
"Mapping", "MutableMapping",
|
"Mapping", "MutableMapping",
|
||||||
"MappingView", "KeysView", "ItemsView", "ValuesView",
|
"MappingView", "KeysView", "ItemsView", "ValuesView",
|
||||||
"Sequence", "MutableSequence",
|
"Sequence", "MutableSequence",
|
||||||
"ByteString",
|
"ByteString", "Buffer",
|
||||||
]
|
]
|
||||||
|
|
||||||
# This module has been renamed from collections.abc to _collections_abc to
|
# This module has been renamed from collections.abc to _collections_abc to
|
||||||
|
@ -439,6 +439,21 @@ class Collection(Sized, Iterable, Container):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
|
class Buffer(metaclass=ABCMeta):
|
||||||
|
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def __buffer__(self, flags: int, /) -> memoryview:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __subclasshook__(cls, C):
|
||||||
|
if cls is Buffer:
|
||||||
|
return _check_methods(C, "__buffer__")
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
class _CallableGenericAlias(GenericAlias):
|
class _CallableGenericAlias(GenericAlias):
|
||||||
""" Represent `Callable[argtypes, resulttype]`.
|
""" Represent `Callable[argtypes, resulttype]`.
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ __all__ = [
|
||||||
"Attribute",
|
"Attribute",
|
||||||
"BlockFinder",
|
"BlockFinder",
|
||||||
"BoundArguments",
|
"BoundArguments",
|
||||||
|
"BufferFlags",
|
||||||
"CORO_CLOSED",
|
"CORO_CLOSED",
|
||||||
"CORO_CREATED",
|
"CORO_CREATED",
|
||||||
"CORO_RUNNING",
|
"CORO_RUNNING",
|
||||||
|
@ -3312,6 +3313,28 @@ def signature(obj, *, follow_wrapped=True, globals=None, locals=None, eval_str=F
|
||||||
globals=globals, locals=locals, eval_str=eval_str)
|
globals=globals, locals=locals, eval_str=eval_str)
|
||||||
|
|
||||||
|
|
||||||
|
class BufferFlags(enum.IntFlag):
|
||||||
|
SIMPLE = 0x0
|
||||||
|
WRITABLE = 0x1
|
||||||
|
FORMAT = 0x4
|
||||||
|
ND = 0x8
|
||||||
|
STRIDES = 0x10 | ND
|
||||||
|
C_CONTIGUOUS = 0x20 | STRIDES
|
||||||
|
F_CONTIGUOUS = 0x40 | STRIDES
|
||||||
|
ANY_CONTIGUOUS = 0x80 | STRIDES
|
||||||
|
INDIRECT = 0x100 | STRIDES
|
||||||
|
CONTIG = ND | WRITABLE
|
||||||
|
CONTIG_RO = ND
|
||||||
|
STRIDED = STRIDES | WRITABLE
|
||||||
|
STRIDED_RO = STRIDES
|
||||||
|
RECORDS = STRIDES | WRITABLE | FORMAT
|
||||||
|
RECORDS_RO = STRIDES | FORMAT
|
||||||
|
FULL = INDIRECT | WRITABLE | FORMAT
|
||||||
|
FULL_RO = INDIRECT | FORMAT
|
||||||
|
READ = 0x100
|
||||||
|
WRITE = 0x200
|
||||||
|
|
||||||
|
|
||||||
def _main():
|
def _main():
|
||||||
""" Logic for inspecting an object given at command line """
|
""" Logic for inspecting an object given at command line """
|
||||||
import argparse
|
import argparse
|
||||||
|
|
|
@ -17,6 +17,7 @@ import contextlib
|
||||||
import unittest
|
import unittest
|
||||||
from test import support
|
from test import support
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
|
import inspect
|
||||||
from itertools import permutations, product
|
from itertools import permutations, product
|
||||||
from random import randrange, sample, choice
|
from random import randrange, sample, choice
|
||||||
import warnings
|
import warnings
|
||||||
|
@ -4438,5 +4439,146 @@ class TestBufferProtocol(unittest.TestCase):
|
||||||
struct.calcsize(format))
|
struct.calcsize(format))
|
||||||
|
|
||||||
|
|
||||||
|
class TestPythonBufferProtocol(unittest.TestCase):
|
||||||
|
def test_basic(self):
|
||||||
|
class MyBuffer:
|
||||||
|
def __buffer__(self, flags):
|
||||||
|
return memoryview(b"hello")
|
||||||
|
|
||||||
|
mv = memoryview(MyBuffer())
|
||||||
|
self.assertEqual(mv.tobytes(), b"hello")
|
||||||
|
self.assertEqual(bytes(MyBuffer()), b"hello")
|
||||||
|
|
||||||
|
def test_bad_buffer_method(self):
|
||||||
|
class MustReturnMV:
|
||||||
|
def __buffer__(self, flags):
|
||||||
|
return 42
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, memoryview, MustReturnMV())
|
||||||
|
|
||||||
|
class NoBytesEither:
|
||||||
|
def __buffer__(self, flags):
|
||||||
|
return b"hello"
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, memoryview, NoBytesEither())
|
||||||
|
|
||||||
|
class WrongArity:
|
||||||
|
def __buffer__(self):
|
||||||
|
return memoryview(b"hello")
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, memoryview, WrongArity())
|
||||||
|
|
||||||
|
def test_release_buffer(self):
|
||||||
|
class WhatToRelease:
|
||||||
|
def __init__(self):
|
||||||
|
self.held = False
|
||||||
|
self.ba = bytearray(b"hello")
|
||||||
|
|
||||||
|
def __buffer__(self, flags):
|
||||||
|
if self.held:
|
||||||
|
raise TypeError("already held")
|
||||||
|
self.held = True
|
||||||
|
return memoryview(self.ba)
|
||||||
|
|
||||||
|
def __release_buffer__(self, buffer):
|
||||||
|
self.held = False
|
||||||
|
|
||||||
|
wr = WhatToRelease()
|
||||||
|
self.assertFalse(wr.held)
|
||||||
|
with memoryview(wr) as mv:
|
||||||
|
self.assertTrue(wr.held)
|
||||||
|
self.assertEqual(mv.tobytes(), b"hello")
|
||||||
|
self.assertFalse(wr.held)
|
||||||
|
|
||||||
|
def test_same_buffer_returned(self):
|
||||||
|
class WhatToRelease:
|
||||||
|
def __init__(self):
|
||||||
|
self.held = False
|
||||||
|
self.ba = bytearray(b"hello")
|
||||||
|
self.created_mv = None
|
||||||
|
|
||||||
|
def __buffer__(self, flags):
|
||||||
|
if self.held:
|
||||||
|
raise TypeError("already held")
|
||||||
|
self.held = True
|
||||||
|
self.created_mv = memoryview(self.ba)
|
||||||
|
return self.created_mv
|
||||||
|
|
||||||
|
def __release_buffer__(self, buffer):
|
||||||
|
assert buffer is self.created_mv
|
||||||
|
self.held = False
|
||||||
|
|
||||||
|
wr = WhatToRelease()
|
||||||
|
self.assertFalse(wr.held)
|
||||||
|
with memoryview(wr) as mv:
|
||||||
|
self.assertTrue(wr.held)
|
||||||
|
self.assertEqual(mv.tobytes(), b"hello")
|
||||||
|
self.assertFalse(wr.held)
|
||||||
|
|
||||||
|
def test_buffer_flags(self):
|
||||||
|
class PossiblyMutable:
|
||||||
|
def __init__(self, data, mutable) -> None:
|
||||||
|
self._data = bytearray(data)
|
||||||
|
self._mutable = mutable
|
||||||
|
|
||||||
|
def __buffer__(self, flags):
|
||||||
|
if flags & inspect.BufferFlags.WRITABLE:
|
||||||
|
if not self._mutable:
|
||||||
|
raise RuntimeError("not mutable")
|
||||||
|
return memoryview(self._data)
|
||||||
|
else:
|
||||||
|
return memoryview(bytes(self._data))
|
||||||
|
|
||||||
|
mutable = PossiblyMutable(b"hello", True)
|
||||||
|
immutable = PossiblyMutable(b"hello", False)
|
||||||
|
with memoryview._from_flags(mutable, inspect.BufferFlags.WRITABLE) as mv:
|
||||||
|
self.assertEqual(mv.tobytes(), b"hello")
|
||||||
|
mv[0] = ord(b'x')
|
||||||
|
self.assertEqual(mv.tobytes(), b"xello")
|
||||||
|
with memoryview._from_flags(mutable, inspect.BufferFlags.SIMPLE) as mv:
|
||||||
|
self.assertEqual(mv.tobytes(), b"xello")
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
mv[0] = ord(b'h')
|
||||||
|
self.assertEqual(mv.tobytes(), b"xello")
|
||||||
|
with memoryview._from_flags(immutable, inspect.BufferFlags.SIMPLE) as mv:
|
||||||
|
self.assertEqual(mv.tobytes(), b"hello")
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
mv[0] = ord(b'x')
|
||||||
|
self.assertEqual(mv.tobytes(), b"hello")
|
||||||
|
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
memoryview._from_flags(immutable, inspect.BufferFlags.WRITABLE)
|
||||||
|
with memoryview(immutable) as mv:
|
||||||
|
self.assertEqual(mv.tobytes(), b"hello")
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
mv[0] = ord(b'x')
|
||||||
|
self.assertEqual(mv.tobytes(), b"hello")
|
||||||
|
|
||||||
|
def test_call_builtins(self):
|
||||||
|
ba = bytearray(b"hello")
|
||||||
|
mv = ba.__buffer__(0)
|
||||||
|
self.assertEqual(mv.tobytes(), b"hello")
|
||||||
|
ba.__release_buffer__(mv)
|
||||||
|
with self.assertRaises(OverflowError):
|
||||||
|
ba.__buffer__(sys.maxsize + 1)
|
||||||
|
|
||||||
|
@unittest.skipIf(_testcapi is None, "requires _testcapi")
|
||||||
|
def test_c_buffer(self):
|
||||||
|
buf = _testcapi.testBuf()
|
||||||
|
self.assertEqual(buf.references, 0)
|
||||||
|
mv = buf.__buffer__(0)
|
||||||
|
self.assertIsInstance(mv, memoryview)
|
||||||
|
self.assertEqual(mv.tobytes(), b"test")
|
||||||
|
self.assertEqual(buf.references, 1)
|
||||||
|
buf.__release_buffer__(mv)
|
||||||
|
self.assertEqual(buf.references, 0)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
mv.tobytes()
|
||||||
|
# Calling it again doesn't cause issues
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
buf.__release_buffer__(mv)
|
||||||
|
self.assertEqual(buf.references, 0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -25,7 +25,7 @@ from collections.abc import Sized, Container, Callable, Collection
|
||||||
from collections.abc import Set, MutableSet
|
from collections.abc import Set, MutableSet
|
||||||
from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView
|
from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView
|
||||||
from collections.abc import Sequence, MutableSequence
|
from collections.abc import Sequence, MutableSequence
|
||||||
from collections.abc import ByteString
|
from collections.abc import ByteString, Buffer
|
||||||
|
|
||||||
|
|
||||||
class TestUserObjects(unittest.TestCase):
|
class TestUserObjects(unittest.TestCase):
|
||||||
|
@ -1949,6 +1949,15 @@ class TestCollectionABCs(ABCTestCase):
|
||||||
self.assertFalse(issubclass(memoryview, ByteString))
|
self.assertFalse(issubclass(memoryview, ByteString))
|
||||||
self.validate_abstract_methods(ByteString, '__getitem__', '__len__')
|
self.validate_abstract_methods(ByteString, '__getitem__', '__len__')
|
||||||
|
|
||||||
|
def test_Buffer(self):
|
||||||
|
for sample in [bytes, bytearray, memoryview]:
|
||||||
|
self.assertIsInstance(sample(b"x"), Buffer)
|
||||||
|
self.assertTrue(issubclass(sample, Buffer))
|
||||||
|
for sample in [str, list, tuple]:
|
||||||
|
self.assertNotIsInstance(sample(), Buffer)
|
||||||
|
self.assertFalse(issubclass(sample, Buffer))
|
||||||
|
self.validate_abstract_methods(Buffer, '__buffer__')
|
||||||
|
|
||||||
def test_MutableSequence(self):
|
def test_MutableSequence(self):
|
||||||
for sample in [tuple, str, bytes]:
|
for sample in [tuple, str, bytes]:
|
||||||
self.assertNotIsInstance(sample(), MutableSequence)
|
self.assertNotIsInstance(sample(), MutableSequence)
|
||||||
|
|
|
@ -707,7 +707,7 @@ plain ol' Python and is guaranteed to be available.
|
||||||
|
|
||||||
>>> import builtins
|
>>> import builtins
|
||||||
>>> tests = doctest.DocTestFinder().find(builtins)
|
>>> tests = doctest.DocTestFinder().find(builtins)
|
||||||
>>> 830 < len(tests) < 850 # approximate number of objects with docstrings
|
>>> 830 < len(tests) < 860 # approximate number of objects with docstrings
|
||||||
True
|
True
|
||||||
>>> real_tests = [t for t in tests if len(t.examples) > 0]
|
>>> real_tests = [t for t in tests if len(t.examples) > 0]
|
||||||
>>> len(real_tests) # objects that actually have doctests
|
>>> len(real_tests) # objects that actually have doctests
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Make the buffer protocol accessible in Python code using the new
|
||||||
|
``__buffer__`` and ``__release_buffer__`` magic methods. See :pep:`688` for
|
||||||
|
details. Patch by Jelle Zijlstra.
|
|
@ -169,7 +169,7 @@
|
||||||
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
|
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
|
||||||
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
||||||
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
|
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
|
||||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c
|
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c
|
||||||
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
||||||
|
|
||||||
# Some testing modules MUST be built as shared libraries.
|
# Some testing modules MUST be built as shared libraries.
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
/* Test PEP 688 - Buffers */
|
||||||
|
|
||||||
|
#include "parts.h"
|
||||||
|
|
||||||
|
#include "structmember.h" // PyMemberDef
|
||||||
|
#include <stddef.h> // offsetof
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
PyObject *obj;
|
||||||
|
Py_ssize_t references;
|
||||||
|
} testBufObject;
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
testbuf_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
PyObject *obj = PyBytes_FromString("test");
|
||||||
|
if (obj == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
testBufObject *self = (testBufObject *)type->tp_alloc(type, 0);
|
||||||
|
if (self == NULL) {
|
||||||
|
Py_DECREF(obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
self->obj = obj;
|
||||||
|
self->references = 0;
|
||||||
|
return (PyObject *)self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
testbuf_traverse(testBufObject *self, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
Py_VISIT(self->obj);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
testbuf_clear(testBufObject *self)
|
||||||
|
{
|
||||||
|
Py_CLEAR(self->obj);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
testbuf_dealloc(testBufObject *self)
|
||||||
|
{
|
||||||
|
PyObject_GC_UnTrack(self);
|
||||||
|
Py_XDECREF(self->obj);
|
||||||
|
Py_TYPE(self)->tp_free((PyObject *) self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
testbuf_getbuf(testBufObject *self, Py_buffer *view, int flags)
|
||||||
|
{
|
||||||
|
int buf = PyObject_GetBuffer(self->obj, view, flags);
|
||||||
|
Py_SETREF(view->obj, Py_NewRef(self));
|
||||||
|
self->references++;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
testbuf_releasebuf(testBufObject *self, Py_buffer *view)
|
||||||
|
{
|
||||||
|
self->references--;
|
||||||
|
assert(self->references >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyBufferProcs testbuf_as_buffer = {
|
||||||
|
.bf_getbuffer = (getbufferproc) testbuf_getbuf,
|
||||||
|
.bf_releasebuffer = (releasebufferproc) testbuf_releasebuf,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct PyMemberDef testbuf_members[] = {
|
||||||
|
{"references", T_PYSSIZET, offsetof(testBufObject, references), READONLY},
|
||||||
|
{NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyTypeObject testBufType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
.tp_name = "testBufType",
|
||||||
|
.tp_basicsize = sizeof(testBufObject),
|
||||||
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||||
|
.tp_new = testbuf_new,
|
||||||
|
.tp_dealloc = (destructor) testbuf_dealloc,
|
||||||
|
.tp_traverse = (traverseproc) testbuf_traverse,
|
||||||
|
.tp_clear = (inquiry) testbuf_clear,
|
||||||
|
.tp_as_buffer = &testbuf_as_buffer,
|
||||||
|
.tp_members = testbuf_members
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyTestCapi_Init_Buffer(PyObject *m) {
|
||||||
|
if (PyType_Ready(&testBufType) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (PyModule_AddObjectRef(m, "testBuf", (PyObject *)&testBufType)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ int _PyTestCapi_Init_Float(PyObject *module);
|
||||||
int _PyTestCapi_Init_Structmember(PyObject *module);
|
int _PyTestCapi_Init_Structmember(PyObject *module);
|
||||||
int _PyTestCapi_Init_Exceptions(PyObject *module);
|
int _PyTestCapi_Init_Exceptions(PyObject *module);
|
||||||
int _PyTestCapi_Init_Code(PyObject *module);
|
int _PyTestCapi_Init_Code(PyObject *module);
|
||||||
|
int _PyTestCapi_Init_Buffer(PyObject *module);
|
||||||
int _PyTestCapi_Init_PyOS(PyObject *module);
|
int _PyTestCapi_Init_PyOS(PyObject *module);
|
||||||
int _PyTestCapi_Init_Immortal(PyObject *module);
|
int _PyTestCapi_Init_Immortal(PyObject *module);
|
||||||
|
|
||||||
|
|
|
@ -3959,7 +3959,6 @@ static PyTypeObject MyList_Type = {
|
||||||
MyList_new, /* tp_new */
|
MyList_new, /* tp_new */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Test PEP 560 */
|
/* Test PEP 560 */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -4310,6 +4309,9 @@ PyInit__testcapi(void)
|
||||||
if (_PyTestCapi_Init_Code(m) < 0) {
|
if (_PyTestCapi_Init_Code(m) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (_PyTestCapi_Init_Buffer(m) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (_PyTestCapi_Init_PyOS(m) < 0) {
|
if (_PyTestCapi_Init_PyOS(m) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,66 @@ exit:
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(memoryview__from_flags__doc__,
|
||||||
|
"_from_flags($type, /, object, flags)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Create a new memoryview object which references the given object.");
|
||||||
|
|
||||||
|
#define MEMORYVIEW__FROM_FLAGS_METHODDEF \
|
||||||
|
{"_from_flags", _PyCFunction_CAST(memoryview__from_flags), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, memoryview__from_flags__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
memoryview__from_flags_impl(PyTypeObject *type, PyObject *object, int flags);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
memoryview__from_flags(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
|
||||||
|
|
||||||
|
#define NUM_KEYWORDS 2
|
||||||
|
static struct {
|
||||||
|
PyGC_Head _this_is_not_used;
|
||||||
|
PyObject_VAR_HEAD
|
||||||
|
PyObject *ob_item[NUM_KEYWORDS];
|
||||||
|
} _kwtuple = {
|
||||||
|
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
|
||||||
|
.ob_item = { &_Py_ID(object), &_Py_ID(flags), },
|
||||||
|
};
|
||||||
|
#undef NUM_KEYWORDS
|
||||||
|
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
|
||||||
|
|
||||||
|
#else // !Py_BUILD_CORE
|
||||||
|
# define KWTUPLE NULL
|
||||||
|
#endif // !Py_BUILD_CORE
|
||||||
|
|
||||||
|
static const char * const _keywords[] = {"object", "flags", NULL};
|
||||||
|
static _PyArg_Parser _parser = {
|
||||||
|
.keywords = _keywords,
|
||||||
|
.fname = "_from_flags",
|
||||||
|
.kwtuple = KWTUPLE,
|
||||||
|
};
|
||||||
|
#undef KWTUPLE
|
||||||
|
PyObject *argsbuf[2];
|
||||||
|
PyObject *object;
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf);
|
||||||
|
if (!args) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
object = args[0];
|
||||||
|
flags = _PyLong_AsInt(args[1]);
|
||||||
|
if (flags == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = memoryview__from_flags_impl(type, object, flags);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(memoryview_release__doc__,
|
PyDoc_STRVAR(memoryview_release__doc__,
|
||||||
"release($self, /)\n"
|
"release($self, /)\n"
|
||||||
"--\n"
|
"--\n"
|
||||||
|
@ -356,4 +416,4 @@ skip_optional_pos:
|
||||||
exit:
|
exit:
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
/*[clinic end generated code: output=a832f2fc44e4794c input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=01613814112cedd7 input=a9049054013a1b77]*/
|
||||||
|
|
|
@ -85,7 +85,7 @@ mbuf_alloc(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_PyManagedBuffer_FromObject(PyObject *base)
|
_PyManagedBuffer_FromObject(PyObject *base, int flags)
|
||||||
{
|
{
|
||||||
_PyManagedBufferObject *mbuf;
|
_PyManagedBufferObject *mbuf;
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ _PyManagedBuffer_FromObject(PyObject *base)
|
||||||
if (mbuf == NULL)
|
if (mbuf == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (PyObject_GetBuffer(base, &mbuf->master, PyBUF_FULL_RO) < 0) {
|
if (PyObject_GetBuffer(base, &mbuf->master, flags) < 0) {
|
||||||
mbuf->master.obj = NULL;
|
mbuf->master.obj = NULL;
|
||||||
Py_DECREF(mbuf);
|
Py_DECREF(mbuf);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -777,11 +777,12 @@ PyMemoryView_FromBuffer(const Py_buffer *info)
|
||||||
return mv;
|
return mv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a memoryview from an object that implements the buffer protocol.
|
/* Create a memoryview from an object that implements the buffer protocol,
|
||||||
|
using the given flags.
|
||||||
If the object is a memoryview, the new memoryview must be registered
|
If the object is a memoryview, the new memoryview must be registered
|
||||||
with the same managed buffer. Otherwise, a new managed buffer is created. */
|
with the same managed buffer. Otherwise, a new managed buffer is created. */
|
||||||
PyObject *
|
PyObject *
|
||||||
PyMemoryView_FromObject(PyObject *v)
|
PyMemoryView_FromObjectAndFlags(PyObject *v, int flags)
|
||||||
{
|
{
|
||||||
_PyManagedBufferObject *mbuf;
|
_PyManagedBufferObject *mbuf;
|
||||||
|
|
||||||
|
@ -792,7 +793,7 @@ PyMemoryView_FromObject(PyObject *v)
|
||||||
}
|
}
|
||||||
else if (PyObject_CheckBuffer(v)) {
|
else if (PyObject_CheckBuffer(v)) {
|
||||||
PyObject *ret;
|
PyObject *ret;
|
||||||
mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(v);
|
mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(v, flags);
|
||||||
if (mbuf == NULL)
|
if (mbuf == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
ret = mbuf_add_view(mbuf, NULL);
|
ret = mbuf_add_view(mbuf, NULL);
|
||||||
|
@ -805,6 +806,14 @@ PyMemoryView_FromObject(PyObject *v)
|
||||||
Py_TYPE(v)->tp_name);
|
Py_TYPE(v)->tp_name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
/* Create a memoryview from an object that implements the buffer protocol.
|
||||||
|
If the object is a memoryview, the new memoryview must be registered
|
||||||
|
with the same managed buffer. Otherwise, a new managed buffer is created. */
|
||||||
|
PyObject *
|
||||||
|
PyMemoryView_FromObject(PyObject *v)
|
||||||
|
{
|
||||||
|
return PyMemoryView_FromObjectAndFlags(v, PyBUF_FULL_RO);
|
||||||
|
}
|
||||||
|
|
||||||
/* Copy the format string from a base object that might vanish. */
|
/* Copy the format string from a base object that might vanish. */
|
||||||
static int
|
static int
|
||||||
|
@ -851,7 +860,7 @@ memory_from_contiguous_copy(const Py_buffer *src, char order)
|
||||||
if (bytes == NULL)
|
if (bytes == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(bytes);
|
mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(bytes, PyBUF_FULL_RO);
|
||||||
Py_DECREF(bytes);
|
Py_DECREF(bytes);
|
||||||
if (mbuf == NULL)
|
if (mbuf == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -968,6 +977,24 @@ memoryview_impl(PyTypeObject *type, PyObject *object)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
@classmethod
|
||||||
|
memoryview._from_flags
|
||||||
|
|
||||||
|
object: object
|
||||||
|
flags: int
|
||||||
|
|
||||||
|
Create a new memoryview object which references the given object.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
memoryview__from_flags_impl(PyTypeObject *type, PyObject *object, int flags)
|
||||||
|
/*[clinic end generated code: output=bf71f9906c266ee2 input=f5f82fd0e744356b]*/
|
||||||
|
{
|
||||||
|
return PyMemoryView_FromObjectAndFlags(object, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
/* Previously in abstract.c */
|
/* Previously in abstract.c */
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
@ -3184,6 +3211,7 @@ static PyMethodDef memory_methods[] = {
|
||||||
MEMORYVIEW_TOLIST_METHODDEF
|
MEMORYVIEW_TOLIST_METHODDEF
|
||||||
MEMORYVIEW_CAST_METHODDEF
|
MEMORYVIEW_CAST_METHODDEF
|
||||||
MEMORYVIEW_TOREADONLY_METHODDEF
|
MEMORYVIEW_TOREADONLY_METHODDEF
|
||||||
|
MEMORYVIEW__FROM_FLAGS_METHODDEF
|
||||||
{"__enter__", memory_enter, METH_NOARGS, NULL},
|
{"__enter__", memory_enter, METH_NOARGS, NULL},
|
||||||
{"__exit__", memory_exit, METH_VARARGS, NULL},
|
{"__exit__", memory_exit, METH_VARARGS, NULL},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
|
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
|
||||||
#include "pycore_pystate.h" // _PyThreadState_GET()
|
#include "pycore_pystate.h" // _PyThreadState_GET()
|
||||||
#include "pycore_symtable.h" // PySTEntry_Type
|
#include "pycore_symtable.h" // PySTEntry_Type
|
||||||
|
#include "pycore_typeobject.h" // _PyBufferWrapper_Type
|
||||||
#include "pycore_unionobject.h" // _PyUnion_Type
|
#include "pycore_unionobject.h" // _PyUnion_Type
|
||||||
#include "pycore_interpreteridobject.h" // _PyInterpreterID_Type
|
#include "pycore_interpreteridobject.h" // _PyInterpreterID_Type
|
||||||
|
|
||||||
|
@ -2084,6 +2085,7 @@ static PyTypeObject* static_types[] = {
|
||||||
&_PyAsyncGenASend_Type,
|
&_PyAsyncGenASend_Type,
|
||||||
&_PyAsyncGenAThrow_Type,
|
&_PyAsyncGenAThrow_Type,
|
||||||
&_PyAsyncGenWrappedValue_Type,
|
&_PyAsyncGenWrappedValue_Type,
|
||||||
|
&_PyBufferWrapper_Type,
|
||||||
&_PyContextTokenMissing_Type,
|
&_PyContextTokenMissing_Type,
|
||||||
&_PyCoroWrapper_Type,
|
&_PyCoroWrapper_Type,
|
||||||
&_Py_GenericAliasIterType,
|
&_Py_GenericAliasIterType,
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "pycore_symtable.h" // _Py_Mangle()
|
#include "pycore_symtable.h" // _Py_Mangle()
|
||||||
#include "pycore_dict.h" // _PyDict_KeysSize()
|
#include "pycore_dict.h" // _PyDict_KeysSize()
|
||||||
#include "pycore_initconfig.h" // _PyStatus_OK()
|
#include "pycore_initconfig.h" // _PyStatus_OK()
|
||||||
|
#include "pycore_memoryobject.h" // PyMemoryView_FromObjectAndFlags()
|
||||||
#include "pycore_moduleobject.h" // _PyModule_GetDef()
|
#include "pycore_moduleobject.h" // _PyModule_GetDef()
|
||||||
#include "pycore_object.h" // _PyType_HasFeature()
|
#include "pycore_object.h" // _PyType_HasFeature()
|
||||||
#include "pycore_long.h" // _PyLong_IsNegative()
|
#include "pycore_long.h" // _PyLong_IsNegative()
|
||||||
|
@ -8059,6 +8060,58 @@ wrap_descr_delete(PyObject *self, PyObject *args, void *wrapped)
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
wrap_buffer(PyObject *self, PyObject *args, void *wrapped)
|
||||||
|
{
|
||||||
|
PyObject *arg = NULL;
|
||||||
|
|
||||||
|
if (!PyArg_UnpackTuple(args, "", 1, 1, &arg)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_ssize_t flags = PyNumber_AsSsize_t(arg, PyExc_OverflowError);
|
||||||
|
if (flags == -1 && PyErr_Occurred()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (flags > INT_MAX) {
|
||||||
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
|
"buffer flags too large");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PyMemoryView_FromObjectAndFlags(self, Py_SAFE_DOWNCAST(flags, Py_ssize_t, int));
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
wrap_releasebuffer(PyObject *self, PyObject *args, void *wrapped)
|
||||||
|
{
|
||||||
|
PyObject *arg = NULL;
|
||||||
|
if (!PyArg_UnpackTuple(args, "", 1, 1, &arg)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!PyMemoryView_Check(arg)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"expected a memoryview object");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyMemoryViewObject *mview = (PyMemoryViewObject *)arg;
|
||||||
|
if (mview->view.obj != self) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"memoryview's buffer is not this object");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (mview->flags & _Py_MEMORYVIEW_RELEASED) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"memoryview's buffer has already been released");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *res = PyObject_CallMethodNoArgs((PyObject *)mview, &_Py_ID(release));
|
||||||
|
if (res == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_DECREF(res);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
wrap_init(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds)
|
wrap_init(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds)
|
||||||
{
|
{
|
||||||
|
@ -8895,6 +8948,132 @@ slot_tp_finalize(PyObject *self)
|
||||||
PyErr_SetRaisedException(exc);
|
PyErr_SetRaisedException(exc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct _PyBufferWrapper {
|
||||||
|
PyObject_HEAD
|
||||||
|
PyObject *mv;
|
||||||
|
PyObject *obj;
|
||||||
|
} PyBufferWrapper;
|
||||||
|
|
||||||
|
static int
|
||||||
|
bufferwrapper_traverse(PyBufferWrapper *self, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
Py_VISIT(self->mv);
|
||||||
|
Py_VISIT(self->obj);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bufferwrapper_dealloc(PyObject *self)
|
||||||
|
{
|
||||||
|
PyBufferWrapper *bw = (PyBufferWrapper *)self;
|
||||||
|
|
||||||
|
_PyObject_GC_UNTRACK(self);
|
||||||
|
Py_XDECREF(bw->mv);
|
||||||
|
Py_XDECREF(bw->obj);
|
||||||
|
Py_TYPE(self)->tp_free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bufferwrapper_releasebuf(PyObject *self, Py_buffer *view)
|
||||||
|
{
|
||||||
|
PyBufferWrapper *bw = (PyBufferWrapper *)self;
|
||||||
|
|
||||||
|
assert(PyMemoryView_Check(bw->mv));
|
||||||
|
Py_TYPE(bw->mv)->tp_as_buffer->bf_releasebuffer(bw->mv, view);
|
||||||
|
if (Py_TYPE(bw->obj)->tp_as_buffer != NULL
|
||||||
|
&& Py_TYPE(bw->obj)->tp_as_buffer->bf_releasebuffer != NULL) {
|
||||||
|
Py_TYPE(bw->obj)->tp_as_buffer->bf_releasebuffer(bw->obj, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyBufferProcs bufferwrapper_as_buffer = {
|
||||||
|
.bf_releasebuffer = bufferwrapper_releasebuf,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
PyTypeObject _PyBufferWrapper_Type = {
|
||||||
|
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||||
|
.tp_name = "_buffer_wrapper",
|
||||||
|
.tp_basicsize = sizeof(PyBufferWrapper),
|
||||||
|
.tp_alloc = PyType_GenericAlloc,
|
||||||
|
.tp_free = PyObject_GC_Del,
|
||||||
|
.tp_traverse = (traverseproc)bufferwrapper_traverse,
|
||||||
|
.tp_dealloc = bufferwrapper_dealloc,
|
||||||
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||||
|
.tp_as_buffer = &bufferwrapper_as_buffer,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
slot_bf_getbuffer(PyObject *self, Py_buffer *buffer, int flags)
|
||||||
|
{
|
||||||
|
PyObject *flags_obj = PyLong_FromLong(flags);
|
||||||
|
if (flags_obj == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
PyBufferWrapper *wrapper = NULL;
|
||||||
|
PyObject *stack[2] = {self, flags_obj};
|
||||||
|
PyObject *ret = vectorcall_method(&_Py_ID(__buffer__), stack, 2);
|
||||||
|
if (ret == NULL) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (!PyMemoryView_Check(ret)) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"__buffer__ returned non-memoryview object");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyObject_GetBuffer(ret, buffer, flags) < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
assert(buffer->obj == ret);
|
||||||
|
|
||||||
|
wrapper = PyObject_GC_New(PyBufferWrapper, &_PyBufferWrapper_Type);
|
||||||
|
if (wrapper == NULL) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
wrapper->mv = ret;
|
||||||
|
wrapper->obj = Py_NewRef(self);
|
||||||
|
_PyObject_GC_TRACK(wrapper);
|
||||||
|
|
||||||
|
buffer->obj = (PyObject *)wrapper;
|
||||||
|
Py_DECREF(ret);
|
||||||
|
Py_DECREF(flags_obj);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
Py_XDECREF(wrapper);
|
||||||
|
Py_XDECREF(ret);
|
||||||
|
Py_DECREF(flags_obj);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer)
|
||||||
|
{
|
||||||
|
PyObject *mv;
|
||||||
|
if (Py_TYPE(buffer->obj) == &_PyBufferWrapper_Type) {
|
||||||
|
// Make sure we pass the same memoryview to
|
||||||
|
// __release_buffer__() that __buffer__() returned.
|
||||||
|
mv = Py_NewRef(((PyBufferWrapper *)buffer->obj)->mv);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mv = PyMemoryView_FromBuffer(buffer);
|
||||||
|
if (mv == NULL) {
|
||||||
|
PyErr_WriteUnraisable(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PyObject *stack[2] = {self, mv};
|
||||||
|
PyObject *ret = vectorcall_method(&_Py_ID(__release_buffer__), stack, 2);
|
||||||
|
Py_DECREF(mv);
|
||||||
|
if (ret == NULL) {
|
||||||
|
PyErr_WriteUnraisable(self);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Py_DECREF(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
slot_am_await(PyObject *self)
|
slot_am_await(PyObject *self)
|
||||||
{
|
{
|
||||||
|
@ -8962,6 +9141,7 @@ an all-zero entry.
|
||||||
|
|
||||||
#undef TPSLOT
|
#undef TPSLOT
|
||||||
#undef FLSLOT
|
#undef FLSLOT
|
||||||
|
#undef BUFSLOT
|
||||||
#undef AMSLOT
|
#undef AMSLOT
|
||||||
#undef ETSLOT
|
#undef ETSLOT
|
||||||
#undef SQSLOT
|
#undef SQSLOT
|
||||||
|
@ -8981,6 +9161,8 @@ an all-zero entry.
|
||||||
#define ETSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
|
#define ETSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
|
||||||
{#NAME, offsetof(PyHeapTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \
|
{#NAME, offsetof(PyHeapTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \
|
||||||
PyDoc_STR(DOC), .name_strobj = &_Py_ID(NAME) }
|
PyDoc_STR(DOC), .name_strobj = &_Py_ID(NAME) }
|
||||||
|
#define BUFSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
|
||||||
|
ETSLOT(NAME, as_buffer.SLOT, FUNCTION, WRAPPER, DOC)
|
||||||
#define AMSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
|
#define AMSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
|
||||||
ETSLOT(NAME, as_async.SLOT, FUNCTION, WRAPPER, DOC)
|
ETSLOT(NAME, as_async.SLOT, FUNCTION, WRAPPER, DOC)
|
||||||
#define SQSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
|
#define SQSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
|
||||||
|
@ -9062,6 +9244,13 @@ static pytype_slotdef slotdefs[] = {
|
||||||
"Create and return new object. See help(type) for accurate signature."),
|
"Create and return new object. See help(type) for accurate signature."),
|
||||||
TPSLOT(__del__, tp_finalize, slot_tp_finalize, (wrapperfunc)wrap_del, ""),
|
TPSLOT(__del__, tp_finalize, slot_tp_finalize, (wrapperfunc)wrap_del, ""),
|
||||||
|
|
||||||
|
BUFSLOT(__buffer__, bf_getbuffer, slot_bf_getbuffer, wrap_buffer,
|
||||||
|
"__buffer__($self, flags, /)\n--\n\n"
|
||||||
|
"Return a buffer object that exposes the underlying memory of the object."),
|
||||||
|
BUFSLOT(__release_buffer__, bf_releasebuffer, slot_bf_releasebuffer, wrap_releasebuffer,
|
||||||
|
"__release_buffer__($self, /)\n--\n\n"
|
||||||
|
"Release the buffer object that exposes the underlying memory of the object."),
|
||||||
|
|
||||||
AMSLOT(__await__, am_await, slot_am_await, wrap_unaryfunc,
|
AMSLOT(__await__, am_await, slot_am_await, wrap_unaryfunc,
|
||||||
"__await__($self, /)\n--\n\nReturn an iterator to be used in await expression."),
|
"__await__($self, /)\n--\n\nReturn an iterator to be used in await expression."),
|
||||||
AMSLOT(__aiter__, am_aiter, slot_am_aiter, wrap_unaryfunc,
|
AMSLOT(__aiter__, am_aiter, slot_am_aiter, wrap_unaryfunc,
|
||||||
|
@ -9208,8 +9397,12 @@ slotptr(PyTypeObject *type, int ioffset)
|
||||||
|
|
||||||
/* Note: this depends on the order of the members of PyHeapTypeObject! */
|
/* Note: this depends on the order of the members of PyHeapTypeObject! */
|
||||||
assert(offset >= 0);
|
assert(offset >= 0);
|
||||||
assert((size_t)offset < offsetof(PyHeapTypeObject, as_buffer));
|
assert((size_t)offset < offsetof(PyHeapTypeObject, ht_name));
|
||||||
if ((size_t)offset >= offsetof(PyHeapTypeObject, as_sequence)) {
|
if ((size_t)offset >= offsetof(PyHeapTypeObject, as_buffer)) {
|
||||||
|
ptr = (char *)type->tp_as_buffer;
|
||||||
|
offset -= offsetof(PyHeapTypeObject, as_buffer);
|
||||||
|
}
|
||||||
|
else if ((size_t)offset >= offsetof(PyHeapTypeObject, as_sequence)) {
|
||||||
ptr = (char *)type->tp_as_sequence;
|
ptr = (char *)type->tp_as_sequence;
|
||||||
offset -= offsetof(PyHeapTypeObject, as_sequence);
|
offset -= offsetof(PyHeapTypeObject, as_sequence);
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,7 @@
|
||||||
<ClCompile Include="..\Modules\_testcapi\structmember.c" />
|
<ClCompile Include="..\Modules\_testcapi\structmember.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\exceptions.c" />
|
<ClCompile Include="..\Modules\_testcapi\exceptions.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\code.c" />
|
<ClCompile Include="..\Modules\_testcapi\code.c" />
|
||||||
|
<ClCompile Include="..\Modules\_testcapi\buffer.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\pyos.c" />
|
<ClCompile Include="..\Modules\_testcapi\pyos.c" />
|
||||||
<ClCompile Include="..\Modules\_testcapi\immortal.c" />
|
<ClCompile Include="..\Modules\_testcapi\immortal.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -60,6 +60,9 @@
|
||||||
<ClCompile Include="..\Modules\_testcapi\code.c">
|
<ClCompile Include="..\Modules\_testcapi\code.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\Modules\_testcapi\buffer.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\Modules\_testcapi\pyos.c">
|
<ClCompile Include="..\Modules\_testcapi\pyos.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
|
@ -121,6 +121,8 @@ IDENTIFIERS = [
|
||||||
'__xor__',
|
'__xor__',
|
||||||
'__divmod__',
|
'__divmod__',
|
||||||
'__rdivmod__',
|
'__rdivmod__',
|
||||||
|
'__buffer__',
|
||||||
|
'__release_buffer__',
|
||||||
]
|
]
|
||||||
|
|
||||||
NON_GENERATED_IMMORTAL_OBJECTS = [
|
NON_GENERATED_IMMORTAL_OBJECTS = [
|
||||||
|
|
|
@ -86,6 +86,7 @@ Objects/sliceobject.c - PyEllipsis_Type -
|
||||||
Objects/sliceobject.c - PySlice_Type -
|
Objects/sliceobject.c - PySlice_Type -
|
||||||
Objects/tupleobject.c - PyTupleIter_Type -
|
Objects/tupleobject.c - PyTupleIter_Type -
|
||||||
Objects/tupleobject.c - PyTuple_Type -
|
Objects/tupleobject.c - PyTuple_Type -
|
||||||
|
Objects/typeobject.c - _PyBufferWrapper_Type -
|
||||||
Objects/typeobject.c - PyBaseObject_Type -
|
Objects/typeobject.c - PyBaseObject_Type -
|
||||||
Objects/typeobject.c - PySuper_Type -
|
Objects/typeobject.c - PySuper_Type -
|
||||||
Objects/typeobject.c - PyType_Type -
|
Objects/typeobject.c - PyType_Type -
|
||||||
|
|
Can't render this file because it has a wrong number of fields in line 4.
|
|
@ -404,6 +404,7 @@ Modules/_testbuffer.c ndarray_memoryview_from_buffer strides -
|
||||||
Modules/_testbuffer.c ndarray_memoryview_from_buffer suboffsets -
|
Modules/_testbuffer.c ndarray_memoryview_from_buffer suboffsets -
|
||||||
Modules/_testbuffer.c ndarray_push kwlist -
|
Modules/_testbuffer.c ndarray_push kwlist -
|
||||||
Modules/_testbuffer.c staticarray_init kwlist -
|
Modules/_testbuffer.c staticarray_init kwlist -
|
||||||
|
Modules/_testcapi/buffer.c - testBufType -
|
||||||
Modules/_testcapi/code.c get_code_extra_index key -
|
Modules/_testcapi/code.c get_code_extra_index key -
|
||||||
Modules/_testcapi/datetime.c - test_run_counter -
|
Modules/_testcapi/datetime.c - test_run_counter -
|
||||||
Modules/_testcapi/exceptions.c - PyRecursingInfinitelyError_Type -
|
Modules/_testcapi/exceptions.c - PyRecursingInfinitelyError_Type -
|
||||||
|
|
Can't render this file because it has a wrong number of fields in line 4.
|
Loading…
Reference in New Issue