summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJelle Zijlstra <jelle.zijlstra@gmail.com>2023-05-04 14:59:46 (GMT)
committerGitHub <noreply@github.com>2023-05-04 14:59:46 (GMT)
commit04f673327530f47f002e784459037231de478412 (patch)
tree56b29e35a1147b9655b5f4b4337f8a2f905b0e3f
parentb17d32c1142d16a5fea0c95bce185bf9be696491 (diff)
downloadcpython-04f673327530f47f002e784459037231de478412.zip
cpython-04f673327530f47f002e784459037231de478412.tar.gz
cpython-04f673327530f47f002e784459037231de478412.tar.bz2
gh-102500: Implement PEP 688 (#102521)
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
-rw-r--r--Include/internal/pycore_global_objects_fini_generated.h3
-rw-r--r--Include/internal/pycore_global_strings.h3
-rw-r--r--Include/internal/pycore_memoryobject.h17
-rw-r--r--Include/internal/pycore_runtime_init_generated.h3
-rw-r--r--Include/internal/pycore_typeobject.h2
-rw-r--r--Include/internal/pycore_unicodeobject_generated.h9
-rw-r--r--Include/pybuffer.h2
-rw-r--r--Lib/_collections_abc.py17
-rw-r--r--Lib/inspect.py23
-rw-r--r--Lib/test/test_buffer.py142
-rw-r--r--Lib/test/test_collections.py11
-rw-r--r--Lib/test/test_doctest.py2
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-03-07-17-37-00.gh-issue-102500.RUSQhz.rst3
-rw-r--r--Modules/Setup.stdlib.in2
-rw-r--r--Modules/_testcapi/buffer.c102
-rw-r--r--Modules/_testcapi/parts.h1
-rw-r--r--Modules/_testcapimodule.c4
-rw-r--r--Objects/clinic/memoryobject.c.h62
-rw-r--r--Objects/memoryobject.c40
-rw-r--r--Objects/object.c2
-rw-r--r--Objects/typeobject.c197
-rw-r--r--PCbuild/_testcapi.vcxproj1
-rw-r--r--PCbuild/_testcapi.vcxproj.filters3
-rw-r--r--Tools/build/generate_global_objects.py2
-rw-r--r--Tools/c-analyzer/cpython/globals-to-fix.tsv1
-rw-r--r--Tools/c-analyzer/cpython/ignored.tsv1
26 files changed, 640 insertions, 15 deletions
diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h
index f0740b6..9377fd8 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -593,6 +593,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__await__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bases__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bool__));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__buffer__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__build_class__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__builtins__));
_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(__reduce__));
_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(__reversed__));
_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(registry));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(rel_tol));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(release));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(reload));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(repl));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(replace));
diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h
index 234d5e2..ed9b2bb 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -81,6 +81,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__await__)
STRUCT_FOR_ID(__bases__)
STRUCT_FOR_ID(__bool__)
+ STRUCT_FOR_ID(__buffer__)
STRUCT_FOR_ID(__build_class__)
STRUCT_FOR_ID(__builtins__)
STRUCT_FOR_ID(__bytes__)
@@ -180,6 +181,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__rdivmod__)
STRUCT_FOR_ID(__reduce__)
STRUCT_FOR_ID(__reduce_ex__)
+ STRUCT_FOR_ID(__release_buffer__)
STRUCT_FOR_ID(__repr__)
STRUCT_FOR_ID(__reversed__)
STRUCT_FOR_ID(__rfloordiv__)
@@ -610,6 +612,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(reducer_override)
STRUCT_FOR_ID(registry)
STRUCT_FOR_ID(rel_tol)
+ STRUCT_FOR_ID(release)
STRUCT_FOR_ID(reload)
STRUCT_FOR_ID(repl)
STRUCT_FOR_ID(replace)
diff --git a/Include/internal/pycore_memoryobject.h b/Include/internal/pycore_memoryobject.h
new file mode 100644
index 0000000..acc12c9
--- /dev/null
+++ b/Include/internal/pycore_memoryobject.h
@@ -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 */
diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h
index 16f2147..6ade8fb 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -587,6 +587,7 @@ extern "C" {
INIT_ID(__await__), \
INIT_ID(__bases__), \
INIT_ID(__bool__), \
+ INIT_ID(__buffer__), \
INIT_ID(__build_class__), \
INIT_ID(__builtins__), \
INIT_ID(__bytes__), \
@@ -686,6 +687,7 @@ extern "C" {
INIT_ID(__rdivmod__), \
INIT_ID(__reduce__), \
INIT_ID(__reduce_ex__), \
+ INIT_ID(__release_buffer__), \
INIT_ID(__repr__), \
INIT_ID(__reversed__), \
INIT_ID(__rfloordiv__), \
@@ -1116,6 +1118,7 @@ extern "C" {
INIT_ID(reducer_override), \
INIT_ID(registry), \
INIT_ID(rel_tol), \
+ INIT_ID(release), \
INIT_ID(reload), \
INIT_ID(repl), \
INIT_ID(replace), \
diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h
index 6a5ab7e..f42f8f6 100644
--- a/Include/internal/pycore_typeobject.h
+++ b/Include/internal/pycore_typeobject.h
@@ -138,6 +138,8 @@ _Py_type_getattro(PyTypeObject *type, PyObject *name);
PyObject *_Py_slot_tp_getattro(PyObject *self, PyObject *name);
PyObject *_Py_slot_tp_getattr_hook(PyObject *self, PyObject *name);
+PyAPI_DATA(PyTypeObject) _PyBufferWrapper_Type;
+
PyObject *
_PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found);
PyObject *
diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h
index cd41b73..0b33ea1 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -96,6 +96,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(__bool__);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
+ string = &_Py_ID(__buffer__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ _PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(__build_class__);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
@@ -393,6 +396,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(__reduce_ex__);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
+ string = &_Py_ID(__release_buffer__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ _PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(__repr__);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
@@ -1683,6 +1689,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(rel_tol);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
+ string = &_Py_ID(release);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ _PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(reload);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
diff --git a/Include/pybuffer.h b/Include/pybuffer.h
index bbac609..ca1c605 100644
--- a/Include/pybuffer.h
+++ b/Include/pybuffer.h
@@ -104,7 +104,7 @@ PyAPI_FUNC(void) PyBuffer_Release(Py_buffer *view);
/* Maximum number of dimensions */
#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_WRITABLE 0x0001
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index 9d7724c..2117190 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -49,7 +49,7 @@ __all__ = ["Awaitable", "Coroutine",
"Mapping", "MutableMapping",
"MappingView", "KeysView", "ItemsView", "ValuesView",
"Sequence", "MutableSequence",
- "ByteString",
+ "ByteString", "Buffer",
]
# This module has been renamed from collections.abc to _collections_abc to
@@ -439,6 +439,21 @@ class Collection(Sized, Iterable, Container):
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):
""" Represent `Callable[argtypes, resulttype]`.
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 92c2675..95da7fb 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -43,6 +43,7 @@ __all__ = [
"Attribute",
"BlockFinder",
"BoundArguments",
+ "BufferFlags",
"CORO_CLOSED",
"CORO_CREATED",
"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)
+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():
""" Logic for inspecting an object given at command line """
import argparse
diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py
index 098d2d9..b6e82ad 100644
--- a/Lib/test/test_buffer.py
+++ b/Lib/test/test_buffer.py
@@ -17,6 +17,7 @@ import contextlib
import unittest
from test import support
from test.support import os_helper
+import inspect
from itertools import permutations, product
from random import randrange, sample, choice
import warnings
@@ -4438,5 +4439,146 @@ class TestBufferProtocol(unittest.TestCase):
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__":
unittest.main()
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index fb568a4..8fc28a6 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -25,7 +25,7 @@ from collections.abc import Sized, Container, Callable, Collection
from collections.abc import Set, MutableSet
from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView
from collections.abc import Sequence, MutableSequence
-from collections.abc import ByteString
+from collections.abc import ByteString, Buffer
class TestUserObjects(unittest.TestCase):
@@ -1949,6 +1949,15 @@ class TestCollectionABCs(ABCTestCase):
self.assertFalse(issubclass(memoryview, ByteString))
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):
for sample in [tuple, str, bytes]:
self.assertNotIsInstance(sample(), MutableSequence)
diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py
index 3491d4c..542fcdb 100644
--- a/Lib/test/test_doctest.py
+++ b/Lib/test/test_doctest.py
@@ -707,7 +707,7 @@ plain ol' Python and is guaranteed to be available.
>>> import 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
>>> real_tests = [t for t in tests if len(t.examples) > 0]
>>> len(real_tests) # objects that actually have doctests
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-07-17-37-00.gh-issue-102500.RUSQhz.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-07-17-37-00.gh-issue-102500.RUSQhz.rst
new file mode 100644
index 0000000..e03113b
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-07-17-37-00.gh-issue-102500.RUSQhz.rst
@@ -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.
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index 6b48334..a7803cf 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -169,7 +169,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.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
# Some testing modules MUST be built as shared libraries.
diff --git a/Modules/_testcapi/buffer.c b/Modules/_testcapi/buffer.c
new file mode 100644
index 0000000..aff9a47
--- /dev/null
+++ b/Modules/_testcapi/buffer.c
@@ -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;
+}
diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h
index d75412d..663d4f2 100644
--- a/Modules/_testcapi/parts.h
+++ b/Modules/_testcapi/parts.h
@@ -38,6 +38,7 @@ int _PyTestCapi_Init_Float(PyObject *module);
int _PyTestCapi_Init_Structmember(PyObject *module);
int _PyTestCapi_Init_Exceptions(PyObject *module);
int _PyTestCapi_Init_Code(PyObject *module);
+int _PyTestCapi_Init_Buffer(PyObject *module);
int _PyTestCapi_Init_PyOS(PyObject *module);
int _PyTestCapi_Init_Immortal(PyObject *module);
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 1ecc442..38f4758 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -3959,7 +3959,6 @@ static PyTypeObject MyList_Type = {
MyList_new, /* tp_new */
};
-
/* Test PEP 560 */
typedef struct {
@@ -4310,6 +4309,9 @@ PyInit__testcapi(void)
if (_PyTestCapi_Init_Code(m) < 0) {
return NULL;
}
+ if (_PyTestCapi_Init_Buffer(m) < 0) {
+ return NULL;
+ }
if (_PyTestCapi_Init_PyOS(m) < 0) {
return NULL;
}
diff --git a/Objects/clinic/memoryobject.c.h b/Objects/clinic/memoryobject.c.h
index ff7b50b..25a2234 100644
--- a/Objects/clinic/memoryobject.c.h
+++ b/Objects/clinic/memoryobject.c.h
@@ -62,6 +62,66 @@ exit:
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__,
"release($self, /)\n"
"--\n"
@@ -356,4 +416,4 @@ skip_optional_pos:
exit:
return return_value;
}
-/*[clinic end generated code: output=a832f2fc44e4794c input=a9049054013a1b77]*/
+/*[clinic end generated code: output=01613814112cedd7 input=a9049054013a1b77]*/
diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c
index 34cc797..f008a8c 100644
--- a/Objects/memoryobject.c
+++ b/Objects/memoryobject.c
@@ -85,7 +85,7 @@ mbuf_alloc(void)
}
static PyObject *
-_PyManagedBuffer_FromObject(PyObject *base)
+_PyManagedBuffer_FromObject(PyObject *base, int flags)
{
_PyManagedBufferObject *mbuf;
@@ -93,7 +93,7 @@ _PyManagedBuffer_FromObject(PyObject *base)
if (mbuf == 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;
Py_DECREF(mbuf);
return NULL;
@@ -777,11 +777,12 @@ PyMemoryView_FromBuffer(const Py_buffer *info)
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
with the same managed buffer. Otherwise, a new managed buffer is created. */
PyObject *
-PyMemoryView_FromObject(PyObject *v)
+PyMemoryView_FromObjectAndFlags(PyObject *v, int flags)
{
_PyManagedBufferObject *mbuf;
@@ -792,7 +793,7 @@ PyMemoryView_FromObject(PyObject *v)
}
else if (PyObject_CheckBuffer(v)) {
PyObject *ret;
- mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(v);
+ mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(v, flags);
if (mbuf == NULL)
return NULL;
ret = mbuf_add_view(mbuf, NULL);
@@ -805,6 +806,14 @@ PyMemoryView_FromObject(PyObject *v)
Py_TYPE(v)->tp_name);
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. */
static int
@@ -851,7 +860,7 @@ memory_from_contiguous_copy(const Py_buffer *src, char order)
if (bytes == NULL)
return NULL;
- mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(bytes);
+ mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(bytes, PyBUF_FULL_RO);
Py_DECREF(bytes);
if (mbuf == 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 */
/****************************************************************************/
@@ -3184,6 +3211,7 @@ static PyMethodDef memory_methods[] = {
MEMORYVIEW_TOLIST_METHODDEF
MEMORYVIEW_CAST_METHODDEF
MEMORYVIEW_TOREADONLY_METHODDEF
+ MEMORYVIEW__FROM_FLAGS_METHODDEF
{"__enter__", memory_enter, METH_NOARGS, NULL},
{"__exit__", memory_exit, METH_VARARGS, NULL},
{NULL, NULL}
diff --git a/Objects/object.c b/Objects/object.c
index 41c52e2..a7c79c6 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -14,6 +14,7 @@
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_symtable.h" // PySTEntry_Type
+#include "pycore_typeobject.h" // _PyBufferWrapper_Type
#include "pycore_unionobject.h" // _PyUnion_Type
#include "pycore_interpreteridobject.h" // _PyInterpreterID_Type
@@ -2084,6 +2085,7 @@ static PyTypeObject* static_types[] = {
&_PyAsyncGenASend_Type,
&_PyAsyncGenAThrow_Type,
&_PyAsyncGenWrappedValue_Type,
+ &_PyBufferWrapper_Type,
&_PyContextTokenMissing_Type,
&_PyCoroWrapper_Type,
&_Py_GenericAliasIterType,
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 171c76a..456b10e 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -6,6 +6,7 @@
#include "pycore_symtable.h" // _Py_Mangle()
#include "pycore_dict.h" // _PyDict_KeysSize()
#include "pycore_initconfig.h" // _PyStatus_OK()
+#include "pycore_memoryobject.h" // PyMemoryView_FromObjectAndFlags()
#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_object.h" // _PyType_HasFeature()
#include "pycore_long.h" // _PyLong_IsNegative()
@@ -8060,6 +8061,58 @@ wrap_descr_delete(PyObject *self, PyObject *args, void *wrapped)
}
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 *
wrap_init(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds)
{
initproc func = (initproc)wrapped;
@@ -8895,6 +8948,132 @@ slot_tp_finalize(PyObject *self)
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 *
slot_am_await(PyObject *self)
{
@@ -8962,6 +9141,7 @@ an all-zero entry.
#undef TPSLOT
#undef FLSLOT
+#undef BUFSLOT
#undef AMSLOT
#undef ETSLOT
#undef SQSLOT
@@ -8981,6 +9161,8 @@ an all-zero entry.
#define ETSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
{#NAME, offsetof(PyHeapTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \
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) \
ETSLOT(NAME, as_async.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."),
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,
"__await__($self, /)\n--\n\nReturn an iterator to be used in await expression."),
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! */
assert(offset >= 0);
- assert((size_t)offset < offsetof(PyHeapTypeObject, as_buffer));
- if ((size_t)offset >= offsetof(PyHeapTypeObject, as_sequence)) {
+ assert((size_t)offset < offsetof(PyHeapTypeObject, ht_name));
+ 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;
offset -= offsetof(PyHeapTypeObject, as_sequence);
}
diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj
index 56448b6..350f97f 100644
--- a/PCbuild/_testcapi.vcxproj
+++ b/PCbuild/_testcapi.vcxproj
@@ -110,6 +110,7 @@
<ClCompile Include="..\Modules\_testcapi\structmember.c" />
<ClCompile Include="..\Modules\_testcapi\exceptions.c" />
<ClCompile Include="..\Modules\_testcapi\code.c" />
+ <ClCompile Include="..\Modules\_testcapi\buffer.c" />
<ClCompile Include="..\Modules\_testcapi\pyos.c" />
<ClCompile Include="..\Modules\_testcapi\immortal.c" />
</ItemGroup>
diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters
index 297c9ce..af80f1e 100644
--- a/PCbuild/_testcapi.vcxproj.filters
+++ b/PCbuild/_testcapi.vcxproj.filters
@@ -60,6 +60,9 @@
<ClCompile Include="..\Modules\_testcapi\code.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\Modules\_testcapi\buffer.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\Modules\_testcapi\pyos.c">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/Tools/build/generate_global_objects.py b/Tools/build/generate_global_objects.py
index c278177..ded19ee 100644
--- a/Tools/build/generate_global_objects.py
+++ b/Tools/build/generate_global_objects.py
@@ -121,6 +121,8 @@ IDENTIFIERS = [
'__xor__',
'__divmod__',
'__rdivmod__',
+ '__buffer__',
+ '__release_buffer__',
]
NON_GENERATED_IMMORTAL_OBJECTS = [
diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv
index 4dfbbe7..165bd74 100644
--- a/Tools/c-analyzer/cpython/globals-to-fix.tsv
+++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv
@@ -86,6 +86,7 @@ Objects/sliceobject.c - PyEllipsis_Type -
Objects/sliceobject.c - PySlice_Type -
Objects/tupleobject.c - PyTupleIter_Type -
Objects/tupleobject.c - PyTuple_Type -
+Objects/typeobject.c - _PyBufferWrapper_Type -
Objects/typeobject.c - PyBaseObject_Type -
Objects/typeobject.c - PySuper_Type -
Objects/typeobject.c - PyType_Type -
diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv
index 7a5d7d4..fee493f 100644
--- a/Tools/c-analyzer/cpython/ignored.tsv
+++ b/Tools/c-analyzer/cpython/ignored.tsv
@@ -404,6 +404,7 @@ Modules/_testbuffer.c ndarray_memoryview_from_buffer strides -
Modules/_testbuffer.c ndarray_memoryview_from_buffer suboffsets -
Modules/_testbuffer.c ndarray_push kwlist -
Modules/_testbuffer.c staticarray_init kwlist -
+Modules/_testcapi/buffer.c - testBufType -
Modules/_testcapi/code.c get_code_extra_index key -
Modules/_testcapi/datetime.c - test_run_counter -
Modules/_testcapi/exceptions.c - PyRecursingInfinitelyError_Type -