<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blosc Home Page  (Posts about blosc2 python)</title><link>https://blosc.org/</link><description></description><atom:link href="https://blosc.org/categories/blosc2-python.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:blosc@blosc.org"&gt;The Blosc Developers&lt;/a&gt; </copyright><lastBuildDate>Sun, 19 Jul 2026 11:09:13 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Wrapping C-Blosc2 in Python (a beginner's view)</title><link>https://blosc.org/posts/python-blosc2-initial-release/</link><dc:creator>Marta Iborra</dc:creator><description>&lt;p&gt;An initial release of the Python wrapper for
C-Blosc2 is now available in: &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2"&gt;https://github.com/Blosc/python-blosc2&lt;/a&gt;.
In this blog I will try to explain some of the most difficult aspects that I had to learn in doing this
and how I solved them.&lt;/p&gt;
&lt;p&gt;This work is being made thanks to a grant from the Python Software Foundation.&lt;/p&gt;
&lt;section id="python-views"&gt;
&lt;h2&gt;Python views&lt;/h2&gt;
&lt;p&gt;At university, the first programming language that I learned was Python. But because programming was
new for the majority of the class the subject only covered the basics: basic statements and classes.
And although these were easy to understand, the views were unknown to me (until now).&lt;/p&gt;
&lt;p&gt;To explain what the views are, let’s suppose we have the following code in Python:&lt;/p&gt;
&lt;pre class="literal-block"&gt;&amp;gt;&amp;gt;&amp;gt; import sys
&amp;gt;&amp;gt;&amp;gt; a = []
&amp;gt;&amp;gt;&amp;gt; b = a
&amp;gt;&amp;gt;&amp;gt; sys.getrefcount(a)
3&lt;/pre&gt;
&lt;p&gt;The reference count for the object is 3: a, b and the argument passed to
sys.getrefcount().&lt;/p&gt;
&lt;p&gt;Basically, to avoid making copies of a same variable, Python uses views. Every variable has its counter and until the counter is 0, the variable is not deleted.
But that means that two threads cannot access the counter at the same time.  Because having a lock for every variable would be inefficient and could produce deadlocks (which means that several threads are waiting for each other), the GIL was created.  So GIL was my next thing to learn.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="gil-and-cython"&gt;
&lt;h2&gt;GIL and Cython&lt;/h2&gt;
&lt;p&gt;GIL stands for Global Interpreter Lock. With a single lock
on the interpreter there are no deadlocks. But the execution of any
Python program must acquire the interpreter lock, which prevents some
programs to take advantage of the multi-threading execution.&lt;/p&gt;
&lt;p&gt;When writing C extensions, this lock is very useful because
it can be released. Thus, the program can be more efficient (i.e.
threads can actually run in parallel).
To write a function with the GIL I spent many time reading about it.
Unfortunately, nothing seemed to expain what I wanted to do until
I found this nice
&lt;a class="reference external" href="http://nicolas-hug.com/blog/cython_notes#"&gt;blog&lt;/a&gt;
from
Nicolas Hug
in which he explains the 3 rules you have to follow to make Cython release the GIL.&lt;/p&gt;
&lt;p&gt;First of all, Cython needs to know which C functions that were imported are thread-safe.
This is done by using the &lt;cite&gt;nogil&lt;/cite&gt; statement in the function declaration.
Then, inside the function the &lt;cite&gt;with nogil&lt;/cite&gt; statement lets Cython know that this block is
going to be executed with the GIL released. But to make that code block safe,
there cannot be any Python interaction inside that block.&lt;/p&gt;
&lt;p&gt;To understand it better, an example is shown below:&lt;/p&gt;
&lt;pre class="literal-block"&gt;cdef extern from "math_operation.h":
    int add(int a, int b)nogil

cpdef sum(src, dest):
    cdef int len_src = len(src)
    cdef int len_dest = len(dest)
    cdef int result
    with nogil:
        # Code with the GIL released
        result = add(len_src, len_dest)
    # Code with the GIL, any Python interaction can be done here&lt;/pre&gt;
