├── .gitignore ├── .travis.yml ├── README.md ├── cext23 ├── __init__.py ├── cffi │ ├── __init__.py │ ├── cextcffi.py │ └── cextcffi_build.py ├── ctypes │ ├── __init__.py │ └── cextctypes.py └── cython │ ├── __init__.py │ ├── _cext.pyx │ └── cextcython.py ├── setup.py ├── src ├── demo.c └── demo.h └── test ├── test_cffi.py ├── test_ctypes.py └── test_cython.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | 47 | # Translations 48 | *.mo 49 | *.pot 50 | 51 | # Django stuff: 52 | *.log 53 | 54 | # Sphinx documentation 55 | docs/_build/ 56 | 57 | # PyBuilder 58 | target/ 59 | 60 | # Cython generated C files 61 | cext23/cython/*.c 62 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | python: 4 | # We don't actually use the Travis Python, but this keeps it organized. 5 | - "2.7" 6 | - "3.4" 7 | - "3.5" 8 | before_install: 9 | # We do this conditionally because it saves us some downloading if the 10 | # version is the same. 11 | - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then 12 | wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh; 13 | else 14 | wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; 15 | fi 16 | - bash miniconda.sh -b -p $HOME/miniconda 17 | - export PATH="$HOME/miniconda/bin:$PATH" 18 | - hash -r 19 | - conda config --set always_yes yes --set changeps1 no 20 | - conda update -q conda 21 | # Useful for debugging any issues with conda 22 | - conda info -a 23 | 24 | - > 25 | conda create -q -n test-environment 26 | python=$TRAVIS_PYTHON_VERSION 27 | cffi cython numpy pip 28 | - source activate test-environment 29 | - pip install pytest 30 | 31 | install: 32 | - python setup.py install 33 | 34 | script: 35 | - py.test test 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/jiffyclub/cext23.svg?branch=master)](https://travis-ci.org/jiffyclub/cext23) 2 | 3 | # C Extensions for Python 2 and 3 4 | 5 | - [Introduction](#introduction) 6 | - [Commonalities](#commonalities) 7 | - [CFFI](#cffi) 8 | - [Cython](#cython) 9 | - [ctypes](#ctypes) 10 | - [Conclusion](#conclusion) 11 | - [Qualifiers](#qualifiers) 12 | 13 | ## Introduction 14 | 15 | Writing pure-Python code that runs unmodified on Python 2 & 3 is doable 16 | with the Python standard library [`__future__` module][future] and 17 | packages like [six][] and [python-future][]. 18 | Writing C extensions can be another matter because Python's C-API 19 | [changed substantially][cporting] from Python 2 to 3. 20 | Working with the Python C-API can be a lot of work even when you aren't 21 | trying to support two versions of Python so it's no surprise that options 22 | exist to bypass writing the Python-C interface in straight C. 23 | As a bonus these can also help you communicate with compiled C libraries 24 | using the same code in both Python 2 and 3. 25 | (And [here][snarky] are even more reasons to avoid the Python C-API.) 26 | Below I'll be describing how to wrap C code using 27 | [CFFI][], [Cython][], and [ctypes][]. 28 | 29 | *Note: I'm writing this as someone who has existing C code that I need to 30 | work with. If you don't already have C and are only looking for performance 31 | improvements then you have [other options][perf-alts] you should explore 32 | before deciding to write C.* 33 | 34 | ### Help Out! 35 | 36 | Pull requests with expanded examples and issues with corrections or 37 | suggestions are appreciated! 38 | I would love for this demo repo to have some more complicated examples, 39 | especially involving NumPy and translating between Python and C data types. 40 | (The integers in the current example map seamlessly between Python 41 | and C, but most other data types do not and it requires some extra setup 42 | in the Python wrapper before sending the data to C.) 43 | 44 | ## Commonalities 45 | 46 | cext23 is a demonstration package with the goal of using different 47 | C interface tools to create identical Python APIs from the same 48 | C source code. 49 | As I describe the interface tools below I'll be linking into the 50 | source code to demonstrate the different aspects of each tool. 51 | 52 | In all of the examples discussed the C code being wrapped is exactly 53 | the same and it won't reference Python at all. 54 | All of the Python to C interface layer will be done in Python and/or 55 | generated by one of the tools discussed below. 56 | The C source used in this example repo lives in the [src/](./src/) directory. 57 | 58 | Another thing I've done the same way for each example is that even when 59 | the process creates an importable Python extension I've created a 60 | pure-Python wrapper for it. 61 | That allows me to put a nice Python interface + docstring on the function, 62 | maybe use keyword arguments or do some error checking, all in Python 63 | where it's easy. 64 | The goal is to use the Python wrapper to give users a good experience 65 | while handing the core of the computation off to compiled C. 66 | 67 | ### Testing 68 | 69 | Tests can be run using [pytest][] by cloning this repo 70 | and running the command `python setup.py test`. 71 | Running the tests requires the following dependencies: 72 | 73 | - CFFI 74 | - Cython 75 | - NumPy 76 | 77 | I have only run the tests on Mac and Linux and I don't think the 78 | ctypes example will work on Windows. 79 | 80 | ## CFFI 81 | 82 | From the [CFFI homepage][CFFI]: 83 | 84 | > C Foreign Function Interface for Python. 85 | > The goal is to provide a convenient and reliable way to call compiled 86 | > C code from Python using interface declarations written in C. 87 | 88 | There are a [number of ways][cffi-overview] to use CFFI, 89 | but when interacting with C code the best strategy is what they call 90 | ["API level, out-of-line"][cffi-api-level]. 91 | In this mode you write a Python file that describes your extension: 92 | what headers to include, what C code to build with, what libraries to 93 | link with, what functions are part of your interface, etc. 94 | The file is plain Python, but does contain C code in strings. 95 | CFFI uses that Python to create a C file with your Python-C interface defined 96 | and then compiles that into a library file that you can import into Python. 97 | Check out the example CFFI-build file 98 | [cextcffi_build.py](./cext23/cffi/cextcffi_build.py) 99 | and the wrapping Python module [cextcffi.py](./cext23/cffi/cextcffi.py). 100 | 101 | Overall using CFFI was a pleasant experience. 102 | I especially like that it handles gathering up all the C code into 103 | a single library/extension via the same interface as the 104 | [distutils Extension class][distutils-ext]. 105 | CFFI also has great [setuptools][] [integration][cffi-dist] 106 | to automatically run the extension building as part of installs 107 | (see [setup.py][]). 108 | 109 | ## Cython 110 | 111 | From the [Cython homepage][Cython]: 112 | 113 | > Cython is an optimising static compiler for both the Python programming 114 | > language and the extended Cython programming language (based on Pyrex). 115 | > It makes writing C extensions for Python as easy as Python itself. 116 | 117 | The Cython process is pretty similar to the CFFI process: 118 | you write a special file that Cython converts to C and that is 119 | then compiled up with other C files into an importable Python library. 120 | In the case of CFFI that special file is written in Python; 121 | with Cython the file is written in [Cython's Python-like langauge][cython-lang]. 122 | The cext23 example is in [_cext.pyx](./cext23/cython/_cext.pyx). 123 | 124 | Like CFFI, Cython also has [distutils integration][cython-dist] so that 125 | Cython extensions can be automatically built during installation. 126 | When distributing releases of your source code you may note want to 127 | distribute your Cython `.pyx` code, instead you can distribute the 128 | Cython generated `.c` files so that folks can build your project without 129 | having Cython available. 130 | Doing this requires [a little finesse][cython-dist-c]. 131 | (Now that Cython is available via things like [Anaconda][] and [Conda][] 132 | it's probably less of a burden on users to have Cython installed. 133 | Binary distribution with [wheels][] and [Conda][] also eliminate the need 134 | for users to compile extensions at all.) 135 | 136 | Cython already has broad usage in the scientific Python community where it's 137 | often used to avoid writing plain-C code at all. 138 | (Cython [works very well with NumPy][cython-numpy].) 139 | If you're only looking for a performance gain via C but don't want to 140 | actually write C then Cython is a good choice. 141 | (Though examine [Numba][] as well.) 142 | 143 | ## ctypes 144 | 145 | From the [ctypes][] documentation: 146 | 147 | > ctypes is a foreign function library for Python. 148 | > It provides C compatible data types, and allows calling functions in 149 | > DLLs or shared libraries. 150 | > It can be used to wrap these libraries in pure Python. 151 | 152 | [ctypes][] is a Python standard library module that allows programmers to 153 | call functions in C libraries from pure-Python modules. 154 | Using ctypes does not involve making a Python C extension, 155 | instead it's calling from pure-Python into plain compiled C. 156 | However, you still need some compiled C to call, and ctypes 157 | does not have any facilities for creating a compiled library 158 | that installs with your Python library. 159 | 160 | For this example I used standard [distutils extension features][distutils-ext] 161 | (see [setup.py][]) to create a compiled library that installs in the same 162 | directory as the [cextctypes.py](./cext23/ctypes/cextctypes.py) module. 163 | One challenge of that is that different Python versions on different systems 164 | create files with different names, so it takes some hacks to find the 165 | one to open with ctypes. 166 | I honestly have no idea if this is the correct way to use ctypes, 167 | I expect that ctypes is mostly meant for calling into separately 168 | installed C libraries and not for creating C-Python extensions. 169 | 170 | ## Conclusion 171 | 172 | | | Strengths | Weaknesses | 173 | | --- | --------- | ---------- | 174 | | [CFFI][] | Builds C extension, plain Python and C | can involve C | 175 | | [Cython][] | Builds C extension, Python-like language | yet another language | 176 | | [ctypes][] | standard library | doesn't help build C extension | 177 | 178 | I was impressed with both CFFI and Cython for their ability to compile 179 | my custom C code together with their generated interfaces to create a 180 | complete, importable Python module. 181 | I'm comfortable writing C and I have existing C code so CFFI's 182 | [API discovery magic][cffi-c-magic] is very attractive. 183 | But people who have to write a lot of C interface code 184 | or don't already know C might appreciate the compact, 185 | Pythonic [Cython language][cython-lang]. 186 | CFFI and Cython both seem like great options and it's going to take 187 | some more experimentation before I can decide which is best for my 188 | immediate needs, especially when it comes to working with NumPy arrays. 189 | 190 | While researching this project I found 191 | [this page][ppug-ext] from the [Python Packaging User Guide][ppug] 192 | very useful. 193 | 194 | ## Qualifiers 195 | 196 | This has been my first experience with CFFI, Cython, and ctypes so 197 | my writeup may contain inaccuracies and/or incompleteness. 198 | 199 | [future]: https://docs.python.org/3/library/__future__.html 200 | [six]: https://pythonhosted.org/six/ 201 | [python-future]: http://python-future.org/overview.html 202 | [cporting]: https://docs.python.org/3/howto/cporting.html 203 | [snarky]: http://www.snarky.ca/try-to-not-use-the-c-api-directly 204 | [CFFI]: http://cffi.readthedocs.org/ 205 | [Cython]: http://cython.org/ 206 | [ctypes]: https://docs.python.org/3/library/ctypes.html 207 | [perf-alts]: https://packaging.python.org/en/latest/extensions/#alternatives-to-handcoded-accelerator-modules 208 | [pytest]: https://pytest.org/ 209 | [cffi-overview]: https://cffi.readthedocs.org/en/latest/overview.html 210 | [cffi-api-level]: https://cffi.readthedocs.org/en/latest/overview.html#real-example-api-level-out-of-line 211 | [cffi-c-magic]: https://cffi.readthedocs.org/en/latest/cdef.html#letting-the-c-compiler-fill-the-gaps 212 | [distutils-ext]: https://docs.python.org/3/distutils/apiref.html#distutils.core.Extension 213 | [setuptools]: https://pythonhosted.org/setuptools/index.html 214 | [cffi-dist]: https://cffi.readthedocs.org/en/latest/cdef.html 215 | [setup.py]: ./setup.py 216 | [cython-lang]: http://docs.cython.org/src/userguide/language_basics.html 217 | [cython-dist]: http://docs.cython.org/src/reference/compilation.html#compiling-with-distutils 218 | [cython-dist-c]: http://docs.cython.org/src/reference/compilation.html#distributing-cython-modules 219 | [cython-numpy]: http://docs.cython.org/src/tutorial/numpy.html 220 | [Anaconda]: https://store.continuum.io/cshop/anaconda/ 221 | [Conda]: http://conda.pydata.org/docs/ 222 | [wheels]: https://wheel.readthedocs.org/en/latest/ 223 | [Numba]: http://numba.pydata.org/ 224 | [ppug-ext]: https://packaging.python.org/en/latest/extensions/ 225 | [ppug]: https://packaging.python.org/en/latest/ 226 | -------------------------------------------------------------------------------- /cext23/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiffyclub/cext23/00cdb36301075ebf2cdb181c65fdb42e230e791f/cext23/__init__.py -------------------------------------------------------------------------------- /cext23/cffi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiffyclub/cext23/00cdb36301075ebf2cdb181c65fdb42e230e791f/cext23/cffi/__init__.py -------------------------------------------------------------------------------- /cext23/cffi/cextcffi.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | # See CFFI docs at https://cffi.readthedocs.org/en/latest/ 4 | from ._cextcffi import ffi, lib 5 | 6 | 7 | def scalar_int_add(x, y): 8 | """ 9 | Add two integers. 10 | 11 | """ 12 | return lib.scalar_int_add(x, y) 13 | 14 | 15 | def np_int32_add(x, y): 16 | """ 17 | Add two integer NumPy arrays elementwise. 18 | 19 | """ 20 | # info on the ndarray.ctypes attribute is at 21 | # http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ctypes.html 22 | # I learned about this via this SO question: 23 | # http://stackoverflow.com/questions/16276268/how-to-pass-a-numpy-array-into-a-cffi-function-and-how-to-get-one-back-out 24 | x_ptr = ffi.cast('int32_t *', x.ctypes.data) 25 | y_ptr = ffi.cast('int32_t *', y.ctypes.data) 26 | out = np.empty_like(x) 27 | out_ptr = ffi.cast('int32_t *', out.ctypes.data) 28 | 29 | lib.np_int32_add(x_ptr, y_ptr, out_ptr, x.size) 30 | 31 | return out 32 | -------------------------------------------------------------------------------- /cext23/cffi/cextcffi_build.py: -------------------------------------------------------------------------------- 1 | # See CFFI docs at https://cffi.readthedocs.org/en/latest/ 2 | from cffi import FFI 3 | 4 | 5 | ffi = FFI() 6 | 7 | # set_source is where you specify all the include statements necessary 8 | # for your code to work and also where you specify additional code you 9 | # want compiled up with your extension, e.g. custom C code you've written 10 | # 11 | # set_source takes mostly the same arguments as distutils' Extension, see: 12 | # https://cffi.readthedocs.org/en/latest/cdef.html#ffi-set-source-preparing-out-of-line-modules 13 | # https://docs.python.org/3/distutils/apiref.html#distutils.core.Extension 14 | ffi.set_source( 15 | 'cext23.cffi._cextcffi', 16 | """ 17 | #include "demo.h" 18 | #include 19 | """, 20 | include_dirs=['src/'], 21 | sources=['src/demo.c'], 22 | extra_compile_args=['--std=c99']) 23 | 24 | # declare the functions, variables, etc. from the stuff in set_source 25 | # that you want to access from your C extension: 26 | # https://cffi.readthedocs.org/en/latest/cdef.html#ffi-cdef-declaring-types-and-functions 27 | ffi.cdef( 28 | """ 29 | int scalar_int_add(int a, int b); 30 | int np_int32_add(int32_t* a, int32_t* b, int32_t* out, int size); 31 | """) 32 | -------------------------------------------------------------------------------- /cext23/ctypes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiffyclub/cext23/00cdb36301075ebf2cdb181c65fdb42e230e791f/cext23/ctypes/__init__.py -------------------------------------------------------------------------------- /cext23/ctypes/cextctypes.py: -------------------------------------------------------------------------------- 1 | # See the ctypes documentation at 2 | # https://docs.python.org/3/library/ctypes.html 3 | import ctypes as ct 4 | import glob 5 | import os.path 6 | import sys 7 | 8 | import numpy as np 9 | 10 | 11 | dirname = os.path.dirname(__file__) 12 | 13 | # have to jump through some hoops here because on Python 3.5(+) the 14 | # compiled .so file has a fancy name, see 15 | # https://docs.python.org/3/whatsnew/3.5.html#build-and-c-api-changes 16 | # 17 | # this is probably not how this should be done in practice, 18 | # and I'm not sure about the best way to compile your C code and make it 19 | # available to your Python file 20 | # 21 | # for this reason ctypes is probably best used with already installed 22 | # C libraries 23 | if os.path.exists(os.path.join(dirname, '_cext.so')): 24 | libfile = '_cext.so' 25 | else: 26 | major = sys.version_info.major 27 | minor = sys.version_info.minor 28 | libfile = glob.glob( 29 | os.path.join(dirname, '_cext.*{}{}*.so'.format(major, minor)))[0] 30 | 31 | lib = os.path.join(dirname, libfile) 32 | cext = ct.cdll.LoadLibrary(lib) 33 | 34 | 35 | def scalar_int_add(x, y): 36 | """ 37 | Add two integers. 38 | 39 | """ 40 | return cext.scalar_int_add(x, y) 41 | 42 | 43 | def np_int32_add(x, y): 44 | """ 45 | Add two integer NumPy arrays elementwise. 46 | 47 | """ 48 | # info on the ndarray.ctypes attribute is at 49 | # http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ctypes.html 50 | out = np.empty_like(x) 51 | cext.np_int32_add( 52 | x.ctypes.data_as(ct.POINTER(ct.c_int32)), 53 | y.ctypes.data_as(ct.POINTER(ct.c_int32)), 54 | out.ctypes.data_as(ct.POINTER(ct.c_int32)), 55 | x.size) 56 | return out 57 | -------------------------------------------------------------------------------- /cext23/cython/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiffyclub/cext23/00cdb36301075ebf2cdb181c65fdb42e230e791f/cext23/cython/__init__.py -------------------------------------------------------------------------------- /cext23/cython/_cext.pyx: -------------------------------------------------------------------------------- 1 | # See Cython docs at http://docs.cython.org/ 2 | 3 | from libc.stdint cimport int32_t 4 | 5 | import numpy as np 6 | cimport numpy as np 7 | 8 | cdef extern from "demo.h": 9 | # the cpdef thing both declares the function, wraps it for Python, 10 | # and includes it as part of the module, see 11 | # http://docs.cython.org/src/tutorial/external.html#external-declarations 12 | cpdef int scalar_int_add(int a, int b) 13 | cdef int np_int32_add(int32_t* a, int32_t* b, int32_t* out, int size) 14 | 15 | 16 | def np_int32_add_wrap(np.ndarray x, np.ndarray y): 17 | # x.data is a pointer to the array data in memory 18 | cdef int32_t* x_ptr = x.data 19 | cdef int32_t* y_ptr = y.data 20 | cdef np.ndarray out = np.empty_like(x) 21 | cdef int32_t* out_ptr = out.data 22 | 23 | np_int32_add(x_ptr, y_ptr, out_ptr, x.size) 24 | 25 | return out 26 | -------------------------------------------------------------------------------- /cext23/cython/cextcython.py: -------------------------------------------------------------------------------- 1 | # See Cython docs at http://docs.cython.org/ 2 | from . import _cext 3 | 4 | 5 | def scalar_int_add(x, y): 6 | """ 7 | Add two integers. 8 | 9 | """ 10 | return _cext.scalar_int_add(x, y) 11 | 12 | 13 | def np_int32_add(x, y): 14 | """ 15 | Add two integer NumPy arrays elementwise. 16 | 17 | """ 18 | return _cext.np_int32_add_wrap(x, y) 19 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import numpy as np 4 | from Cython.Build import cythonize 5 | from setuptools import setup, find_packages, Extension 6 | from setuptools.command.test import test as TestCommand 7 | 8 | 9 | class PyTest(TestCommand): 10 | user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")] 11 | 12 | def initialize_options(self): 13 | TestCommand.initialize_options(self) 14 | self.pytest_args = None 15 | 16 | def finalize_options(self): 17 | TestCommand.finalize_options(self) 18 | self.test_args = [] 19 | self.test_suite = True 20 | 21 | def run_tests(self): 22 | # import here, cause outside the eggs aren't loaded 23 | import pytest 24 | errno = pytest.main(self.pytest_args or '') 25 | sys.exit(errno) 26 | 27 | 28 | # see args descriptions at 29 | # https://docs.python.org/3/distutils/apiref.html#distutils.core.Extension 30 | extensions = [ 31 | # this compiles the code for the ctypes example 32 | Extension( 33 | name='cext23.ctypes._cext', 34 | sources=['src/demo.c'], 35 | include_dirs=['src/'], 36 | extra_compile_args=['--std=c99']), 37 | # this compiles the Cython example 38 | Extension( 39 | name='cext23.cython._cext', 40 | sources=['cext23/cython/_cext.pyx', 'src/demo.c'], 41 | include_dirs=['src/', np.get_include()], 42 | extra_compile_args=['--std=c99'])] 43 | 44 | setup( 45 | name='cext23', 46 | version='0.1dev', 47 | packages=find_packages(), 48 | ext_modules=cythonize(extensions), 49 | setup_requires=['cffi >= 1.1'], 50 | cffi_modules=['cext23/cffi/cextcffi_build.py:ffi'], 51 | install_requires=['cffi >= 1.1', 'cython >= 0.23'], 52 | tests_require=['pytest >= 2.7.3'], 53 | cmdclass={'test': PyTest}) 54 | -------------------------------------------------------------------------------- /src/demo.c: -------------------------------------------------------------------------------- 1 | #include "demo.h" 2 | #include 3 | 4 | int scalar_int_add(int a, int b) { 5 | int c; 6 | c = a + b; 7 | return c; 8 | } 9 | 10 | 11 | int np_int32_add(int32_t* a, int32_t* b, int32_t* out, int size) { 12 | for (int i = 0; i < size; ++i) { 13 | out[i] = a[i] + b[i]; 14 | } 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /src/demo.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int scalar_int_add(int a, int b); 4 | int np_int32_add(int32_t* a, int32_t* b, int32_t* out, int size); 5 | -------------------------------------------------------------------------------- /test/test_cffi.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import numpy.testing as npt 3 | 4 | from cext23.cffi import cextcffi 5 | 6 | 7 | def test_scalar_int_add(): 8 | assert cextcffi.scalar_int_add(33, 98) == 131 9 | 10 | 11 | def test_np_int32_add_1d(): 12 | x = np.arange(10, dtype='int32') 13 | y = np.arange(20, 30, dtype='int32') 14 | 15 | result = cextcffi.np_int32_add(x, y) 16 | 17 | npt.assert_array_equal(result, x + y) 18 | 19 | 20 | def test_np_int32_add_2d(): 21 | x = np.arange(10, dtype='int32').reshape((5, 2)) 22 | y = np.arange(20, 30, dtype='int32').reshape((5, 2)) 23 | 24 | result = cextcffi.np_int32_add(x, y) 25 | 26 | npt.assert_array_equal(result, x + y) 27 | -------------------------------------------------------------------------------- /test/test_ctypes.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import numpy.testing as npt 3 | 4 | from cext23.ctypes import cextctypes 5 | 6 | 7 | def test_scalar_int_add(): 8 | assert cextctypes.scalar_int_add(33, 98) == 131 9 | 10 | 11 | def test_np_int32_add_1d(): 12 | x = np.arange(10, dtype='int32') 13 | y = np.arange(20, 30, dtype='int32') 14 | 15 | result = cextctypes.np_int32_add(x, y) 16 | 17 | npt.assert_array_equal(result, x + y) 18 | 19 | 20 | def test_np_int32_add_2d(): 21 | x = np.arange(10, dtype='int32').reshape((5, 2)) 22 | y = np.arange(20, 30, dtype='int32').reshape((5, 2)) 23 | 24 | result = cextctypes.np_int32_add(x, y) 25 | 26 | npt.assert_array_equal(result, x + y) 27 | -------------------------------------------------------------------------------- /test/test_cython.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import numpy.testing as npt 3 | 4 | from cext23.cython import cextcython 5 | 6 | 7 | def test_scalar_int_add(): 8 | assert cextcython.scalar_int_add(33, 98) == 131 9 | 10 | 11 | def test_np_int32_add_1d(): 12 | x = np.arange(10, dtype='int32') 13 | y = np.arange(20, 30, dtype='int32') 14 | 15 | result = cextcython.np_int32_add(x, y) 16 | 17 | npt.assert_array_equal(result, x + y) 18 | 19 | 20 | def test_np_int32_add_2d(): 21 | x = np.arange(10, dtype='int32').reshape((5, 2)) 22 | y = np.arange(20, 30, dtype='int32').reshape((5, 2)) 23 | 24 | result = cextcython.np_int32_add(x, y) 25 | 26 | npt.assert_array_equal(result, x + y) 27 | --------------------------------------------------------------------------------