Skip to content

Commit 1b9d2f5

Browse files
authored
Add conan-py-build example (#231)
* add conan-py-build example * wip * wip
1 parent 5437612 commit 1b9d2f5

10 files changed

Lines changed: 170 additions & 0 deletions

File tree

examples/extensions/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@
1919
#### [Sigstore (cosign)](plugins/sigstore_sign)
2020

2121
- Sign and verify packages with [Sigstore](https://www.sigstore.dev/) using [cosign](https://github.com/sigstore/cosign).
22+
23+
### [Python extension built with conan-py-build](python_build_backend)
24+
25+
- Learn how to build a Python package with a C/C++ extension whose dependencies are managed by Conan through [conan-py-build](https://github.com/conan-io/conan-py-build). [Docs](https://docs.conan.io/2/integrations/python.html)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist/
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
project(myadder LANGUAGES CXX)
3+
4+
find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module)
5+
set(PYBIND11_FINDPYTHON ON)
6+
find_package(pybind11 CONFIG REQUIRED)
7+
find_package(fmt REQUIRED)
8+
9+
pybind11_add_module(_core src/myadder.cpp)
10+
target_link_libraries(_core PRIVATE pybind11::module fmt::fmt)
11+
12+
install(TARGETS _core DESTINATION myadder)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2026 JFrog LTD
4+
5+
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
15+
16+
The above copyright notice and this permission notice shall be included in
17+
all copies or substantial portions of the Software.
18+
19+
20+
21+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27+
THE SOFTWARE.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Python extension built with conan-py-build
2+
3+
A minimal example of a Python package with a C++ extension (built with
4+
**pybind11** and **fmt**) where all the C++ dependencies are managed by
5+
Conan through [`conan-py-build`](https://github.com/conan-io/conan-py-build),
6+
a PEP 517 build backend.
7+
8+
- Conan blog post: https://blog.conan.io/cpp/conan/python/2026/05/05/Introducing-conan-py-build.html
9+
- `conan-py-build` documentation: https://conan-py-build.conan.io
10+
11+
## How it works
12+
13+
- `pyproject.toml` declares `conan-py-build` as the build backend.
14+
- `conanfile.py` is a regular Conan recipe declaring `pybind11` and `fmt`
15+
as requirements, and building the extension with CMake using the same
16+
Python interpreter as `pip`.
17+
- `CMakeLists.txt` builds the `_core` extension module and installs it
18+
into a `myadder` directory, matching the Python package name so the
19+
compiled module ends up next to `__init__.py` in the resulting wheel.
20+
- `python/myadder/__init__.py` re-exports the `add()` function from the
21+
compiled `_core` module.
22+
23+
## Build and test
24+
25+
```bash
26+
git clone https://github.com/conan-io/examples2.git
27+
cd examples2/examples/extensions/python_build_backend
28+
29+
pip wheel . -w dist/
30+
31+
pip install dist/myadder-*.whl
32+
python -c "from myadder import add; add(2, 3)"
33+
```
34+
35+
This prints `2 + 3 = 5` in bold green (via `fmt`'s color support) and returns `5.0`.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import glob
2+
import os
3+
import shutil
4+
5+
from test.examples_tools import run
6+
7+
# ############# Example ################
8+
print("Build a Python C++ extension (pybind11 + fmt) with conan-py-build")
9+
10+
run("pip wheel . -w dist/ -v")
11+
12+
wheels = glob.glob(os.path.join("dist", "myadder-*.whl"))
13+
assert wheels, "No wheel was generated"
14+
15+
run(f"pip install --force-reinstall {wheels[0]}")
16+
17+
output = run('python -c "from myadder import add; add(2, 3)"')
18+
assert "2 + 3 = 5" in output
19+
20+
run("pip uninstall myadder -y")
21+
22+
shutil.rmtree("dist", ignore_errors=True)
23+
shutil.rmtree("build", ignore_errors=True)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import sys
2+
3+
from conan import ConanFile
4+
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
5+
6+
7+
class MyAdderConan(ConanFile):
8+
name = "myadder"
9+
settings = "os", "compiler", "build_type", "arch"
10+
11+
def layout(self):
12+
cmake_layout(self)
13+
14+
def requirements(self):
15+
self.requires("pybind11/3.0.1")
16+
self.requires("fmt/12.1.0")
17+
18+
def generate(self):
19+
# Keep CMake on the same Python interpreter pip uses
20+
tc = CMakeToolchain(self)
21+
tc.cache_variables["Python3_EXECUTABLE"] = sys.executable
22+
tc.generate()
23+
deps = CMakeDeps(self)
24+
deps.generate()
25+
26+
def build(self):
27+
cmake = CMake(self)
28+
cmake.configure()
29+
cmake.build()
30+
31+
def package(self):
32+
cmake = CMake(self)
33+
cmake.install()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[build-system]
2+
requires = ["conan-py-build"]
3+
build-backend = "conan_py_build.build"
4+
5+
[project]
6+
name = "myadder"
7+
version = "0.1.0"
8+
description = "A simple Python package with a C++ extension (pybind11 + fmt) built through Conan"
9+
license = "MIT"
10+
license-files = ["LICENSE"]
11+
requires-python = ">=3.8"
12+
13+
[tool.conan-py-build.wheel]
14+
packages = ["python/myadder"]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from myadder._core import add
2+
3+
__all__ = ["add"]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <pybind11/pybind11.h>
2+
#include <fmt/core.h>
3+
#include <fmt/color.h>
4+
5+
namespace py = pybind11;
6+
7+
double add(double a, double b) {
8+
double result = a + b;
9+
fmt::print(fg(fmt::color::green) | fmt::emphasis::bold,
10+
"{} + {} = {}\n", a, b, result);
11+
return result;
12+
}
13+
14+
PYBIND11_MODULE(_core, m) {
15+
m.doc() = "Simple Python extension using fmt via Conan.";
16+
m.def("add", &add, "Add two numbers and print the result formatted with fmt.",
17+
py::arg("a"), py::arg("b"));
18+
}

0 commit comments

Comments
 (0)