&lt;p&gt;The function &lt;cite&gt;sum&lt;/cite&gt; returns the result of adding the length of &lt;cite&gt;src&lt;/cite&gt; and &lt;cite&gt;dest&lt;/cite&gt;.
As you can see, the function has been defined with the &lt;cite&gt;cpdef&lt;/cite&gt; statement
instead of the &lt;cite&gt;def&lt;/cite&gt;. The &lt;cite&gt;c&lt;/cite&gt; lets Cython know that
this function can be called with C. So this is necessary when writing a
function with the GIL released, otherwise you will be trying to execute a Python
program without the GIL (which, as explained previously cannot be done).
Notice that &lt;cite&gt;len_src&lt;/cite&gt; and &lt;cite&gt;len_dest&lt;/cite&gt; have also been defined as C integers with the
&lt;cite&gt;cdef int&lt;/cite&gt; statement. If not, it would not be possible to work with them
with the GIL released (the &lt;cite&gt;with nogil&lt;/cite&gt; block).&lt;/p&gt;
&lt;p&gt;On the other hand, the &lt;cite&gt;p&lt;/cite&gt; lets Cython know that this function can be called through Python.
This does not have to be done always, only when you want to call that function from Python.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="cython-typed-memoryviews"&gt;
&lt;h2&gt;Cython typed memoryviews&lt;/h2&gt;
&lt;p&gt;One of the main differences between  the python-blosc
and python-blosc2 API, is that the functions &lt;cite&gt;compress_ptr&lt;/cite&gt;
and &lt;cite&gt;decompress_ptr&lt;/cite&gt; are no longer supported. We decided
to do so, because the Pickle protocol 5 already makes
an optimization of the copies. That way, we could have
a similar performance for &lt;cite&gt;compress_ptr&lt;/cite&gt;
and &lt;cite&gt;decompress_ptr&lt;/cite&gt; but with the functions &lt;cite&gt;pack&lt;/cite&gt;
and &lt;cite&gt;unpack&lt;/cite&gt;.&lt;/p&gt;
&lt;p&gt;However, when timing the functions I realised that
in the majority of the cases,
although
the &lt;cite&gt;compress&lt;/cite&gt; function from python-blosc2 was faster
than the &lt;cite&gt;compress_ptr&lt;/cite&gt;,
the
&lt;cite&gt;decompress&lt;/cite&gt; function was slower than the &lt;cite&gt;decompress_ptr&lt;/cite&gt;.
Thus I checked the code to see if the
speed could somehow be
increased.&lt;/p&gt;
&lt;p&gt;Originally, the code used the Python Buffer Protocol.
which is part of the Python/C API. The Python Buffer Protocol lets
you (among other things) obtain a
pointer to the raw data of an object. But because
it wasn't clear for me wether it needed to do a copy
or not
we decided to work with Cython typed memoryviews.&lt;/p&gt;
&lt;p&gt;Cython typed memoryviews are very similar to
Python memory views, but with the main difference
that the
first ones are a C-level type and therefore
they do not have much Python overhead.
Because it is a C-level type you have to know
the dimension of the buffer from which you want to
obtain
the typed memoryview as well as its data type.&lt;/p&gt;
&lt;p&gt;The shape dimension of the buffer is expressed writing
as many &lt;code class="docutils literal"&gt;:&lt;/code&gt; between brackets as dimensions it has.
If the memory is allocated contiguously, you can write
&lt;code class="docutils literal"&gt;::1&lt;/code&gt; instead in the corresponding dimension.
On the other hand, the type is expressed as you would
do it in Cython.
In the following code, you can see an example for a
one-dimensional numpy array:&lt;/p&gt;
&lt;pre class="literal-block"&gt;import numpy as np
arr = np.ones((10**6,), dtype=np.double)
cdef double [:] typed_view = arr&lt;/pre&gt;
&lt;p&gt;However, if you want to define a function that receives
an object whose type may be unknown,
you will have to create a
Python memoryview and then cast it into the
type you wish as in the next example:&lt;/p&gt;
&lt;pre class="literal-block"&gt;# Get a Python memoryview from an object
mem_view = memoryview(object)
# Cast that memory view into an unsigned char memoryview
cdef unsigned char[:]typed_view = mem_view.cast('B')&lt;/pre&gt;
&lt;p&gt;The 'B' indicates to cast the memoryview type into an
unsigned char.&lt;/p&gt;
&lt;p&gt;But if I run the latter code for a binary Python string,
it produces a runtime error. It
took me 10 minutes to fix the error adding the
&lt;cite&gt;const&lt;/cite&gt; statement to the definition of the Cython
typed memoryview (as shown below), but I spent two
days trying to
understand the error and its solution.&lt;/p&gt;
&lt;pre class="literal-block"&gt;# Get a Python memoryview from an object
mem_view = memoryview(object)
# Cast that memory view into an unsigned char memoryview
cdef const unsigned char[:]typed_view = mem_view.cast('B')&lt;/pre&gt;
&lt;p&gt;The reason why the &lt;cite&gt;const&lt;/cite&gt; statement fixed it, is that a binary Python string is
a read-only buffer. By declaring the
typed memoryview to &lt;cite&gt;const&lt;/cite&gt;, Cython is being told that
the object from the memory view is a read-only buffer
so that it cannot change it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusions"&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;So far, my experience wrapping &lt;cite&gt;C-Blosc2&lt;/cite&gt; has had
some ups and downs.&lt;/p&gt;
&lt;p&gt;One method that I use whenever I learn something new is
to write down a summary of what I read. Sometimes is almost a
copy (therefore some people may find it useless), but
it always works really well for me.
It helps me connect the ideas better or
to build a global idea of what I have or want to do.&lt;/p&gt;
&lt;p&gt;Another aspect I realized when doing this wrapper is that because
I am a stubborn person, I usually tend to
force myself to try to understand something and get frustrated
if I do not.
However,
I have to recognize that sometimes it is better to
forget about it until the next day. Your brain will organize
your ideas at night so that you can invest better your time
the next morning.&lt;/p&gt;
&lt;p&gt;But maybe the most difficult
part for me was the beginning, and therefore
I have to thank Francesc Alted and
Aleix Alcacer for giving me a push into the not always easy
world of Python extensions.&lt;/p&gt;
&lt;/section&gt;</description><category>blosc2 python</category><guid>https://blosc.org/posts/python-blosc2-initial-release/</guid><pubDate>Mon, 10 May 2021 07:32:20 GMT</pubDate></item></channel></rss>