python-peps/pep-0688/index.html

574 lines
52 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 688 Making the buffer protocol accessible in Python | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0688/">
<link rel="stylesheet" href="../_static/style.css" type="text/css">
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
<meta property="og:title" content='PEP 688 Making the buffer protocol accessible in Python | peps.python.org'>
<meta property="og:description" content="This PEP proposes a Python-level API for the buffer protocol, which is currently accessible only to C code. This allows type checkers to evaluate whether objects implement the protocol.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0688/">
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
<meta property="og:image:alt" content="Python PEPs">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta name="description" content="This PEP proposes a Python-level API for the buffer protocol, which is currently accessible only to C code. This allows type checkers to evaluate whether objects implement the protocol.">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 688</li>
</ul>
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
</button>
</header>
<article>
<section id="pep-content">
<h1 class="page-title">PEP 688 Making the buffer protocol accessible in Python</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Jelle Zijlstra &lt;jelle.zijlstra&#32;&#97;t&#32;gmail.com&gt;</dd>
<dt class="field-even">Discussions-To<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/19756">Discourse thread</a></dd>
<dt class="field-odd">Status<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
<dt class="field-even">Type<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-odd">Topic<span class="colon">:</span></dt>
<dd class="field-odd"><a class="reference external" href="../topic/typing/">Typing</a></dd>
<dt class="field-even">Created<span class="colon">:</span></dt>
<dd class="field-even">23-Apr-2022</dd>
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
<dd class="field-odd">3.12</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://mail.python.org/archives/list/typing-sig&#64;python.org/thread/CX7GPSIYQEL23RXMYL66GAKGP4RLUD7P/" title="Typing-SIG thread">23-Apr-2022</a>,
<a class="reference external" href="https://discuss.python.org/t/15265" title="Discourse thread">25-Apr-2022</a>,
<a class="reference external" href="https://discuss.python.org/t/19756" title="Discourse thread">06-Oct-2022</a>,
<a class="reference external" href="https://mail.python.org/archives/list/typing-sig&#64;python.org/thread/XH5ZK2MSZIQLL62PYZ6I5532SQKKVCBL/" title="Typing-SIG thread">26-Oct-2022</a></dd>
<dt class="field-odd">Resolution<span class="colon">:</span></dt>
<dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/pep-688-making-the-buffer-protocol-accessible-in-python/15265/35">07-Mar-2023</a></dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
<li><a class="reference internal" href="#current-options">Current options</a></li>
<li><a class="reference internal" href="#kinds-of-buffers">Kinds of buffers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#python-level-buffer-protocol">Python-level buffer protocol</a></li>
<li><a class="reference internal" href="#inspect-bufferflags"><code class="docutils literal notranslate"><span class="pre">inspect.BufferFlags</span></code></a></li>
<li><a class="reference internal" href="#collections-abc-buffer"><code class="docutils literal notranslate"><span class="pre">collections.abc.Buffer</span></code></a></li>
<li><a class="reference internal" href="#example">Example</a></li>
<li><a class="reference internal" href="#equivalent-for-older-python-versions">Equivalent for older Python versions</a></li>
<li><a class="reference internal" href="#no-special-meaning-for-bytes">No special meaning for <code class="docutils literal notranslate"><span class="pre">bytes</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul>
<li><a class="reference internal" href="#buffer-and-release-buffer-attributes"><code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> and <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code> attributes</a></li>
<li><a class="reference internal" href="#removal-of-the-bytes-special-case">Removal of the <code class="docutils literal notranslate"><span class="pre">bytes</span></code> special case</a></li>
</ul>
</li>
<li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#types-buffer"><code class="docutils literal notranslate"><span class="pre">types.Buffer</span></code></a></li>
<li><a class="reference internal" href="#keep-bytearray-compatible-with-bytes">Keep <code class="docutils literal notranslate"><span class="pre">bytearray</span></code> compatible with <code class="docutils literal notranslate"><span class="pre">bytes</span></code></a></li>
<li><a class="reference internal" href="#distinguish-between-mutable-and-immutable-buffers">Distinguish between mutable and immutable buffers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<div class="pep-banner canonical-doc sticky-banner admonition important">
<p class="admonition-title">Important</p>
<p>This PEP is a historical document. The up-to-date, canonical documentation can now be found at <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#python-buffer-protocol" title="(in Python v3.13)"><span>Emulating buffer types</span></a>.</p>
<p class="close-button">×</p>
<p>See <a class="pep reference internal" href="../pep-0001/" title="PEP 1 PEP Purpose and Guidelines">PEP 1</a> for how to propose changes.</p>
</div>
<section id="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p>This PEP proposes a Python-level API for the buffer protocol,
which is currently accessible only to C code. This allows type
checkers to evaluate whether objects implement the protocol.</p>
</section>
<section id="motivation">
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
<p>The CPython C API provides a versatile mechanism for accessing the
underlying memory of an object—the <a class="reference external" href="https://docs.python.org/3/c-api/buffer.html">buffer protocol</a>
introduced in <a class="pep reference internal" href="../pep-3118/" title="PEP 3118 Revising the buffer protocol">PEP 3118</a>.
Functions that accept binary data are usually written to handle any
object implementing the buffer protocol. For example, at the time of writing,
there are around 130 functions in CPython using the Argument Clinic
<code class="docutils literal notranslate"><span class="pre">Py_buffer</span></code> type, which accepts the buffer protocol.</p>
<p>Currently, there is no way for Python code to inspect whether an object
supports the buffer protocol. Moreover, the static type system
does not provide a type annotation to represent the protocol.
This is a <a class="reference external" href="https://github.com/python/typing/issues/593">common problem</a>
when writing type annotations for code that accepts generic buffers.</p>
<p>Similarly, it is impossible for a class written in Python to support
the buffer protocol. A buffer class in
Python would give users the ability to easily wrap a C buffer object, or to test
the behavior of an API that consumes the buffer protocol. Granted, this is not
a particularly common need. However, there has been a
<a class="reference external" href="https://github.com/python/cpython/issues/58006">CPython feature request</a>
for supporting buffer classes written in Python that has been open since 2012.</p>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<section id="current-options">
<h3><a class="toc-backref" href="#current-options" role="doc-backlink">Current options</a></h3>
<p>There are two known workarounds for annotating buffer types in
the type system, but neither is adequate.</p>
<p>First, the <a class="reference external" href="https://github.com/python/typeshed/blob/2a0fc1b582ef84f7a82c0beb39fa617de2539d3d/stdlib/_typeshed/__init__.pyi#L194">current workaround</a>
for buffer types in typeshed is a type alias
that lists well-known buffer types in the standard library, such as
<code class="docutils literal notranslate"><span class="pre">bytes</span></code>, <code class="docutils literal notranslate"><span class="pre">bytearray</span></code>, <code class="docutils literal notranslate"><span class="pre">memoryview</span></code>, and <code class="docutils literal notranslate"><span class="pre">array.array</span></code>. This
approach works for the standard library, but it does not extend to
third-party buffer types.</p>
<p>Second, the <a class="reference external" href="https://docs.python.org/3.10/library/typing.html#typing.ByteString">documentation</a>
for <code class="docutils literal notranslate"><span class="pre">typing.ByteString</span></code> currently states:</p>
<blockquote>
<div>This type represents the types <code class="docutils literal notranslate"><span class="pre">bytes</span></code>, <code class="docutils literal notranslate"><span class="pre">bytearray</span></code>, and
<code class="docutils literal notranslate"><span class="pre">memoryview</span></code> of byte sequences.<p>As a shorthand for this type, <code class="docutils literal notranslate"><span class="pre">bytes</span></code> can be used to annotate
arguments of any of the types mentioned above.</p>
</div></blockquote>
<p>Although this sentence has been in the documentation
<a class="reference external" href="https://github.com/python/cpython/commit/2a19d956ab92fc9084a105cc11292cb0438b322f">since 2015</a>,
the use of <code class="docutils literal notranslate"><span class="pre">bytes</span></code> to include these other types is not specified
in any of the typing PEPs. Furthermore, this mechanism has a number of
problems. It does not include all possible buffer types, and it
makes the <code class="docutils literal notranslate"><span class="pre">bytes</span></code> type ambiguous in type annotations. After all,
there are many operations that are valid on <code class="docutils literal notranslate"><span class="pre">bytes</span></code> objects, but
not on <code class="docutils literal notranslate"><span class="pre">memoryview</span></code> objects, and it is perfectly possible for
a function to accept <code class="docutils literal notranslate"><span class="pre">bytes</span></code> but not <code class="docutils literal notranslate"><span class="pre">memoryview</span></code> objects.
A mypy user
<a class="reference external" href="https://github.com/python/mypy/issues/12643#issuecomment-1105914159">reports</a>
that this shortcut has caused significant problems for the <code class="docutils literal notranslate"><span class="pre">psycopg</span></code> project.</p>
</section>
<section id="kinds-of-buffers">
<h3><a class="toc-backref" href="#kinds-of-buffers" role="doc-backlink">Kinds of buffers</a></h3>
<p>The C buffer protocol supports
<a class="reference external" href="https://docs.python.org/3.10/c-api/buffer.html#buffer-request-types">many options</a>,
affecting strides, contiguity, and support for writing to the buffer. Some of these
options would be useful in the type system. For example, typeshed
currently provides separate type aliases for writable and read-only
buffers.</p>
<p>However, in the C buffer protocol, most of these options cannot be
queried directly on the type object. The only way to figure out
whether an object supports a particular flag is to actually
ask for the buffer. For some types, such as <code class="docutils literal notranslate"><span class="pre">memoryview</span></code>,
the supported flags depend on the instance. As a result, it would
be difficult to represent support for these flags in the type system.</p>
</section>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<section id="python-level-buffer-protocol">
<h3><a class="toc-backref" href="#python-level-buffer-protocol" role="doc-backlink">Python-level buffer protocol</a></h3>
<p>We propose to add two Python-level special methods, <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code>
and <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code>. Python
classes that implement these methods are usable as buffers from C
code. Conversely, classes implemented in C that support the
buffer protocol acquire synthesized methods accessible from Python
code.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> method is called to create a buffer from a Python
object, for example by the <code class="docutils literal notranslate"><span class="pre">memoryview()</span></code> constructor.
It corresponds to the <code class="docutils literal notranslate"><span class="pre">bf_getbuffer</span></code> C slot.
The Python signature for this method is
<code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">__buffer__(self,</span> <span class="pre">flags:</span> <span class="pre">int,</span> <span class="pre">/)</span> <span class="pre">-&gt;</span> <span class="pre">memoryview:</span> <span class="pre">...</span></code>. The method
must return a <code class="docutils literal notranslate"><span class="pre">memoryview</span></code> object. If the <code class="docutils literal notranslate"><span class="pre">bf_getbuffer</span></code> slot
is invoked on a Python class with a <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> method,
the interpreter extracts the underlying <code class="docutils literal notranslate"><span class="pre">Py_buffer</span></code> from the
<code class="docutils literal notranslate"><span class="pre">memoryview</span></code> returned by the method
and returns it to the C caller. Similarly, if Python code calls the
<code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> method on an instance of a C class that
implements <code class="docutils literal notranslate"><span class="pre">bf_getbuffer</span></code>, the returned buffer is wrapped in a
<code class="docutils literal notranslate"><span class="pre">memoryview</span></code> for consumption by Python code.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code> method should be called when a caller no
longer needs the buffer returned by <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code>. It corresponds to the
<code class="docutils literal notranslate"><span class="pre">bf_releasebuffer</span></code> C slot. This is an
optional part of the buffer protocol.
The Python signature for this method is
<code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">__release_buffer__(self,</span> <span class="pre">buffer:</span> <span class="pre">memoryview,</span> <span class="pre">/)</span> <span class="pre">-&gt;</span> <span class="pre">None:</span> <span class="pre">...</span></code>.
The buffer to be released is wrapped in a <code class="docutils literal notranslate"><span class="pre">memoryview</span></code>. When this
method is invoked through CPythons buffer API (for example, through
calling <code class="docutils literal notranslate"><span class="pre">memoryview.release</span></code> on a <code class="docutils literal notranslate"><span class="pre">memoryview</span></code> returned by
<code class="docutils literal notranslate"><span class="pre">__buffer__</span></code>), the passed <code class="docutils literal notranslate"><span class="pre">memoryview</span></code> is the same object
as was returned by <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code>. It is
also possible to call <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code> on a C class that
implements <code class="docutils literal notranslate"><span class="pre">bf_releasebuffer</span></code>.</p>
<p>If <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code> exists on an object,
Python code that calls <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> directly on the object must
call <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code> on the same object when it is done
with the buffer. Otherwise, resources used by the object may
not be reclaimed. Similarly, it is a programming error
to call <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code> without a previous call to
<code class="docutils literal notranslate"><span class="pre">__buffer__</span></code>, or to call it multiple times for a single call
to <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code>. For objects that implement the C buffer protocol,
calls to <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code> where the argument is not a
<code class="docutils literal notranslate"><span class="pre">memoryview</span></code> wrapping the same object will raise an exception.
After a valid call to <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code>, the <code class="docutils literal notranslate"><span class="pre">memoryview</span></code>
is invalidated (as if its <code class="docutils literal notranslate"><span class="pre">release()</span></code> method had been called),
and any subsequent calls to <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code> with the same
<code class="docutils literal notranslate"><span class="pre">memoryview</span></code> will raise an exception.
The interpreter will ensure that misuse
of the Python API will not break invariants at the C level for
example, it will not cause memory safety violations.</p>
</section>
<section id="inspect-bufferflags">
<h3><a class="toc-backref" href="#inspect-bufferflags" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">inspect.BufferFlags</span></code></a></h3>
<p>To help implementations of <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code>, we add <code class="docutils literal notranslate"><span class="pre">inspect.BufferFlags</span></code>,
a subclass of <code class="docutils literal notranslate"><span class="pre">enum.IntFlag</span></code>. This enum contains all flags defined in the
C buffer protocol. For example, <code class="docutils literal notranslate"><span class="pre">inspect.BufferFlags.SIMPLE</span></code> has the same
value as the <code class="docutils literal notranslate"><span class="pre">PyBUF_SIMPLE</span></code> constant.</p>
</section>
<section id="collections-abc-buffer">
<h3><a class="toc-backref" href="#collections-abc-buffer" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">collections.abc.Buffer</span></code></a></h3>
<p>We add a new abstract base classes, <code class="docutils literal notranslate"><span class="pre">collections.abc.Buffer</span></code>,
which requires the <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> method.
This class is intended primarily for use in type annotations:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">need_buffer</span><span class="p">(</span><span class="n">b</span><span class="p">:</span> <span class="n">Buffer</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">memoryview</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">memoryview</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
<span class="n">need_buffer</span><span class="p">(</span><span class="sa">b</span><span class="s2">&quot;xy&quot;</span><span class="p">)</span> <span class="c1"># ok</span>
<span class="n">need_buffer</span><span class="p">(</span><span class="s2">&quot;xy&quot;</span><span class="p">)</span> <span class="c1"># rejected by static type checkers</span>
</pre></div>
</div>
<p>It can also be used in <code class="docutils literal notranslate"><span class="pre">isinstance</span></code> and <code class="docutils literal notranslate"><span class="pre">issubclass</span></code> checks:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Buffer</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">isinstance</span><span class="p">(</span><span class="sa">b</span><span class="s2">&quot;xy&quot;</span><span class="p">,</span> <span class="n">Buffer</span><span class="p">)</span>
<span class="go">True</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">issubclass</span><span class="p">(</span><span class="nb">bytes</span><span class="p">,</span> <span class="n">Buffer</span><span class="p">)</span>
<span class="go">True</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">issubclass</span><span class="p">(</span><span class="nb">memoryview</span><span class="p">,</span> <span class="n">Buffer</span><span class="p">)</span>
<span class="go">True</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">isinstance</span><span class="p">(</span><span class="s2">&quot;xy&quot;</span><span class="p">,</span> <span class="n">Buffer</span><span class="p">)</span>
<span class="go">False</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">issubclass</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">Buffer</span><span class="p">)</span>
<span class="go">False</span>
</pre></div>
</div>
<p>In the typeshed stub files, the class should be defined as a <code class="docutils literal notranslate"><span class="pre">Protocol</span></code>,
following the precedent of other simple ABCs in <code class="docutils literal notranslate"><span class="pre">collections.abc</span></code> such as
<code class="docutils literal notranslate"><span class="pre">collections.abc.Iterable</span></code> or <code class="docutils literal notranslate"><span class="pre">collections.abc.Sized</span></code>.</p>
</section>
<section id="example">
<h3><a class="toc-backref" href="#example" role="doc-backlink">Example</a></h3>
<p>The following is an example of a Python class that implements the
buffer protocol:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">contextlib</span>
<span class="kn">import</span> <span class="nn">inspect</span>
<span class="k">class</span> <span class="nc">MyBuffer</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">view</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">__buffer__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">flags</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">memoryview</span><span class="p">:</span>
<span class="k">if</span> <span class="n">flags</span> <span class="o">!=</span> <span class="n">inspect</span><span class="o">.</span><span class="n">BufferFlags</span><span class="o">.</span><span class="n">FULL_RO</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">&quot;Only BufferFlags.FULL_RO supported&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">view</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">&quot;Buffer already held&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">view</span> <span class="o">=</span> <span class="nb">memoryview</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">view</span>
<span class="k">def</span> <span class="nf">__release_buffer__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">view</span><span class="p">:</span> <span class="nb">memoryview</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">view</span> <span class="ow">is</span> <span class="n">view</span> <span class="c1"># guaranteed to be true</span>
<span class="bp">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">release</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">view</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">extend</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">view</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">&quot;Cannot extend held buffer&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
<span class="n">buffer</span> <span class="o">=</span> <span class="n">MyBuffer</span><span class="p">(</span><span class="sa">b</span><span class="s2">&quot;capybara&quot;</span><span class="p">)</span>
<span class="k">with</span> <span class="nb">memoryview</span><span class="p">(</span><span class="n">buffer</span><span class="p">)</span> <span class="k">as</span> <span class="n">view</span><span class="p">:</span>
<span class="n">view</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nb">ord</span><span class="p">(</span><span class="s2">&quot;C&quot;</span><span class="p">)</span>
<span class="k">with</span> <span class="n">contextlib</span><span class="o">.</span><span class="n">suppress</span><span class="p">(</span><span class="ne">RuntimeError</span><span class="p">):</span>
<span class="n">buffer</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="sa">b</span><span class="s2">&quot;!&quot;</span><span class="p">)</span> <span class="c1"># raises RuntimeError</span>
<span class="n">buffer</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="sa">b</span><span class="s2">&quot;!&quot;</span><span class="p">)</span> <span class="c1"># ok, buffer is no longer held</span>
<span class="k">with</span> <span class="nb">memoryview</span><span class="p">(</span><span class="n">buffer</span><span class="p">)</span> <span class="k">as</span> <span class="n">view</span><span class="p">:</span>
<span class="k">assert</span> <span class="n">view</span><span class="o">.</span><span class="n">tobytes</span><span class="p">()</span> <span class="o">==</span> <span class="sa">b</span><span class="s2">&quot;Capybara!&quot;</span>
</pre></div>
</div>
</section>
<section id="equivalent-for-older-python-versions">
<h3><a class="toc-backref" href="#equivalent-for-older-python-versions" role="doc-backlink">Equivalent for older Python versions</a></h3>
<p>New typing features are usually backported to older Python versions
in the <a class="reference external" href="https://pypi.org/project/typing-extensions/">typing_extensions</a>
package. Because the buffer protocol
is currently accessible only in C, this PEP cannot be fully implemented
in a pure-Python package like <code class="docutils literal notranslate"><span class="pre">typing_extensions</span></code>. As a temporary
workaround, an abstract base class <code class="docutils literal notranslate"><span class="pre">typing_extensions.Buffer</span></code>
will be provided for Python versions
that do not have <code class="docutils literal notranslate"><span class="pre">collections.abc.Buffer</span></code> available.</p>
<p>After this PEP is implemented, inheriting from <code class="docutils literal notranslate"><span class="pre">collections.abc.Buffer</span></code> will
not be necessary to indicate that an object supports the buffer protocol.
However, in older Python versions, it will be necessary to explicitly
inherit from <code class="docutils literal notranslate"><span class="pre">typing_extensions.Buffer</span></code> to indicate to type checkers that
a class supports the buffer protocol, since objects supporting the buffer
protocol will not have a <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> method. It is expected that this
will happen primarily in stub files, because buffer classes are necessarily
implemented in C code, which cannot have types defined inline.
For runtime uses, the <code class="docutils literal notranslate"><span class="pre">ABC.register</span></code> API can be used to register
buffer classes with <code class="docutils literal notranslate"><span class="pre">typing_extensions.Buffer</span></code>.</p>
</section>
<section id="no-special-meaning-for-bytes">
<h3><a class="toc-backref" href="#no-special-meaning-for-bytes" role="doc-backlink">No special meaning for <code class="docutils literal notranslate"><span class="pre">bytes</span></code></a></h3>
<p>The special case stating that <code class="docutils literal notranslate"><span class="pre">bytes</span></code> may be used as a shorthand
for other <code class="docutils literal notranslate"><span class="pre">ByteString</span></code> types will be removed from the <code class="docutils literal notranslate"><span class="pre">typing</span></code>
documentation.
With <code class="docutils literal notranslate"><span class="pre">collections.abc.Buffer</span></code> available as an alternative, there will be no good
reason to allow <code class="docutils literal notranslate"><span class="pre">bytes</span></code> as a shorthand.
Type checkers currently implementing this behavior
should deprecate and eventually remove it.</p>
</section>
</section>
<section id="backwards-compatibility">
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
<section id="buffer-and-release-buffer-attributes">
<h3><a class="toc-backref" href="#buffer-and-release-buffer-attributes" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> and <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code> attributes</a></h3>
<p>As the runtime changes in this PEP only add new functionality, there are
few backwards compatibility concerns.</p>
<p>However, code that uses a <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> or <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code> attribute for
other purposes may be affected. While all dunders are technically reserved for the
language, it is still good practice to ensure that a new dunder does not
interfere with too much existing code, especially widely used packages. A survey
of publicly accessible code found:</p>
<ul class="simple">
<li>PyPy <a class="reference external" href="https://doc.pypy.org/en/latest/__pypy__-module.html#generally-available-functionality">supports</a>
a <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> method with compatible semantics to those proposed in this
PEP. A PyPy core developer <a class="reference external" href="https://discuss.python.org/t/pep-688-making-the-buffer-protocol-accessible-in-python/15265/34">expressed his support</a>
for this PEP.</li>
<li>pyzmq <a class="reference external" href="https://github.com/zeromq/pyzmq/blob/fe18dc55516ef50d168fc02f8550a67ff5b5633d/zmq/backend/cffi/message.py#L190">implements</a>
a PyPy-compatible <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> method.</li>
<li>mpi4py <a class="reference external" href="https://github.com/mpi4py/mpi4py/blob/453b87d0da37c5914b91afb511b188556dff2a9c/src/mpi4py/typing.py#L66">defines</a>
a <code class="docutils literal notranslate"><span class="pre">SupportsBuffer</span></code> protocol that would be equivalent to this PEPs <code class="docutils literal notranslate"><span class="pre">collections.abc.Buffer</span></code>.</li>
<li>NumPy used to have an undocumented behavior where it would access a <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> attribute
(not method) to get an objects buffer. This was <a class="reference external" href="https://github.com/numpy/numpy/pull/13049">removed</a>
in 2019 for NumPy 1.17. The behavior would have last worked in NumPy 1.16, which only supported
Python 3.7 and older. Python 3.7 will have reached its end of life by the time this PEP is expected to
be implemented.</li>
</ul>
<p>Thus, this PEPs use of the <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> method will improve interoperability with
PyPy and not interfere with the current versions of any major Python packages.</p>
<p>No publicly accessible code uses the name <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code>.</p>
</section>
<section id="removal-of-the-bytes-special-case">
<h3><a class="toc-backref" href="#removal-of-the-bytes-special-case" role="doc-backlink">Removal of the <code class="docutils literal notranslate"><span class="pre">bytes</span></code> special case</a></h3>
<p>Separately, the recommendation to remove the special behavior for
<code class="docutils literal notranslate"><span class="pre">bytes</span></code> in type checkers does have a backwards compatibility
impact on their users. An <a class="reference external" href="https://github.com/python/mypy/pull/12661">experiment</a>
with mypy shows that several major open source projects that use it
for type checking will see new errors if the <code class="docutils literal notranslate"><span class="pre">bytes</span></code> promotion
is removed. Many of these errors can be fixed by improving
the stubs in typeshed, as has already been done for the
<a class="reference external" href="https://github.com/python/typeshed/pull/7631">builtins</a>,
<a class="reference external" href="https://github.com/python/typeshed/pull/7677">binascii</a>,
<a class="reference external" href="https://github.com/python/typeshed/pull/7678">pickle</a>, and
<a class="reference external" href="https://github.com/python/typeshed/pull/7679">re</a> modules.
A <a class="reference external" href="https://github.com/python/typeshed/issues/9006">review</a> of all
usage of <code class="docutils literal notranslate"><span class="pre">bytes</span></code> types in typeshed is in progress.
Overall, the change improves type safety and makes the type system
more consistent, so we believe the migration cost is worth it.</p>
</section>
</section>
<section id="how-to-teach-this">
<h2><a class="toc-backref" href="#how-to-teach-this" role="doc-backlink">How to Teach This</a></h2>
<p>We will add notes pointing to <code class="docutils literal notranslate"><span class="pre">collections.abc.Buffer</span></code> in appropriate places in the
documentation, such as <a class="reference external" href="https://typing.readthedocs.io/en/latest/">typing.readthedocs.io</a>
and the <a class="reference external" href="https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html">mypy cheat sheet</a>.
Type checkers may provide additional pointers in their error messages. For example,
when they encounter a buffer object being passed to a function that
is annotated to only accept <code class="docutils literal notranslate"><span class="pre">bytes</span></code>, the error message could include a note suggesting
the use of <code class="docutils literal notranslate"><span class="pre">collections.abc.Buffer</span></code> instead.</p>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
<p>An implementation of this PEP is
<a class="reference external" href="https://github.com/python/cpython/compare/main...JelleZijlstra:pep688v2?expand=1">available</a>
in the authors fork.</p>
</section>
<section id="rejected-ideas">
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
<section id="types-buffer">
<h3><a class="toc-backref" href="#types-buffer" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">types.Buffer</span></code></a></h3>
<p>An earlier version of this PEP proposed adding a new <code class="docutils literal notranslate"><span class="pre">types.Buffer</span></code> type with
an <code class="docutils literal notranslate"><span class="pre">__instancecheck__</span></code> implemented in C so that <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> checks can be
used to check whether a type implements the buffer protocol. This avoids the
complexity of exposing the full buffer protocol to Python code, while still
allowing the type system to check for the buffer protocol.</p>
<p>However, that approach
does not compose well with the rest of the type system, because <code class="docutils literal notranslate"><span class="pre">types.Buffer</span></code>
would be a nominal type, not a structural one. For example, there would be no way
to represent “an object that supports both the buffer protocol and <code class="docutils literal notranslate"><span class="pre">__len__</span></code>”. With
the current proposal, <code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> is like any other special method, so a
<code class="docutils literal notranslate"><span class="pre">Protocol</span></code> can be defined combining it with another method.</p>
<p>More generally, no other part of Python works like the proposed <code class="docutils literal notranslate"><span class="pre">types.Buffer</span></code>.
The current proposal is more consistent with the rest of the language, where
C-level slots usually have corresponding Python-level special methods.</p>
</section>
<section id="keep-bytearray-compatible-with-bytes">
<h3><a class="toc-backref" href="#keep-bytearray-compatible-with-bytes" role="doc-backlink">Keep <code class="docutils literal notranslate"><span class="pre">bytearray</span></code> compatible with <code class="docutils literal notranslate"><span class="pre">bytes</span></code></a></h3>
<p>It has been suggested to remove the special case where <code class="docutils literal notranslate"><span class="pre">memoryview</span></code> is
always compatible with <code class="docutils literal notranslate"><span class="pre">bytes</span></code>, but keep it for <code class="docutils literal notranslate"><span class="pre">bytearray</span></code>, because
the two types have very similar interfaces. However, several standard
library functions (e.g., <code class="docutils literal notranslate"><span class="pre">re.compile</span></code>, <code class="docutils literal notranslate"><span class="pre">socket.getaddrinfo</span></code>, and most
functions accepting path-like arguments) accept
<code class="docutils literal notranslate"><span class="pre">bytes</span></code> but not <code class="docutils literal notranslate"><span class="pre">bytearray</span></code>. In most codebases, <code class="docutils literal notranslate"><span class="pre">bytearray</span></code> is also
not a very common type. We prefer to have users spell out accepted types
explicitly (or use <code class="docutils literal notranslate"><span class="pre">Protocol</span></code> from <a class="pep reference internal" href="../pep-0544/" title="PEP 544 Protocols: Structural subtyping (static duck typing)">PEP 544</a> if only a specific set of
methods is required). This aspect of the proposal was <a class="reference external" href="https://mail.python.org/archives/list/typing-sig&#64;python.org/thread/XH5ZK2MSZIQLL62PYZ6I5532SQKKVCBL/">specifically
discussed</a>
on the typing-sig mailing list, without any strong disagreement from the
typing community.</p>
</section>
<section id="distinguish-between-mutable-and-immutable-buffers">
<h3><a class="toc-backref" href="#distinguish-between-mutable-and-immutable-buffers" role="doc-backlink">Distinguish between mutable and immutable buffers</a></h3>
<p>The most frequently used distinction within buffer types is
whether or not the buffer is mutable. Some functions accept only
mutable buffers (e.g., <code class="docutils literal notranslate"><span class="pre">bytearray</span></code>, some <code class="docutils literal notranslate"><span class="pre">memoryview</span></code> objects),
others accept all buffers.</p>
<p>An earlier version of this PEP proposed using the presence of the
<code class="docutils literal notranslate"><span class="pre">bf_releasebuffer</span></code> slot to determine whether a buffer type is mutable.
This rule holds for most standard library buffer types, but the relationship
between mutability and the presence of this slot is not absolute. For
example, <code class="docutils literal notranslate"><span class="pre">numpy</span></code> arrays are mutable but do not have this slot.</p>
<p>The current buffer protocol does not provide any way to reliably
determine whether a buffer type represents a mutable or immutable
buffer. Therefore, this PEP does not add type system support
for this distinction.
The question can be revisited in the future if the buffer protocol
is enhanced to provide static introspection support.
A <a class="reference external" href="https://discuss.python.org/t/introspection-and-mutable-xor-shared-semantics-for-pybuffer/20314">sketch</a>
for such a mechanism exists.</p>
</section>
</section>
<section id="acknowledgments">
<h2><a class="toc-backref" href="#acknowledgments" role="doc-backlink">Acknowledgments</a></h2>
<p>Many people have provided useful feedback on drafts of this PEP.
Petr Viktorin has been particularly helpful in improving my understanding
of the subtleties of the buffer protocol.</p>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0688.rst">https://github.com/python/peps/blob/main/peps/pep-0688.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0688.rst">2024-10-17 12:49:39 GMT</a></p>
</article>
<nav id="pep-sidebar">
<h2>Contents</h2>
<ul>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
<li><a class="reference internal" href="#current-options">Current options</a></li>
<li><a class="reference internal" href="#kinds-of-buffers">Kinds of buffers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#python-level-buffer-protocol">Python-level buffer protocol</a></li>
<li><a class="reference internal" href="#inspect-bufferflags"><code class="docutils literal notranslate"><span class="pre">inspect.BufferFlags</span></code></a></li>
<li><a class="reference internal" href="#collections-abc-buffer"><code class="docutils literal notranslate"><span class="pre">collections.abc.Buffer</span></code></a></li>
<li><a class="reference internal" href="#example">Example</a></li>
<li><a class="reference internal" href="#equivalent-for-older-python-versions">Equivalent for older Python versions</a></li>
<li><a class="reference internal" href="#no-special-meaning-for-bytes">No special meaning for <code class="docutils literal notranslate"><span class="pre">bytes</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a><ul>
<li><a class="reference internal" href="#buffer-and-release-buffer-attributes"><code class="docutils literal notranslate"><span class="pre">__buffer__</span></code> and <code class="docutils literal notranslate"><span class="pre">__release_buffer__</span></code> attributes</a></li>
<li><a class="reference internal" href="#removal-of-the-bytes-special-case">Removal of the <code class="docutils literal notranslate"><span class="pre">bytes</span></code> special case</a></li>
</ul>
</li>
<li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#types-buffer"><code class="docutils literal notranslate"><span class="pre">types.Buffer</span></code></a></li>
<li><a class="reference internal" href="#keep-bytearray-compatible-with-bytes">Keep <code class="docutils literal notranslate"><span class="pre">bytearray</span></code> compatible with <code class="docutils literal notranslate"><span class="pre">bytes</span></code></a></li>
<li><a class="reference internal" href="#distinguish-between-mutable-and-immutable-buffers">Distinguish between mutable and immutable buffers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
<br>
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0688.rst">Page Source (GitHub)</a>
</nav>
</section>
<script src="../_static/colour_scheme.js"></script>
<script src="../_static/wrap_tables.js"></script>
<script src="../_static/sticky_banner.js"></script>
</body>
</html>