python_example is the smallest honest pybind11 project you can copy
Example pybind11 module built with a Python-based build system
At a glance
- What is it?
- pybind/python_example is a reference extension module that shows how a C++ source file becomes an importable Python package with pybind11, setuptools and a pyproject.toml. It is a teaching artefact, and reading it teaches you more than copying it does.
- Who is it for?
- Use python_example as the reference when you are writing your first pybind11 extension, or when you want the CI, wheel building and documentation scaffolding that a real pybind11 package needs, because it keeps every file small and readable. Do not adopt it as a dependency: it is an example module named python_example with a version of 0.0.1, and its purpose is to be read and copied.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One C++ file, one extension module, one pip package
The scope here is deliberately tiny. The repository is called python_example, its description calls it an example pybind11 module built with a Python-based build system, and the whole extension is one source file, `src/main.cpp`, compiled into a module also named `python_example`.
The project exists to show the plumbing rather than the programming. There is no application logic, no API surface to memorise and no framework to adopt. What it demonstrates is the chain from a C++ translation unit to a Python package you can install with pip and import. The top-level entries show how little is involved: `src/`, `setup.py`, `pyproject.toml`, `docs/`, `tests/`, plus CI configuration under `.github/`.
The `pyproject.toml` is the modern half of that chain. It declares the build backend and the metadata, and the build requirements are short:
[build-system]
requires = [
"setuptools>=61",
"pybind11>=2.10.0",
]
build-backend = "setuptools.build_meta"The project metadata names the distribution `python_example`, gives it version 0.0.1, describes it as a test project using pybind11, lists Sylvain Corlay as the author, and requires Python 3.11 or newer through `requires-python = ">=3.11"`. The README says the same thing in prose: this requires Python 3.11 or later, and for older versions of Python you should look through the commit history.
That version floor is the first decision the example makes for you, and it is worth understanding why it exists before you copy it.
setup.py wires one source file into Pybind11Extension
The `setup.py` is where the extension is actually declared, and it is short enough to read in one go. It reads the version out of `pyproject.toml` at setup time using `tomllib` and `pathlib`, so the version has one home rather than two:
with Path(__file__).with_name("pyproject.toml").open("rb") as f:
__version__ = tomllib.load(f)["project"]["version"]Then the extension list names the module, the source file, and a macro that passes the version string into the compiled code:
ext_modules = [
Pybind11Extension(
"python_example",
["src/main.cpp"],
# Example: passing in the version to the compiled code
define_macros=[("VERSION_INFO", __version__)],
),
]The comments in that file carry two pieces of advice worth lifting. You can set `cxx_std` to 11, 14 or 17, and if you do, `build_ext` can be removed from the command class. You can also set `include_pybind11=false` and add the include directory yourself, for example from a submodule.
There is a third comment that is the most practically useful of the three. If you glob your source files, sort them, to get bit-for-bit reproducible builds. A glob that returns files in filesystem order gives you a different binary on different machines, which matters the moment anyone tries to compare hashes.
Installing from a clone, then importing the module
The installation instructions in the README are two steps: clone the repository, and install it from the local directory. The install command is the only one given:
pip install ./python_exampleRun it against the checkout, and pip builds the extension using the setuptools backend declared in `pyproject.toml`, which pulls in pybind11 and compiles `src/main.cpp`. The result is an importable module.
The README closes with a short test call, which is the fastest way to confirm the build worked:
import python_example
python_example.add(1, 2)A working `add` call is the whole acceptance test. If the import succeeds and the function returns, then the compiler toolchain, the build backend and the pybind11 headers are all in place, and you can start editing `src/main.cpp`.
For the documentation, the project uses Sphinx, and the point of choosing it is that Sphinx can inspect the signatures and docstrings inside the extension module itself to generate the API reference rather than requiring a hand-written stub file. Two commands produce the HTML reference:
cd python_example/docs
make htmlOther output formats are possible, but the README points you at the Sphinx manual for those rather than listing commands. Worth knowing before you plan a docs pipeline: this project documents an extension automatically, it does not document it by hand.
CI: pip builds, cibuildwheel wheels, and a conda recipe
The CI examples are the part of this repository most likely to be reused in a real project, because the packaging story is where hand-rolled extensions usually go wrong.
Two GitHub Actions workflows are referenced directly. The `pip.yml` file is described as a basic recipe for building and testing, and the `wheels.yml` file shows a simple way to produce binary wheels for all platforms using cibuildwheel. The README table also lists separate status badges for pip builds and for cibuildwheel, so the two are tracked independently.
The `pyproject.toml` configures cibuildwheel directly:
[tool.cibuildwheel]
test-command = "python {project}/tests/test.py"
enable = ["pypy", "graalpy"]
build-frontend = "build[uv]"Three things stand out. The wheel test command points at `tests/test.py` inside the project directory, so every wheel is tested after it is built, not merely compiled. The `enable` list asks for PyPy and GraalPy builds in addition to the default CPython targets, which is a lot of interpreters to keep green for an example. And the build frontend is `build[uv]`, so the wheel builds go through uv.
A third workflow, `conda.yml`, has an example of a conda recipe build. The repository also carries a `.appveyor.yml`, so the older Windows CI service is still configured alongside GitHub Actions.
The test dependency is declared as an optional extra, `test = ["pytest"]`, and linting is handled by ruff, configured with `target-version = "py311"` and an `extend-select` list covering flake8-bugbear, isort, pygrep-hooks, Ruff-specific rules and pyupgrade.
What the example does not cover
Read this repository as a starting point, not a template to copy wholesale, because several things a production extension needs are absent by design.
There is no error handling to speak of, no memory management discussion, and no threading. A pybind11 extension that hands Python objects across a thread boundary, or that returns a pointer to memory it does not own, has problems this example never encounters. The `add` function is the entire public API.
There is also no packaging beyond the module itself: no type stubs, no `py.typed` marker, and no mention of how to ship type information to a static checker. If your package is consumed by a typed codebase, that work is yours.
The build configuration is minimal in one specific way. The `pyproject.toml` has no `project.scripts` entry point, so there is no console command, and the `pyproject.toml` has no `dependencies` list, so the compiled module depends on nothing at runtime. Both are decisions you would very likely reverse for a real package.
Finally, the version. It is 0.0.1, which is the honest version for a test project, and the README does not document a release process for the example itself. The repository also has no GitHub releases, so there is no tagged artefact to install and nothing to pin against.
Licence terms and the state of the branch
The licence is the one place where this repository asks you to read carefully rather than skim. The README states that pybind11 is provided under a BSD-style licence found in the LICENSE file, and that by using, distributing or contributing to the project you agree to its terms and conditions. The repository is classified as carrying a custom licence that GitHub cannot categorise, so the LICENSE file is the authoritative text and the summary in the README is not a substitute for it.
The `NOTICE`-style obligations that BSD-style licences typically carry are about retaining the copyright notice, and a derived project that vendors this code has to keep that notice. Read the file yourself before redistributing anything built from it. This is not legal advice, and the point is only that the licence is stated by reference to a file rather than quoted.
On maintenance, the repository is not archived and the last push was on 2026-10-19, so the example is current rather than abandoned. It has no GitHub releases, which for an example project is normal: there is nothing to publish, since you clone it and install it locally.
The default branch is `master` rather than `main`, and the README's own advice for older Python versions points you at the commit history. That combination tells you something useful about how this project expects to be used: as a starting point that you copy and adapt, with the history available when your constraints differ from the current ones.
Editorial conclusion
Use python_example as the reference when you are writing your first pybind11 extension, or when you want the CI, wheel building and documentation scaffolding that a real pybind11 package needs, because it keeps every file small and readable. Do not adopt it as a dependency: it is an example module named python_example with a version of 0.0.1, and its purpose is to be read and copied. Before you build on it, check that your own target Python version matches its 3.11 floor, and read the LICENSE file yourself, since the repository is classified as carrying a custom licence and the README describes pybind11's BSD-style terms rather than a plain MIT grant.
Frequently asked questions
How do I install the python_example pybind11 module?
Clone the repository and install it from the local directory with `pip install ./python_example`. The build uses the setuptools backend declared in pyproject.toml, which compiles src/main.cpp into a module named python_example.
Which Python versions does python_example support?
The project requires Python 3.11 or newer, declared as `requires-python = ">=3.11"` in pyproject.toml. The README says that for older versions of Python you should check the commit history.
How do I build binary wheels for python_example?
The wheels.yml workflow in .github/workflows shows how to build wheels for all platforms using cibuildwheel, which is configured in pyproject.toml with a test command of `python {project}/tests/test.py` and pypy and graalpy enabled.
How is the documentation for the extension generated?
The project uses Sphinx, which can inspect the signatures and documentation strings inside the extension module to produce the API reference. The HTML reference is generated by changing into python_example/docs and running `make html`.
What licence does python_example use?
The README states that pybind11 is provided under a BSD-style licence found in the LICENSE file, and that using, distributing or contributing to the project means agreeing to those terms. The repository is recorded as carrying a custom licence, so the LICENSE file itself is the authoritative text.
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/pybind-python-example)Was this useful?