Building and Importing¶
Compiling the Module¶
The most common way to build CPython extension modules is to use setuptools and
a setup.py
file. We start with a normal setuptools setup.py
file, for
example:
from setuptools import setup, find_packages
setup(
name='fib',
version='0.1.0',
packages=find_packages(),
license='GPL-2',
classifiers=[
'Development Status :: 3 - Alpha',
'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
'Natural Language :: English',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: Implementation :: CPython',
],
)
This defines a new package called fib
with version ‘0.1.0’ and some other
metadata.
Note
In the classifiers we indicate:
Programming Language :: Python :: Implementation :: CPython
This indicates that our package requires CPython extension modules or implementation details and would not work on PyPy, Jython, or other Python implementations.
With the standard boilerplate in place we need to add the C extension specific
parts. Start by importing the Extension
type from setuptools
:
from setuptools import setup, find_packages, Extension
Next, we need to add a list of all of the extension modules we need build to our
call to setup
:
setup(
..., # the arguments from before
ext_modules=[
Extension(
# the qualified name of the extension module to build
'fib.fib',
# the files to compile into our module relative to ``setup.py``
['fib/fib.c'],
),
],
)
The Extension
class makes sure we get the correct CPython headers and flags
which were used to build the CPython invoking setup.py
. We can customize the
build process with arguments to Extension
, but the default is enough to get
us started.
Building¶
Let’s make sure our build environment is working.
Run the following commands from the repo root:
$ cd exercises/fib
$ python setup.py build_ext --inplace
python setup.py build_ext --inplace
uses the setup.py
to build our
extension modules. The --inplace
flag moves the compiled shared objects into
the source tree so that they are in the Python search path.
Importing and Using¶
Now that the modules are built and moved to the search path, we can try to
import and use the code in a normal repl. From the fib
directory startup
ipython
and try some of the following expressions:
In [1]: from fib import fib
In [2]: fib(1)
Out[2]: 1
In [3]: fib(2)
Out[3]: 1
In [4]: fib(3)
Out[4]: 2
In [5]: fib(10)
Out[5]: 55
Invalid Arguments¶
Think back to our definition of pyfib
in C. We started with:
static PyObject *
pyfib(PyObject *self, PyObject *n)
{
unsigned long as_unsigned_long = PyLong_AsUnsignedLong(n);
/* ... */
}
What happens if n
is not actually an int
object? Try fib('a')
or another non-int object.
If C-c
doesn’t kill the session, you might need to use C-z
and then $
kill %1
.
Why Did this Hang?¶
When n
is not an integer object, PyLong_AsUnsignedLong()
raises an
exception and returns (unsigned long) -1
which is
UNSIGNED_LONG_MAX
. We ignore the error and enter the Fibonacci
function’s loop which tries computing the 18446744073709551615
th Fibonacci
number which will take a very long time.