Pythran compiles one Python comment into a native module
Ahead of Time compiler for numeric kernels
At a glance
- What is it?
- Pythran turns a typed Python module into a native extension, and the build surface around it is uneven: Debian installs ATLAS for you, macOS needs a config file written by hand, and the README's own Windows section still promises Python 3.5 while everything else says 3.7.
- Who is it for?
- Pythran earns its place when a numeric kernel is hot and its argument types fit on one comment line. Pass on it for exploration code, for anything outside the compiled subset, and for Windows, where the README promises a platform the classifiers deny.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One comment decides what the C++ compiler sees
The unit of work in Pythran is a single module, and the interface the code generator cares about sits in a comment above the function. In the README example the function `dprod` carries one line, `#pythran export dprod(int list, int list)`, and those two type descriptions decide what C++ gets generated. Everything below that comment has to fit a subset of Python the compiler can type.
The subset is narrow on purpose, and the same example shows what fits inside it: a sum over a zip of two lists, a generator expression, and multiplication of the resulting integers. The docstring above it even names `numpy.dot` as supported, so a module can move from a hand written loop to a BLAS call without leaving Python.
Nothing in the interface line names the output file, the module name, or any optimisation flag. The command line handles that part. Type descriptions, compiler flags and the resulting shared object are three separate surfaces here, and keeping them apart is the first thing to understand before touching a real kernel.
Debian gets ATLAS, macOS gets a config file you write yourself
Installing on Debian is a two step affair. The first command pulls C++ and Python level dependencies through the system package manager, the second installs the compiler from PyPI.
$> sudo apt-get install libatlas-base-dev
$> sudo apt-get install python-dev python-ply python-numpy
$> pip install pythranNotice which BLAS that buys you. ATLAS arrives as a system package, and the README writes no config file afterwards, so nothing tells the compiler where the headers live. A conda or mamba user reaches the same package through a different channel instead: `mamba install -c conda-forge pythran`, or the same command with `conda` in place of `mamba`.
macOS is the exception, because OpenBLAS has to be named before the first compilation:
$> pip install pythran
$> brew install openblas
$> printf '[compiler]\nblas=openblas\ninclude_dirs=/usr/local/opt/openblas/include\nlibrary_dirs=/usr/local/opt/openblas/lib' > ~/.pythranrcThat last line is a `printf` behind a single output redirect, so it replaces an existing `~/.pythranrc` instead of adding to it. The keys `blas`, `include_dirs` and `library_dirs` sit under the `[compiler]` section, and the same file can carry `CXX=g++-4.9` and `CC=gcc-4.9` when the default toolchain is wrong. ArchLinux and Fedora take a packaged route around all of this with `pacman -S python-pythran` and `dnf install pythran`.
From dprod.py to an importable dprod.so
A first kernel is four lines of real code. Save the example as `dprod.py` and keep the export comment exactly where it sits, directly under the module docstring:
"""
Naive dotproduct! Pythran supports numpy.dot
"""
#pythran export dprod(int list, int list)
def dprod(l0,l1):
"""WoW, generator expression, zip and sum."""
return sum(x * y for x, y in zip(l0, l1))Compilation is one command, and importing afterwards needs no path change because the extension lands next to the source:
$> pythran dprod.py
$> python -c 'import dprod' # this imports the native module insteadWhat you end up with is a `dprod.so` that shadows the Python module on the import path, which is exactly what the comment on that second command says. Move the shared object out of the way before timing anything, or you will measure whichever version the import picked up. The repository does carry a `.gitignore`, yet the README says nothing about which generated files it covers, so keeping build output out of version control stays with you. More worked kernels ship in the `pythran/tests/cases/` directory, and `pythran --help` covers the command line.
The Windows section promises Python 3.5 against a 3.7 floor
Read the compatibility paragraphs against each other and they disagree. One states that Pythran now supports Python 3.7 and upward, and `pyproject.toml` sets `requires-python = ">=3.7"` to match. Four headings further down, the Windows subsection says support is on going and targets Python 3.5+ with either Visual Studio 2017 or clang-cl.
Package metadata backs the 3.7 floor and contradicts the Windows claim on the same page. The classifiers name `Operating System :: POSIX :: Linux` and `Operating System :: MacOS`, with no Windows entry among them. A reader on Windows is told the compiler targets their platform and told at the same time that its packaging does not.
The last push to the default `master` branch landed on 2026-09-14, so this is not an abandoned tree, and the stale 3.5 line reads like a section that stopped being revisited. Version `0.9.5` is the last point the README mentions Python 2.7 support; from there on, Python 3 only.
Two console scripts ship, only one of them is documented
`pyproject.toml` registers two entry points, and the README names just one:
[project.scripts]
pythran = "pythran.run:run"
pythran-config = "pythran.config:run"The first is the compiler driver you have already used. The second, `pythran-config`, points at `pythran.config:run`, a module the README never mentions. That gap lands precisely where it hurts, on macOS, where the documented fix for OpenBLAS is a hand typed `printf` into `~/.pythranrc` even though a config command installs alongside the compiler.
Coverage for the driver itself exists but is scattered: `pythran --help` handles the CLI, the user guide is the MANUAL file in the doc directory, and DEVGUIDE and TUTORIAL sit next to it. The pydoc lines are untidy. Alongside `pydoc pythran` and `pydoc pythran.typing`, the README offers a third written as `pydoc -b # in the browser`, with no module argument after the flag, so as printed it cannot open a page for anything. Run `pythran --help` and read MANUAL before you start guessing flags.
The build requirement skips setuptools 60 and 61
Nearly all packaging lives in `pyproject.toml`, and `setup.py` has been reduced to two lines that import `setup` and call it. Build requirements are therefore visible in one place, where they read oddly:
[build-system]
requires = ["setuptools>=62; python_version>='3.12'",
"setuptools<60; python_version<'3.12'"]Two environment markers, two disjoint ranges. Python 3.12 and newer take setuptools 62 or later, everything below 3.12 is held under 60, and every release in between satisfies neither marker. A build front end pinned to one of those gaps cannot resolve this file, and nothing offers a workaround, because the build system is not documented anywhere in the README.
Runtime dependencies come from `requirements.txt`, pinned far more tightly than the front end: `ply>=3.4` for the parser, `gast~=0.7.0` and `beniget~=0.5.0` for the AST, plus `numpy`. The Debian instruction to install `python-ply` and `python-numpy` through apt lines up with that list, while the macOS `brew install openblas` line covers a system dependency that no Python file records.
What a kernel must declare before the compiler takes it
Three constraints decide whether a piece of code is a candidate at all. First, the interface has to be declared: with no `#pythran export` line describing argument types, there is nothing concrete for the generator to work from. Second, the body has to stay inside the supported subset, and the boundary of that subset is never enumerated in the README, so a kernel leaning on an unlisted library call or an unusual construct has no documented behaviour to check against before you start guessing.
Third, the project does not claim the maturity its history invites. `pyproject.toml` classifies it as `Development Status :: 4 - Beta` while the rest of the packaging reads like a settled tool, and the upgrade path runs through a Python floor of 3.7 that the Windows section still contradicts.
Failure handling is where the documentation runs out. There is no rollback guidance in the README, no note about deleting a stale generated module after a source change, and no mention of how `pythran-config` handles a file you wrote by hand. Test on a copy.
A BLAS and an OpenMP runtime instead of a compiler
The honest alternative is to skip the compiler entirely. A kernel written against numpy arrays needs no build step: vectorise it, call `numpy.dot`, and leave the source in Python. numpy is a hard requirement of the compiler anyway, so staying with the interpreter costs nothing already paid for.
The other route leaves Python behind on purpose. The repository ships an OpenMP runtime as a top-level package next to the compiler itself, and the macOS configuration shows a real toolchain being driven, with CXX and CC overrides plus `include_dirs` and `library_dirs` pointing into an installed BLAS. When a kernel needs a library call or SIMD units the subset cannot express, hand written C++ with OpenMP and the same BLAS removes the annotation step and the generated file management with it.
Between those sits a comparison the packaging hints at without documenting: `cython` appears in the optional test group of `pyproject.toml` alongside `ipython`, `nbval`, `wheel`, `packaging`, `ninja` and `meson`, and in no required dependency. The README never compares the two, so treat the compiled output of your own kernel as the only reference point.
Editorial conclusion
Pythran earns its place when a numeric kernel is hot and its argument types fit on one comment line. Pass on it for exploration code, for anything outside the compiled subset, and for Windows, where the README promises a platform the classifiers deny. Before you commit, write the export line, run `pythran dprod.py` on a copy of the kernel, and check that the BLAS keys in your `~/.pythranrc` match the library you installed, since the documented macOS command overwrites that file instead of editing it.
Frequently asked questions
Which Python versions does Pythran support?
The README says Pythran now supports Python 3.7 and upward, and `pyproject.toml` sets `requires-python = ">=3.7"` to match, although the Windows subsection still says 3.5+. Python 2.7 support ended with version 0.9.5.
How do I compile a Python module into a native extension with Pythran?
Add an export comment such as `#pythran export dprod(int list, int list)` above the function, then run `pythran dprod.py`. The README states this generates a `dprod.so` you import in place of the Python module.
Does Pythran work on Windows?
The README describes Windows support as on going and targeting Python 3.5+ with Visual Studio 2017 or clang-cl, while `pyproject.toml` lists only Linux and MacOS classifiers and requires Python 3.7 or newer. Compiler selection goes through the CXX and CC environment variables.
How do I point Pythran at OpenBLAS on macOS?
Write a `~/.pythranrc` file with a `[compiler]` section setting `blas=openblas` together with `include_dirs` and `library_dirs`, exactly as the README's `printf` command does after `brew install openblas`. Beware that the printed command overwrites an existing file rather than appending to it.
What does Pythran need at runtime?
`requirements.txt` lists `ply>=3.4`, `gast~=0.7.0`, `beniget~=0.5.0` and `numpy`, and the Debian instructions add ATLAS headers and libraries from the system. The macOS path swaps that system package for OpenBLAS through `~/.pythranrc`.
Where can I find Pythran examples and further documentation?
The README points at the `pythran/tests/cases/` directory for compiled examples and at the MANUAL and TUTORIAL files in the doc directory for users. `pythran --help` covers the command line itself.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/serge-sans-paille-pythran)Was this useful?