CINXE.COM
PEP 3118 – Revising the buffer protocol | peps.python.org
<!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 3118 – Revising the buffer protocol | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-3118/"> <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 3118 – Revising the buffer protocol | peps.python.org'> <meta property="og:description" content="This PEP proposes re-designing the buffer interface (PyBufferProcs function pointers) to improve the way Python allows memory sharing in Python 3.0"> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-3118/"> <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 re-designing the buffer interface (PyBufferProcs function pointers) to improve the way Python allows memory sharing in Python 3.0"> <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> » </li> <li><a href="../pep-0000/">PEP Index</a> » </li> <li>PEP 3118</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 3118 – Revising the buffer protocol</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Travis Oliphant <oliphant at ee.byu.edu>, Carl Banks <pythondev at aerojockey.com></dd> <dt class="field-even">Status<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd> <dt class="field-odd">Type<span class="colon">:</span></dt> <dd class="field-odd"><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-even">Created<span class="colon">:</span></dt> <dd class="field-even">28-Aug-2006</dd> <dt class="field-odd">Python-Version<span class="colon">:</span></dt> <dd class="field-odd">3.0</dd> <dt class="field-even">Post-History<span class="colon">:</span></dt> <dd class="field-even"><p></p></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="#rationale">Rationale</a></li> <li><a class="reference internal" href="#proposal-overview">Proposal Overview</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#access-flags">Access flags</a></li> <li><a class="reference internal" href="#the-py-buffer-struct">The Py_buffer struct</a></li> <li><a class="reference internal" href="#releasing-the-buffer">Releasing the buffer</a></li> </ul> </li> <li><a class="reference internal" href="#new-c-api-calls-are-proposed">New C-API calls are proposed</a></li> <li><a class="reference internal" href="#additions-to-the-struct-string-syntax">Additions to the struct string-syntax</a></li> <li><a class="reference internal" href="#examples-of-data-format-descriptions">Examples of Data-Format Descriptions</a></li> <li><a class="reference internal" href="#code-to-be-affected">Code to be affected</a></li> <li><a class="reference internal" href="#issues-and-details">Issues and Details</a></li> <li><a class="reference internal" href="#code">Code</a></li> <li><a class="reference internal" href="#examples">Examples</a><ul> <li><a class="reference internal" href="#ex-1">Ex. 1</a></li> <li><a class="reference internal" href="#ex-2">Ex. 2</a></li> <li><a class="reference internal" href="#ex-3">Ex. 3</a></li> <li><a class="reference internal" href="#ex-4">Ex. 4</a></li> </ul> </li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>This PEP proposes re-designing the buffer interface (<code class="docutils literal notranslate"><span class="pre">PyBufferProcs</span></code> function pointers) to improve the way Python allows memory sharing in Python 3.0</p> <p>In particular, it is proposed that the character buffer portion of the API be eliminated and the multiple-segment portion be re-designed in conjunction with allowing for strided memory to be shared. In addition, the new buffer interface will allow the sharing of any multi-dimensional nature of the memory and what data-format the memory contains.</p> <p>This interface will allow any extension module to either create objects that share memory or create algorithms that use and manipulate raw memory from arbitrary objects that export the interface.</p> </section> <section id="rationale"> <h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2> <p>The Python 2.X buffer protocol allows different Python types to exchange a pointer to a sequence of internal buffers. This functionality is <em>extremely</em> useful for sharing large segments of memory between different high-level objects, but it is too limited and has issues:</p> <ol class="arabic"> <li>There is the little used “sequence-of-segments” option (bf_getsegcount) that is not well motivated.</li> <li>There is the apparently redundant character-buffer option (bf_getcharbuffer)</li> <li>There is no way for a consumer to tell the buffer-API-exporting object it is “finished” with its view of the memory and therefore no way for the exporting object to be sure that it is safe to reallocate the pointer to the memory that it owns (for example, the array object reallocating its memory after sharing it with the buffer object which held the original pointer led to the infamous buffer-object problem).</li> <li>Memory is just a pointer with a length. There is no way to describe what is “in” the memory (float, int, C-structure, etc.)</li> <li>There is no shape information provided for the memory. But, several array-like Python types could make use of a standard way to describe the shape-interpretation of the memory (wxPython, GTK, pyQT, CVXOPT, PyVox, Audio and Video Libraries, ctypes, NumPy, data-base interfaces, etc.)</li> <li>There is no way to share discontiguous memory (except through the sequence of segments notion).<p>There are two widely used libraries that use the concept of discontiguous memory: PIL and NumPy. Their view of discontiguous arrays is different, though. The proposed buffer interface allows sharing of either memory model. Exporters will typically use only one approach and consumers may choose to support discontiguous arrays of each type however they choose.</p> <p>NumPy uses the notion of constant striding in each dimension as its basic concept of an array. With this concept, a simple sub-region of a larger array can be described without copying the data. Thus, stride information is the additional information that must be shared.</p> <p>The PIL uses a more opaque memory representation. Sometimes an image is contained in a contiguous segment of memory, but sometimes it is contained in an array of pointers to the contiguous segments (usually lines) of the image. The PIL is where the idea of multiple buffer segments in the original buffer interface came from.</p> <p>NumPy’s strided memory model is used more often in computational libraries and because it is so simple it makes sense to support memory sharing using this model. The PIL memory model is sometimes used in C-code where a 2-d array can then be accessed using double pointer indirection: e.g. <code class="docutils literal notranslate"><span class="pre">image[i][j]</span></code>.</p> <p>The buffer interface should allow the object to export either of these memory models. Consumers are free to either require contiguous memory or write code to handle one or both of these memory models.</p> </li> </ol> </section> <section id="proposal-overview"> <h2><a class="toc-backref" href="#proposal-overview" role="doc-backlink">Proposal Overview</a></h2> <ul class="simple"> <li>Eliminate the char-buffer and multiple-segment sections of the buffer-protocol.</li> <li>Unify the read/write versions of getting the buffer.</li> <li>Add a new function to the interface that should be called when the consumer object is “done” with the memory area.</li> <li>Add a new variable to allow the interface to describe what is in memory (unifying what is currently done now in struct and array)</li> <li>Add a new variable to allow the protocol to share shape information</li> <li>Add a new variable for sharing stride information</li> <li>Add a new mechanism for sharing arrays that must be accessed using pointer indirection.</li> <li>Fix all objects in the core and the standard library to conform to the new interface</li> <li>Extend the struct module to handle more format specifiers</li> <li>Extend the buffer object into a new memory object which places a Python veneer around the buffer interface.</li> <li>Add a few functions to make it easy to copy contiguous data in and out of object supporting the buffer interface.</li> </ul> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <p>While the new specification allows for complicated memory sharing, simple contiguous buffers of bytes can still be obtained from an object. In fact, the new protocol allows a standard mechanism for doing this even if the original object is not represented as a contiguous chunk of memory.</p> <p>The easiest way to obtain a simple contiguous chunk of memory is to use the provided C-API to obtain a chunk of memory.</p> <p>Change the <code class="docutils literal notranslate"><span class="pre">PyBufferProcs</span></code> structure to</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">typedef</span> <span class="n">struct</span> <span class="p">{</span> <span class="n">getbufferproc</span> <span class="n">bf_getbuffer</span><span class="p">;</span> <span class="n">releasebufferproc</span> <span class="n">bf_releasebuffer</span><span class="p">;</span> <span class="p">}</span> <span class="n">PyBufferProcs</span><span class="p">;</span> </pre></div> </div> <p>Both of these routines are optional for a type object</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">typedef</span> <span class="nb">int</span> <span class="p">(</span><span class="o">*</span><span class="n">getbufferproc</span><span class="p">)(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">obj</span><span class="p">,</span> <span class="n">PyBuffer</span> <span class="o">*</span><span class="n">view</span><span class="p">,</span> <span class="nb">int</span> <span class="n">flags</span><span class="p">)</span> </pre></div> </div> <p>This function returns <code class="docutils literal notranslate"><span class="pre">0</span></code> on success and <code class="docutils literal notranslate"><span class="pre">-1</span></code> on failure (and raises an error). The first variable is the “exporting” object. The second argument is the address to a bufferinfo structure. Both arguments must never be NULL.</p> <p>The third argument indicates what kind of buffer the consumer is prepared to deal with and therefore what kind of buffer the exporter is allowed to return. The new buffer interface allows for much more complicated memory sharing possibilities. Some consumers may not be able to handle all the complexity but may want to see if the exporter will let them take a simpler view to its memory.</p> <p>In addition, some exporters may not be able to share memory in every possible way and may need to raise errors to signal to some consumers that something is just not possible. These errors should be <code class="docutils literal notranslate"><span class="pre">PyErr_BufferError</span></code> unless there is another error that is actually causing the problem. The exporter can use flags information to simplify how much of the PyBuffer structure is filled in with non-default values and/or raise an error if the object can’t support a simpler view of its memory.</p> <p>The exporter should always fill in all elements of the buffer structure (with defaults or NULLs if nothing else is requested). The PyBuffer_FillInfo function can be used for simple cases.</p> <section id="access-flags"> <h3><a class="toc-backref" href="#access-flags" role="doc-backlink">Access flags</a></h3> <p>Some flags are useful for requesting a specific kind of memory segment, while others indicate to the exporter what kind of information the consumer can deal with. If certain information is not asked for by the consumer, but the exporter cannot share its memory without that information, then a <code class="docutils literal notranslate"><span class="pre">PyErr_BufferError</span></code> should be raised.</p> <p><code class="docutils literal notranslate"><span class="pre">PyBUF_SIMPLE</span></code></p> <blockquote> <div>This is the default flag state (0). The returned buffer may or may not have writable memory. The format will be assumed to be unsigned bytes. This is a “stand-alone” flag constant. It never needs to be |’d to the others. The exporter will raise an error if it cannot provide such a contiguous buffer of bytes.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">PyBUF_WRITABLE</span></code></p> <blockquote> <div>The returned buffer must be writable. If it is not writable, then raise an error.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">PyBUF_FORMAT</span></code></p> <blockquote> <div>The returned buffer must have true format information if this flag is provided. This would be used when the consumer is going to be checking for what ‘kind’ of data is actually stored. An exporter should always be able to provide this information if requested. If format is not explicitly requested then the format must be returned as <code class="docutils literal notranslate"><span class="pre">NULL</span></code> (which means “B”, or unsigned bytes)</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">PyBUF_ND</span></code></p> <blockquote> <div>The returned buffer must provide shape information. The memory will be assumed C-style contiguous (last dimension varies the fastest). The exporter may raise an error if it cannot provide this kind of contiguous buffer. If this is not given then shape will be NULL.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">PyBUF_STRIDES</span></code> (implies <code class="docutils literal notranslate"><span class="pre">PyBUF_ND</span></code>)</p> <blockquote> <div>The returned buffer must provide strides information (i.e. the strides cannot be NULL). This would be used when the consumer can handle strided, discontiguous arrays. Handling strides automatically assumes you can handle shape. The exporter may raise an error if cannot provide a strided-only representation of the data (i.e. without the suboffsets).</div></blockquote> <div class="line-block"> <div class="line"><code class="docutils literal notranslate"><span class="pre">PyBUF_C_CONTIGUOUS</span></code></div> <div class="line"><code class="docutils literal notranslate"><span class="pre">PyBUF_F_CONTIGUOUS</span></code></div> <div class="line"><code class="docutils literal notranslate"><span class="pre">PyBUF_ANY_CONTIGUOUS</span></code></div> </div> <blockquote> <div>These flags indicate that the returned buffer must be respectively, C-contiguous (last dimension varies the fastest), Fortran contiguous (first dimension varies the fastest) or either one. All of these flags imply PyBUF_STRIDES and guarantee that the strides buffer info structure will be filled in correctly.</div></blockquote> <p><code class="docutils literal notranslate"><span class="pre">PyBUF_INDIRECT</span></code> (implies <code class="docutils literal notranslate"><span class="pre">PyBUF_STRIDES</span></code>)</p> <blockquote> <div>The returned buffer must have suboffsets information (which can be NULL if no suboffsets are needed). This would be used when the consumer can handle indirect array referencing implied by these suboffsets.</div></blockquote> <p>Specialized combinations of flags for specific kinds of memory_sharing.</p> <blockquote> <div>Multi-dimensional (but contiguous)<blockquote> <div><div class="line-block"> <div class="line"><code class="docutils literal notranslate"><span class="pre">PyBUF_CONTIG</span></code> (<code class="docutils literal notranslate"><span class="pre">PyBUF_ND</span> <span class="pre">|</span> <span class="pre">PyBUF_WRITABLE</span></code>)</div> <div class="line"><code class="docutils literal notranslate"><span class="pre">PyBUF_CONTIG_RO</span></code> (<code class="docutils literal notranslate"><span class="pre">PyBUF_ND</span></code>)</div> </div> </div></blockquote> <p>Multi-dimensional using strides but aligned</p> <blockquote> <div><div class="line-block"> <div class="line"><code class="docutils literal notranslate"><span class="pre">PyBUF_STRIDED</span></code> (<code class="docutils literal notranslate"><span class="pre">PyBUF_STRIDES</span> <span class="pre">|</span> <span class="pre">PyBUF_WRITABLE</span></code>)</div> <div class="line"><code class="docutils literal notranslate"><span class="pre">PyBUF_STRIDED_RO</span></code> (<code class="docutils literal notranslate"><span class="pre">PyBUF_STRIDES</span></code>)</div> </div> </div></blockquote> <p>Multi-dimensional using strides and not necessarily aligned</p> <blockquote> <div><div class="line-block"> <div class="line"><code class="docutils literal notranslate"><span class="pre">PyBUF_RECORDS</span></code> (<code class="docutils literal notranslate"><span class="pre">PyBUF_STRIDES</span> <span class="pre">|</span> <span class="pre">PyBUF_WRITABLE</span> <span class="pre">|</span> <span class="pre">PyBUF_FORMAT</span></code>)</div> <div class="line"><code class="docutils literal notranslate"><span class="pre">PyBUF_RECORDS_RO</span></code> (<code class="docutils literal notranslate"><span class="pre">PyBUF_STRIDES</span> <span class="pre">|</span> <span class="pre">PyBUF_FORMAT</span></code>)</div> </div> </div></blockquote> <p>Multi-dimensional using sub-offsets</p> <blockquote> <div><div class="line-block"> <div class="line"><code class="docutils literal notranslate"><span class="pre">PyBUF_FULL</span></code> (<code class="docutils literal notranslate"><span class="pre">PyBUF_INDIRECT</span> <span class="pre">|</span> <span class="pre">PyBUF_WRITABLE</span> <span class="pre">|</span> <span class="pre">PyBUF_FORMAT</span></code>)</div> <div class="line"><code class="docutils literal notranslate"><span class="pre">PyBUF_FULL_RO</span></code> (<code class="docutils literal notranslate"><span class="pre">PyBUF_INDIRECT</span> <span class="pre">|</span> <span class="pre">PyBUF_FORMAT</span></code>)</div> </div> </div></blockquote> </div></blockquote> <p>Thus, the consumer simply wanting a contiguous chunk of bytes from the object would use <code class="docutils literal notranslate"><span class="pre">PyBUF_SIMPLE</span></code>, while a consumer that understands how to make use of the most complicated cases could use <code class="docutils literal notranslate"><span class="pre">PyBUF_FULL</span></code>.</p> <p>The format information is only guaranteed to be non-NULL if <code class="docutils literal notranslate"><span class="pre">PyBUF_FORMAT</span></code> is in the flag argument, otherwise it is expected the consumer will assume unsigned bytes.</p> <p>There is a C-API that simple exporting objects can use to fill-in the buffer info structure correctly according to the provided flags if a contiguous chunk of “unsigned bytes” is all that can be exported.</p> </section> <section id="the-py-buffer-struct"> <h3><a class="toc-backref" href="#the-py-buffer-struct" role="doc-backlink">The Py_buffer struct</a></h3> <p>The bufferinfo structure is:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">struct</span> <span class="n">bufferinfo</span> <span class="p">{</span> <span class="n">void</span> <span class="o">*</span><span class="n">buf</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="nb">len</span><span class="p">;</span> <span class="nb">int</span> <span class="n">readonly</span><span class="p">;</span> <span class="n">const</span> <span class="n">char</span> <span class="o">*</span><span class="nb">format</span><span class="p">;</span> <span class="nb">int</span> <span class="n">ndim</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="o">*</span><span class="n">shape</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="o">*</span><span class="n">strides</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="o">*</span><span class="n">suboffsets</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="n">itemsize</span><span class="p">;</span> <span class="n">void</span> <span class="o">*</span><span class="n">internal</span><span class="p">;</span> <span class="p">}</span> <span class="n">Py_buffer</span><span class="p">;</span> </pre></div> </div> <p>Before calling the bf_getbuffer function, the bufferinfo structure can be filled with whatever, but the <code class="docutils literal notranslate"><span class="pre">buf</span></code> field must be NULL when requesting a new buffer. Upon return from bf_getbuffer, the bufferinfo structure is filled in with relevant information about the buffer. This same bufferinfo structure must be passed to bf_releasebuffer (if available) when the consumer is done with the memory. The caller is responsible for keeping a reference to obj until releasebuffer is called (i.e. the call to bf_getbuffer does not alter the reference count of obj).</p> <p>The members of the bufferinfo structure are:</p> <dl> <dt><code class="docutils literal notranslate"><span class="pre">buf</span></code></dt><dd>a pointer to the start of the memory for the object</dd> <dt><code class="docutils literal notranslate"><span class="pre">len</span></code></dt><dd>the total bytes of memory the object uses. This should be the same as the product of the shape array multiplied by the number of bytes per item of memory.</dd> <dt><code class="docutils literal notranslate"><span class="pre">readonly</span></code></dt><dd>an integer variable to hold whether or not the memory is readonly. 1 means the memory is readonly, zero means the memory is writable.</dd> <dt><code class="docutils literal notranslate"><span class="pre">format</span></code></dt><dd>a NULL-terminated format-string (following the struct-style syntax including extensions) indicating what is in each element of memory. The number of elements is len / itemsize, where itemsize is the number of bytes implied by the format. This can be NULL which implies standard unsigned bytes (“B”).</dd> <dt><code class="docutils literal notranslate"><span class="pre">ndim</span></code></dt><dd>a variable storing the number of dimensions the memory represents. Must be >=0. A value of 0 means that shape and strides and suboffsets must be <code class="docutils literal notranslate"><span class="pre">NULL</span></code> (i.e. the memory represents a scalar).</dd> <dt><code class="docutils literal notranslate"><span class="pre">shape</span></code></dt><dd>an array of <code class="docutils literal notranslate"><span class="pre">Py_ssize_t</span></code> of length <code class="docutils literal notranslate"><span class="pre">ndims</span></code> indicating the shape of the memory as an N-D array. Note that <code class="docutils literal notranslate"><span class="pre">((*shape)[0]</span> <span class="pre">*</span> <span class="pre">...</span> <span class="pre">*</span> <span class="pre">(*shape)[ndims-1])*itemsize</span> <span class="pre">=</span> <span class="pre">len</span></code>. If ndims is 0 (indicating a scalar), then this must be <code class="docutils literal notranslate"><span class="pre">NULL</span></code>.</dd> <dt><code class="docutils literal notranslate"><span class="pre">strides</span></code></dt><dd>address of a <code class="docutils literal notranslate"><span class="pre">Py_ssize_t*</span></code> variable that will be filled with a pointer to an array of <code class="docutils literal notranslate"><span class="pre">Py_ssize_t</span></code> of length <code class="docutils literal notranslate"><span class="pre">ndims</span></code> (or <code class="docutils literal notranslate"><span class="pre">NULL</span></code> if <code class="docutils literal notranslate"><span class="pre">ndims</span></code> is 0). indicating the number of bytes to skip to get to the next element in each dimension. If this is not requested by the caller (<code class="docutils literal notranslate"><span class="pre">PyBUF_STRIDES</span></code> is not set), then this should be set to NULL which indicates a C-style contiguous array or a PyExc_BufferError raised if this is not possible.</dd> <dt><code class="docutils literal notranslate"><span class="pre">suboffsets</span></code></dt><dd>address of a <code class="docutils literal notranslate"><span class="pre">Py_ssize_t</span> <span class="pre">*</span></code> variable that will be filled with a pointer to an array of <code class="docutils literal notranslate"><span class="pre">Py_ssize_t</span></code> of length <code class="docutils literal notranslate"><span class="pre">*ndims</span></code>. If these suboffset numbers are >=0, then the value stored along the indicated dimension is a pointer and the suboffset value dictates how many bytes to add to the pointer after de-referencing. A suboffset value that it negative indicates that no de-referencing should occur (striding in a contiguous memory block). If all suboffsets are negative (i.e. no de-referencing is needed, then this must be NULL (the default value). If this is not requested by the caller (PyBUF_INDIRECT is not set), then this should be set to NULL or an PyExc_BufferError raised if this is not possible.<p>For clarity, here is a function that returns a pointer to the element in an N-D array pointed to by an N-dimensional index when there are both non-NULL strides and suboffsets:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">void</span> <span class="o">*</span><span class="n">get_item_pointer</span><span class="p">(</span><span class="nb">int</span> <span class="n">ndim</span><span class="p">,</span> <span class="n">void</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="n">Py_ssize_t</span> <span class="o">*</span><span class="n">strides</span><span class="p">,</span> <span class="n">Py_ssize_t</span> <span class="o">*</span><span class="n">suboffsets</span><span class="p">,</span> <span class="n">Py_ssize_t</span> <span class="o">*</span><span class="n">indices</span><span class="p">)</span> <span class="p">{</span> <span class="n">char</span> <span class="o">*</span><span class="n">pointer</span> <span class="o">=</span> <span class="p">(</span><span class="n">char</span><span class="o">*</span><span class="p">)</span><span class="n">buf</span><span class="p">;</span> <span class="nb">int</span> <span class="n">i</span><span class="p">;</span> <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">ndim</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="n">pointer</span> <span class="o">+=</span> <span class="n">strides</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">indices</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="k">if</span> <span class="p">(</span><span class="n">suboffsets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">>=</span><span class="mi">0</span> <span class="p">)</span> <span class="p">{</span> <span class="n">pointer</span> <span class="o">=</span> <span class="o">*</span><span class="p">((</span><span class="n">char</span><span class="o">**</span><span class="p">)</span><span class="n">pointer</span><span class="p">)</span> <span class="o">+</span> <span class="n">suboffsets</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="p">(</span><span class="n">void</span><span class="o">*</span><span class="p">)</span><span class="n">pointer</span><span class="p">;</span> <span class="p">}</span> </pre></div> </div> <p>Notice the suboffset is added “after” the dereferencing occurs. Thus slicing in the ith dimension would add to the suboffsets in the (i-1)st dimension. Slicing in the first dimension would change the location of the starting pointer directly (i.e. buf would be modified).</p> </dd> <dt><code class="docutils literal notranslate"><span class="pre">itemsize</span></code></dt><dd>This is a storage for the itemsize (in bytes) of each element of the shared memory. It is technically un-necessary as it can be obtained using <code class="docutils literal notranslate"><span class="pre">PyBuffer_SizeFromFormat</span></code>, however an exporter may know this information without parsing the format string and it is necessary to know the itemsize for proper interpretation of striding. Therefore, storing it is more convenient and faster.</dd> <dt><code class="docutils literal notranslate"><span class="pre">internal</span></code></dt><dd>This is for use internally by the exporting object. For example, this might be re-cast as an integer by the exporter and used to store flags about whether or not the shape, strides, and suboffsets arrays must be freed when the buffer is released. The consumer should never alter this value.</dd> </dl> <p>The exporter is responsible for making sure that any memory pointed to by buf, format, shape, strides, and suboffsets is valid until releasebuffer is called. If the exporter wants to be able to change an object’s shape, strides, and/or suboffsets before releasebuffer is called then it should allocate those arrays when getbuffer is called (pointing to them in the buffer-info structure provided) and free them when releasebuffer is called.</p> </section> <section id="releasing-the-buffer"> <h3><a class="toc-backref" href="#releasing-the-buffer" role="doc-backlink">Releasing the buffer</a></h3> <p>The same bufferinfo struct should be used in the release-buffer interface call. The caller is responsible for the memory of the Py_buffer structure itself.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">typedef</span> <span class="n">void</span> <span class="p">(</span><span class="o">*</span><span class="n">releasebufferproc</span><span class="p">)(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">obj</span><span class="p">,</span> <span class="n">Py_buffer</span> <span class="o">*</span><span class="n">view</span><span class="p">)</span> </pre></div> </div> <p>Callers of getbufferproc must make sure that this function is called when memory previously acquired from the object is no longer needed. The exporter of the interface must make sure that any memory pointed to in the bufferinfo structure remains valid until releasebuffer is called.</p> <p>If the bf_releasebuffer function is not provided (i.e. it is NULL), then it does not ever need to be called.</p> <p>Exporters will need to define a bf_releasebuffer function if they can re-allocate their memory, strides, shape, suboffsets, or format variables which they might share through the struct bufferinfo. Several mechanisms could be used to keep track of how many getbuffer calls have been made and shared. Either a single variable could be used to keep track of how many “views” have been exported, or a linked-list of bufferinfo structures filled in could be maintained in each object.</p> <p>All that is specifically required by the exporter, however, is to ensure that any memory shared through the bufferinfo structure remains valid until releasebuffer is called on the bufferinfo structure exporting that memory.</p> </section> </section> <section id="new-c-api-calls-are-proposed"> <h2><a class="toc-backref" href="#new-c-api-calls-are-proposed" role="doc-backlink">New C-API calls are proposed</a></h2> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">int</span> <span class="n">PyObject_CheckBuffer</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">obj</span><span class="p">)</span> </pre></div> </div> <p>Return 1 if the getbuffer function is available otherwise 0.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">int</span> <span class="n">PyObject_GetBuffer</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">obj</span><span class="p">,</span> <span class="n">Py_buffer</span> <span class="o">*</span><span class="n">view</span><span class="p">,</span> <span class="nb">int</span> <span class="n">flags</span><span class="p">)</span> </pre></div> </div> <p>This is a C-API version of the getbuffer function call. It checks to make sure object has the required function pointer and issues the call. Returns -1 and raises an error on failure and returns 0 on success.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">void</span> <span class="n">PyBuffer_Release</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">obj</span><span class="p">,</span> <span class="n">Py_buffer</span> <span class="o">*</span><span class="n">view</span><span class="p">)</span> </pre></div> </div> <p>This is a C-API version of the releasebuffer function call. It checks to make sure the object has the required function pointer and issues the call. This function always succeeds even if there is no releasebuffer function for the object.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span> <span class="o">*</span><span class="n">PyObject_GetMemoryView</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">obj</span><span class="p">)</span> </pre></div> </div> <p>Return a memory-view object from an object that defines the buffer interface.</p> <p>A memory-view object is an extended buffer object that could replace the buffer object (but doesn’t have to as that could be kept as a simple 1-d memory-view object). Its C-structure is</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">typedef</span> <span class="n">struct</span> <span class="p">{</span> <span class="n">PyObject_HEAD</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">base</span><span class="p">;</span> <span class="n">Py_buffer</span> <span class="n">view</span><span class="p">;</span> <span class="p">}</span> <span class="n">PyMemoryViewObject</span><span class="p">;</span> </pre></div> </div> <p>This is functionally similar to the current buffer object except a reference to base is kept and the memory view is not re-grabbed. Thus, this memory view object holds on to the memory of base until it is deleted.</p> <p>This memory-view object will support multi-dimensional slicing and be the first object provided with Python to do so. Slices of the memory-view object are other memory-view objects with the same base but with a different view of the base object.</p> <p>When an “element” from the memory-view is returned it is always a bytes object whose format should be interpreted by the format attribute of the memoryview object. The struct module can be used to “decode” the bytes in Python if desired. Or the contents can be passed to a NumPy array or other object consuming the buffer protocol.</p> <p>The Python name will be</p> <p><code class="docutils literal notranslate"><span class="pre">__builtin__.memoryview</span></code></p> <p>Methods:</p> <div class="line-block"> <div class="line"><code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> (will support multi-dimensional slicing)</div> <div class="line"><code class="docutils literal notranslate"><span class="pre">__setitem__</span></code> (will support multi-dimensional slicing)</div> <div class="line"><code class="docutils literal notranslate"><span class="pre">tobytes</span></code> (obtain a new bytes-object of a copy of the memory).</div> <div class="line"><code class="docutils literal notranslate"><span class="pre">tolist</span></code> (obtain a “nested” list of the memory. Everything is interpreted into standard Python objects as the struct module unpack would do – in fact it uses struct.unpack to accomplish it).</div> </div> <p>Attributes (taken from the memory of the base object):</p> <ul class="simple"> <li><code class="docutils literal notranslate"><span class="pre">format</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">itemsize</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">shape</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">strides</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">suboffsets</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">readonly</span></code></li> <li><code class="docutils literal notranslate"><span class="pre">ndim</span></code></li> </ul> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Py_ssize_t</span> <span class="n">PyBuffer_SizeFromFormat</span><span class="p">(</span><span class="n">const</span> <span class="n">char</span> <span class="o">*</span><span class="p">)</span> </pre></div> </div> <p>Return the implied itemsize of the data-format area from a struct-style description.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span> <span class="o">*</span> <span class="n">PyMemoryView_GetContiguous</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">obj</span><span class="p">,</span> <span class="nb">int</span> <span class="n">buffertype</span><span class="p">,</span> <span class="n">char</span> <span class="n">fortran</span><span class="p">)</span> </pre></div> </div> <p>Return a memoryview object to a contiguous chunk of memory represented by obj. If a copy must be made (because the memory pointed to by obj is not contiguous), then a new bytes object will be created and become the base object for the returned memory view object.</p> <p>The buffertype argument can be PyBUF_READ, PyBUF_WRITE, PyBUF_UPDATEIFCOPY to determine whether the returned buffer should be readable, writable, or set to update the original buffer if a copy must be made. If buffertype is PyBUF_WRITE and the buffer is not contiguous an error will be raised. In this circumstance, the user can use PyBUF_UPDATEIFCOPY to ensure that a writable temporary contiguous buffer is returned. The contents of this contiguous buffer will be copied back into the original object after the memoryview object is deleted as long as the original object is writable. If this is not allowed by the original object, then a BufferError is raised.</p> <p>If the object is multi-dimensional, then if fortran is ‘F’, the first dimension of the underlying array will vary the fastest in the buffer. If fortran is ‘C’, then the last dimension will vary the fastest (C-style contiguous). If fortran is ‘A’, then it does not matter and you will get whatever the object decides is more efficient. If a copy is made, then the memory must be freed by calling <code class="docutils literal notranslate"><span class="pre">PyMem_Free</span></code>.</p> <p>You receive a new reference to the memoryview object.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">int</span> <span class="n">PyObject_CopyToObject</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">obj</span><span class="p">,</span> <span class="n">void</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="n">Py_ssize_t</span> <span class="nb">len</span><span class="p">,</span> <span class="n">char</span> <span class="n">fortran</span><span class="p">)</span> </pre></div> </div> <p>Copy <code class="docutils literal notranslate"><span class="pre">len</span></code> bytes of data pointed to by the contiguous chunk of memory pointed to by <code class="docutils literal notranslate"><span class="pre">buf</span></code> into the buffer exported by obj. Return 0 on success and return -1 and raise an error on failure. If the object does not have a writable buffer, then an error is raised. If fortran is ‘F’, then if the object is multi-dimensional, then the data will be copied into the array in Fortran-style (first dimension varies the fastest). If fortran is ‘C’, then the data will be copied into the array in C-style (last dimension varies the fastest). If fortran is ‘A’, then it does not matter and the copy will be made in whatever way is more efficient.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">int</span> <span class="n">PyObject_CopyData</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">dest</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">src</span><span class="p">)</span> </pre></div> </div> <p>These last three C-API calls allow a standard way of getting data in and out of Python objects into contiguous memory areas no matter how it is actually stored. These calls use the extended buffer interface to perform their work.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">int</span> <span class="n">PyBuffer_IsContiguous</span><span class="p">(</span><span class="n">Py_buffer</span> <span class="o">*</span><span class="n">view</span><span class="p">,</span> <span class="n">char</span> <span class="n">fortran</span><span class="p">)</span> </pre></div> </div> <p>Return 1 if the memory defined by the view object is C-style (fortran = ‘C’) or Fortran-style (fortran = ‘F’) contiguous or either one (fortran = ‘A’). Return 0 otherwise.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">void</span> <span class="n">PyBuffer_FillContiguousStrides</span><span class="p">(</span><span class="nb">int</span> <span class="n">ndim</span><span class="p">,</span> <span class="n">Py_ssize_t</span> <span class="o">*</span><span class="n">shape</span><span class="p">,</span> <span class="n">Py_ssize_t</span> <span class="o">*</span><span class="n">strides</span><span class="p">,</span> <span class="n">Py_ssize_t</span> <span class="n">itemsize</span><span class="p">,</span> <span class="n">char</span> <span class="n">fortran</span><span class="p">)</span> </pre></div> </div> <p>Fill the strides array with byte-strides of a contiguous (C-style if fortran is ‘C’ or Fortran-style if fortran is ‘F’ array of the given shape with the given number of bytes per element.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">int</span> <span class="n">PyBuffer_FillInfo</span><span class="p">(</span><span class="n">Py_buffer</span> <span class="o">*</span><span class="n">view</span><span class="p">,</span> <span class="n">void</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="n">Py_ssize_t</span> <span class="nb">len</span><span class="p">,</span> <span class="nb">int</span> <span class="n">readonly</span><span class="p">,</span> <span class="nb">int</span> <span class="n">infoflags</span><span class="p">)</span> </pre></div> </div> <p>Fills in a buffer-info structure correctly for an exporter that can only share a contiguous chunk of memory of “unsigned bytes” of the given length. Returns 0 on success and -1 (with raising an error) on error.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyExc_BufferError</span> </pre></div> </div> <p>A new error object for returning buffer errors which arise because an exporter cannot provide the kind of buffer that a consumer expects. This will also be raised when a consumer requests a buffer from an object that does not provide the protocol.</p> </section> <section id="additions-to-the-struct-string-syntax"> <h2><a class="toc-backref" href="#additions-to-the-struct-string-syntax" role="doc-backlink">Additions to the struct string-syntax</a></h2> <p>The struct string-syntax is missing some characters to fully implement data-format descriptions already available elsewhere (in ctypes and NumPy for example). The Python 2.5 specification is at <a class="reference external" href="http://docs.python.org/library/struct.html">http://docs.python.org/library/struct.html</a>.</p> <p>Here are the proposed additions:</p> <table class="docutils align-default"> <thead> <tr class="row-odd"><th class="head">Character</th> <th class="head">Description</th> </tr> </thead> <tbody> <tr class="row-even"><td>‘t’</td> <td>bit (number before states how many bits)</td> </tr> <tr class="row-odd"><td>‘?’</td> <td>platform _Bool type</td> </tr> <tr class="row-even"><td>‘g’</td> <td>long double</td> </tr> <tr class="row-odd"><td>‘c’</td> <td>ucs-1 (latin-1) encoding</td> </tr> <tr class="row-even"><td>‘u’</td> <td>ucs-2</td> </tr> <tr class="row-odd"><td>‘w’</td> <td>ucs-4</td> </tr> <tr class="row-even"><td>‘O’</td> <td>pointer to Python Object</td> </tr> <tr class="row-odd"><td>‘Z’</td> <td>complex (whatever the next specifier is)</td> </tr> <tr class="row-even"><td>‘&’</td> <td>specific pointer (prefix before another character)</td> </tr> <tr class="row-odd"><td>‘T{}’</td> <td>structure (detailed layout inside {})</td> </tr> <tr class="row-even"><td>‘(k1,k2,…,kn)’</td> <td>multi-dimensional array of whatever follows</td> </tr> <tr class="row-odd"><td>‘:name:’</td> <td>optional name of the preceding element</td> </tr> <tr class="row-even"><td>‘X{}’</td> <td><dl class="simple"> <dt>pointer to a function (optional function</dt><dd>signature inside {} with any return value preceded by -> and placed at the end)</dd> </dl> </td> </tr> </tbody> </table> <p>The struct module will be changed to understand these as well and return appropriate Python objects on unpacking. Unpacking a long-double will return a decimal object or a ctypes long-double. Unpacking ‘u’ or ‘w’ will return Python unicode. Unpacking a multi-dimensional array will return a list (of lists if >1d). Unpacking a pointer will return a ctypes pointer object. Unpacking a function pointer will return a ctypes call-object (perhaps). Unpacking a bit will return a Python Bool. White-space in the struct-string syntax will be ignored if it isn’t already. Unpacking a named-object will return some kind of named-tuple-like object that acts like a tuple but whose entries can also be accessed by name. Unpacking a nested structure will return a nested tuple.</p> <p>Endian-specification (‘!’, ‘@’,’=’,’>’,’<’, ‘^’) is also allowed inside the string so that it can change if needed. The previously-specified endian string is in force until changed. The default endian is ‘@’ which means native data-types and alignment. If un-aligned, native data-types are requested, then the endian specification is ‘^’.</p> <p>According to the struct-module, a number can precede a character code to specify how many of that type there are. The <code class="docutils literal notranslate"><span class="pre">(k1,k2,...,kn)</span></code> extension also allows specifying if the data is supposed to be viewed as a (C-style contiguous, last-dimension varies the fastest) multi-dimensional array of a particular format.</p> <p>Functions should be added to ctypes to create a ctypes object from a struct description, and add long-double, and ucs-2 to ctypes.</p> </section> <section id="examples-of-data-format-descriptions"> <h2><a class="toc-backref" href="#examples-of-data-format-descriptions" role="doc-backlink">Examples of Data-Format Descriptions</a></h2> <p>Here are some examples of C-structures and how they would be represented using the struct-style syntax.</p> <p><named> is the constructor for a named-tuple (not-specified yet).</p> <dl> <dt>float</dt><dd><code class="docutils literal notranslate"><span class="pre">'d'</span></code> <–> Python float</dd> <dt>complex double</dt><dd><code class="docutils literal notranslate"><span class="pre">'Zd'</span></code> <–> Python complex</dd> <dt>RGB Pixel data</dt><dd><code class="docutils literal notranslate"><span class="pre">'BBB'</span></code> <–> (int, int, int) <code class="docutils literal notranslate"><span class="pre">'B:r:</span> <span class="pre">B:g:</span> <span class="pre">B:b:'</span></code> <–> <named>((int, int, int), (‘r’,’g’,’b’))</dd> <dt>Mixed endian (weird but possible)</dt><dd><code class="docutils literal notranslate"><span class="pre">'>i:big:</span> <span class="pre"><i:little:'</span></code> <–> <named>((int, int), (‘big’, ‘little’))</dd> <dt>Nested structure</dt><dd><div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">struct</span> <span class="p">{</span> <span class="nb">int</span> <span class="n">ival</span><span class="p">;</span> <span class="n">struct</span> <span class="p">{</span> <span class="n">unsigned</span> <span class="n">short</span> <span class="n">sval</span><span class="p">;</span> <span class="n">unsigned</span> <span class="n">char</span> <span class="n">bval</span><span class="p">;</span> <span class="n">unsigned</span> <span class="n">char</span> <span class="n">cval</span><span class="p">;</span> <span class="p">}</span> <span class="n">sub</span><span class="p">;</span> <span class="p">}</span> <span class="sd">"""i:ival:</span> <span class="sd"> T{</span> <span class="sd"> H:sval:</span> <span class="sd"> B:bval:</span> <span class="sd"> B:cval:</span> <span class="sd"> }:sub:</span> <span class="sd">"""</span> </pre></div> </div> </dd> <dt>Nested array</dt><dd><div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">struct</span> <span class="p">{</span> <span class="nb">int</span> <span class="n">ival</span><span class="p">;</span> <span class="n">double</span> <span class="n">data</span><span class="p">[</span><span class="mi">16</span><span class="o">*</span><span class="mi">4</span><span class="p">];</span> <span class="p">}</span> <span class="sd">"""i:ival:</span> <span class="sd"> (16,4)d:data:</span> <span class="sd">"""</span> </pre></div> </div> </dd> </dl> <p>Note, that in the last example, the C-structure compared against is intentionally a 1-d array and not a 2-d array data[16][4]. The reason for this is to avoid the confusions between static multi-dimensional arrays in C (which are laid out contiguously) and dynamic multi-dimensional arrays which use the same syntax to access elements, data[0][1], but whose memory is not necessarily contiguous. The struct-syntax <em>always</em> uses contiguous memory and the multi-dimensional character is information about the memory to be communicated by the exporter.</p> <p>In other words, the struct-syntax description does not have to match the C-syntax exactly as long as it describes the same memory layout. The fact that a C-compiler would think of the memory as a 1-d array of doubles is irrelevant to the fact that the exporter wanted to communicate to the consumer that this field of the memory should be thought of as a 2-d array where a new dimension is considered after every 4 elements.</p> </section> <section id="code-to-be-affected"> <h2><a class="toc-backref" href="#code-to-be-affected" role="doc-backlink">Code to be affected</a></h2> <p>All objects and modules in Python that export or consume the old buffer interface will be modified. Here is a partial list.</p> <ul class="simple"> <li>buffer object</li> <li>bytes object</li> <li>string object</li> <li>unicode object</li> <li>array module</li> <li>struct module</li> <li>mmap module</li> <li>ctypes module</li> </ul> <p>Anything else using the buffer API.</p> </section> <section id="issues-and-details"> <h2><a class="toc-backref" href="#issues-and-details" role="doc-backlink">Issues and Details</a></h2> <p>It is intended that this PEP will be back-ported to Python 2.6 by adding the C-API and the two functions to the existing buffer protocol.</p> <p>Previous versions of this PEP proposed a read/write locking scheme, but it was later perceived as a) too complicated for common simple use cases that do not require any locking and b) too simple for use cases that required concurrent read/write access to a buffer with changing, short-living locks. It is therefore left to users to implement their own specific locking scheme around buffer objects if they require consistent views across concurrent read/write access. A future PEP may be proposed which includes a separate locking API after some experience with these user-schemes is obtained</p> <p>The sharing of strided memory and suboffsets is new and can be seen as a modification of the multiple-segment interface. It is motivated by NumPy and the PIL. NumPy objects should be able to share their strided memory with code that understands how to manage strided memory because strided memory is very common when interfacing with compute libraries.</p> <p>Also, with this approach it should be possible to write generic code that works with both kinds of memory without copying.</p> <p>Memory management of the format string, the shape array, the strides array, and the suboffsets array in the bufferinfo structure is always the responsibility of the exporting object. The consumer should not set these pointers to any other memory or try to free them.</p> <p>Several ideas were discussed and rejected:</p> <blockquote> <div>Having a “releaser” object whose release-buffer was called. This was deemed unacceptable because it caused the protocol to be asymmetric (you called release on something different than you “got” the buffer from). It also complicated the protocol without providing a real benefit.<p>Passing all the struct variables separately into the function. This had the advantage that it allowed one to set NULL to variables that were not of interest, but it also made the function call more difficult. The flags variable allows the same ability of consumers to be “simple” in how they call the protocol.</p> </div></blockquote> </section> <section id="code"> <h2><a class="toc-backref" href="#code" role="doc-backlink">Code</a></h2> <p>The authors of the PEP promise to contribute and maintain the code for this proposal but will welcome any help.</p> </section> <section id="examples"> <h2><a class="toc-backref" href="#examples" role="doc-backlink">Examples</a></h2> <section id="ex-1"> <h3><a class="toc-backref" href="#ex-1" role="doc-backlink">Ex. 1</a></h3> <p>This example shows how an image object that uses contiguous lines might expose its buffer:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">struct</span> <span class="n">rgba</span> <span class="p">{</span> <span class="n">unsigned</span> <span class="n">char</span> <span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">a</span><span class="p">;</span> <span class="p">};</span> <span class="n">struct</span> <span class="n">ImageObject</span> <span class="p">{</span> <span class="n">PyObject_HEAD</span><span class="p">;</span> <span class="o">...</span> <span class="n">struct</span> <span class="n">rgba</span><span class="o">**</span> <span class="n">lines</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="n">height</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="n">width</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="n">shape_array</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span> <span class="n">Py_ssize_t</span> <span class="n">stride_array</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span> <span class="n">Py_ssize_t</span> <span class="n">view_count</span><span class="p">;</span> <span class="p">};</span> </pre></div> </div> <p>“lines” points to malloced 1-D array of <code class="docutils literal notranslate"><span class="pre">(struct</span> <span class="pre">rgba*)</span></code>. Each pointer in THAT block points to a separately malloced array of <code class="docutils literal notranslate"><span class="pre">(struct</span> <span class="pre">rgba)</span></code>.</p> <p>In order to access, say, the red value of the pixel at x=30, y=50, you’d use “lines[50][30].r”.</p> <p>So what does ImageObject’s getbuffer do? Leaving error checking out:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">int</span> <span class="n">Image_getbuffer</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="bp">self</span><span class="p">,</span> <span class="n">Py_buffer</span> <span class="o">*</span><span class="n">view</span><span class="p">,</span> <span class="nb">int</span> <span class="n">flags</span><span class="p">)</span> <span class="p">{</span> <span class="n">static</span> <span class="n">Py_ssize_t</span> <span class="n">suboffsets</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">};</span> <span class="n">view</span><span class="o">-></span><span class="n">buf</span> <span class="o">=</span> <span class="bp">self</span><span class="o">-></span><span class="n">lines</span><span class="p">;</span> <span class="n">view</span><span class="o">-></span><span class="nb">len</span> <span class="o">=</span> <span class="bp">self</span><span class="o">-></span><span class="n">height</span><span class="o">*</span><span class="bp">self</span><span class="o">-></span><span class="n">width</span><span class="p">;</span> <span class="n">view</span><span class="o">-></span><span class="n">readonly</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">view</span><span class="o">-></span><span class="n">ndims</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="bp">self</span><span class="o">-></span><span class="n">shape_array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">height</span><span class="p">;</span> <span class="bp">self</span><span class="o">-></span><span class="n">shape_array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">width</span><span class="p">;</span> <span class="n">view</span><span class="o">-></span><span class="n">shape</span> <span class="o">=</span> <span class="o">&</span><span class="bp">self</span><span class="o">-></span><span class="n">shape_array</span><span class="p">;</span> <span class="bp">self</span><span class="o">-></span><span class="n">stride_array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">struct</span> <span class="n">rgba</span><span class="o">*</span><span class="p">);</span> <span class="bp">self</span><span class="o">-></span><span class="n">stride_array</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">struct</span> <span class="n">rgba</span><span class="p">);</span> <span class="n">view</span><span class="o">-></span><span class="n">strides</span> <span class="o">=</span> <span class="o">&</span><span class="bp">self</span><span class="o">-></span><span class="n">stride_array</span><span class="p">;</span> <span class="n">view</span><span class="o">-></span><span class="n">suboffsets</span> <span class="o">=</span> <span class="n">suboffsets</span><span class="p">;</span> <span class="bp">self</span><span class="o">-></span><span class="n">view_count</span> <span class="o">++</span><span class="p">;</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> <span class="nb">int</span> <span class="n">Image_releasebuffer</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="bp">self</span><span class="p">,</span> <span class="n">Py_buffer</span> <span class="o">*</span><span class="n">view</span><span class="p">)</span> <span class="p">{</span> <span class="bp">self</span><span class="o">-></span><span class="n">view_count</span><span class="o">--</span><span class="p">;</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre></div> </div> </section> <section id="ex-2"> <h3><a class="toc-backref" href="#ex-2" role="doc-backlink">Ex. 2</a></h3> <p>This example shows how an object that wants to expose a contiguous chunk of memory (which will never be re-allocated while the object is alive) would do that.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">int</span> <span class="n">myobject_getbuffer</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="bp">self</span><span class="p">,</span> <span class="n">Py_buffer</span> <span class="o">*</span><span class="n">view</span><span class="p">,</span> <span class="nb">int</span> <span class="n">flags</span><span class="p">)</span> <span class="p">{</span> <span class="n">void</span> <span class="o">*</span><span class="n">buf</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="nb">len</span><span class="p">;</span> <span class="nb">int</span> <span class="n">readonly</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">buf</span> <span class="o">=</span> <span class="o">/*</span> <span class="n">Point</span> <span class="n">to</span> <span class="n">buffer</span> <span class="o">*/</span> <span class="nb">len</span> <span class="o">=</span> <span class="o">/*</span> <span class="n">Set</span> <span class="n">to</span> <span class="n">size</span> <span class="n">of</span> <span class="n">buffer</span> <span class="o">*/</span> <span class="n">readonly</span> <span class="o">=</span> <span class="o">/*</span> <span class="n">Set</span> <span class="n">to</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">readonly</span> <span class="o">*/</span> <span class="k">return</span> <span class="n">PyObject_FillBufferInfo</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="nb">len</span><span class="p">,</span> <span class="n">readonly</span><span class="p">,</span> <span class="n">flags</span><span class="p">);</span> <span class="p">}</span> <span class="o">/*</span> <span class="n">No</span> <span class="n">releasebuffer</span> <span class="ow">is</span> <span class="n">necessary</span> <span class="n">because</span> <span class="n">the</span> <span class="n">memory</span> <span class="n">will</span> <span class="n">never</span> <span class="n">be</span> <span class="n">re</span><span class="o">-</span><span class="n">allocated</span> <span class="o">*/</span> </pre></div> </div> </section> <section id="ex-3"> <h3><a class="toc-backref" href="#ex-3" role="doc-backlink">Ex. 3</a></h3> <p>A consumer that wants to only get a simple contiguous chunk of bytes from a Python object, obj would do the following:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Py_buffer</span> <span class="n">view</span><span class="p">;</span> <span class="nb">int</span> <span class="n">ret</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">PyObject_GetBuffer</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="o">&</span><span class="n">view</span><span class="p">,</span> <span class="n">Py_BUF_SIMPLE</span><span class="p">)</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="o">/*</span> <span class="n">error</span> <span class="k">return</span> <span class="o">*/</span> <span class="p">}</span> <span class="o">/*</span> <span class="n">Now</span><span class="p">,</span> <span class="n">view</span><span class="o">.</span><span class="n">buf</span> <span class="ow">is</span> <span class="n">the</span> <span class="n">pointer</span> <span class="n">to</span> <span class="n">memory</span> <span class="n">view</span><span class="o">.</span><span class="n">len</span> <span class="ow">is</span> <span class="n">the</span> <span class="n">length</span> <span class="n">view</span><span class="o">.</span><span class="n">readonly</span> <span class="ow">is</span> <span class="n">whether</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">the</span> <span class="n">memory</span> <span class="ow">is</span> <span class="n">read</span><span class="o">-</span><span class="n">only</span><span class="o">.</span> <span class="o">*/</span> <span class="o">/*</span> <span class="n">After</span> <span class="n">using</span> <span class="n">the</span> <span class="n">information</span> <span class="ow">and</span> <span class="n">you</span> <span class="n">don</span><span class="s1">'t need it anymore */</span> <span class="k">if</span> <span class="p">(</span><span class="n">PyBuffer_Release</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="o">&</span><span class="n">view</span><span class="p">)</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="o">/*</span> <span class="n">error</span> <span class="k">return</span> <span class="o">*/</span> <span class="p">}</span> </pre></div> </div> </section> <section id="ex-4"> <h3><a class="toc-backref" href="#ex-4" role="doc-backlink">Ex. 4</a></h3> <p>A consumer that wants to be able to use any object’s memory but is writing an algorithm that only handle contiguous memory could do the following:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">void</span> <span class="o">*</span><span class="n">buf</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="nb">len</span><span class="p">;</span> <span class="n">char</span> <span class="o">*</span><span class="nb">format</span><span class="p">;</span> <span class="nb">int</span> <span class="n">copy</span><span class="p">;</span> <span class="n">copy</span> <span class="o">=</span> <span class="n">PyObject_GetContiguous</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="o">&</span><span class="n">buf</span><span class="p">,</span> <span class="o">&</span><span class="nb">len</span><span class="p">,</span> <span class="o">&</span><span class="nb">format</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">'A'</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">copy</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="o">/*</span> <span class="n">error</span> <span class="k">return</span> <span class="o">*/</span> <span class="p">}</span> <span class="o">/*</span> <span class="n">process</span> <span class="n">memory</span> <span class="n">pointed</span> <span class="n">to</span> <span class="n">by</span> <span class="n">buffer</span> <span class="k">if</span> <span class="nb">format</span> <span class="ow">is</span> <span class="n">correct</span> <span class="o">*/</span> <span class="o">/*</span> <span class="n">Optional</span><span class="p">:</span> <span class="k">if</span><span class="p">,</span> <span class="n">after</span> <span class="n">processing</span><span class="p">,</span> <span class="n">we</span> <span class="n">want</span> <span class="n">to</span> <span class="n">copy</span> <span class="n">data</span> <span class="kn">from</span> <span class="nn">buffer</span> <span class="n">back</span> <span class="n">into</span> <span class="n">the</span> <span class="nb">object</span> <span class="n">we</span> <span class="n">could</span> <span class="n">do</span> <span class="o">*/</span> <span class="k">if</span> <span class="p">(</span><span class="n">PyObject_CopyToObject</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="nb">len</span><span class="p">,</span> <span class="s1">'A'</span><span class="p">)</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="o">/*</span> <span class="n">error</span> <span class="k">return</span> <span class="o">*/</span> <span class="p">}</span> <span class="o">/*</span> <span class="n">Make</span> <span class="n">sure</span> <span class="n">that</span> <span class="k">if</span> <span class="n">a</span> <span class="n">copy</span> <span class="n">was</span> <span class="n">made</span><span class="p">,</span> <span class="n">the</span> <span class="n">memory</span> <span class="ow">is</span> <span class="n">freed</span> <span class="o">*/</span> <span class="k">if</span> <span class="p">(</span><span class="n">copy</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="n">PyMem_Free</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span> </pre></div> </div> </section> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This PEP is placed in the public domain.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-3118.rst">https://github.com/python/peps/blob/main/peps/pep-3118.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-3118.rst">2023-09-09 17:39:29 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="#rationale">Rationale</a></li> <li><a class="reference internal" href="#proposal-overview">Proposal Overview</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#access-flags">Access flags</a></li> <li><a class="reference internal" href="#the-py-buffer-struct">The Py_buffer struct</a></li> <li><a class="reference internal" href="#releasing-the-buffer">Releasing the buffer</a></li> </ul> </li> <li><a class="reference internal" href="#new-c-api-calls-are-proposed">New C-API calls are proposed</a></li> <li><a class="reference internal" href="#additions-to-the-struct-string-syntax">Additions to the struct string-syntax</a></li> <li><a class="reference internal" href="#examples-of-data-format-descriptions">Examples of Data-Format Descriptions</a></li> <li><a class="reference internal" href="#code-to-be-affected">Code to be affected</a></li> <li><a class="reference internal" href="#issues-and-details">Issues and Details</a></li> <li><a class="reference internal" href="#code">Code</a></li> <li><a class="reference internal" href="#examples">Examples</a><ul> <li><a class="reference internal" href="#ex-1">Ex. 1</a></li> <li><a class="reference internal" href="#ex-2">Ex. 2</a></li> <li><a class="reference internal" href="#ex-3">Ex. 3</a></li> <li><a class="reference internal" href="#ex-4">Ex. 4</a></li> </ul> </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-3118.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>