Skip to main content

Python package using Pybind11

Installation#

sudo apt install -y libffi-dev python3 python3-pip python3-dev \
&& python3 -m pip install -U pip setuptools wheel twine keyrings.alt pybind11

Package directory structure#

workspace
β”œβ”€β”€ LICENSE.txt
β”œβ”€β”€ MANIFEST.in
β”œβ”€β”€ README.md
β”œβ”€β”€ CHANGELOG
β”œβ”€β”€ setup.cfg
β”œβ”€β”€ setup.py
β”œβ”€β”€ c_src
β”‚ └── ...
└── py_src
└── package
β”œβ”€β”€ test
| β”œβ”€β”€ __init__.py
| └── ...
β”œβ”€β”€ subPackage
β”‚ β”œβ”€β”€ __init__.py
β”‚ └── module1.py
β”œβ”€β”€ __init__.py
└── __main__.py

C/C++#

function#

#include <pybind11/pybind11.h>
namespace py = pybind11;
int add(int i, int j) { return i + j; }
PYBIND11_MODULE(_<package>, m) {
// m.def( "add", &add );
m.def("add",
&add,
"A function which adds two numbers",
py::arg("i") = 1,
py::arg("j") = 2);
}

variable#

#include <pybind11/pybind11.h>
namespace py = pybind11;
PYBIND11_MODULE(_<package>, m) {
// Built-in types and general objects (more on that later)
// are automatically converted.
m.attr("var1") = 10;
// Can be explicitly converted using the function `py::cast`.
py::object var2 = py::cast("It is string");
m.attr("var2") = var2;
}

class#

#include <pybind11/pybind11.h>
#include <string>
namespace py = pybind11;
class TestClass {
public:
TestClass(int a);
~TestClass();
int add(int a, int b);
int sub(int a, int b);
float sub(float a, float b);
std::string get_name(void);
void set_name(std::string name);
private:
std::string name;
};
PYBIND11_MODULE(_<package>, m) {
py::class_<TestClass>(m, "TestClass")
.def(py::init<int>())
.def("add", &TestClass::add)
.def("sub", (int (TestClass::*)(int, int)) & TestClass::sub)
.def("sub", (float (TestClass::*)(float, float)) & TestClass::sub)
// .def_readwrite( "name", &TestClass::name ) // public
.def_property(
"name", &TestClass::get_name, &TestClass::set_name); // private
}

Python#

__init__.py#

from <package>._<package> import *
...

Build#

setup.py#

from glob import glob
from os import path
from setuptools import setup
from pybind11.setup_helpers import ParallelCompile, Pybind11Extension, build_ext
BASE_DIR = path.dirname(path.abspath(__file__))
CHANGELOG_PATH = path.join(BASE_DIR, "CHANGELOG")
with open(CHANGELOG_PATH, "r") as f:
__version__ = f.readline()
__version__ = __version__.split()
__version__ = __version__[1][1:-1]
ParallelCompile("NPY_NUM_BUILD_JOBS").install()
ext_modules = [
Pybind11Extension(
# Ref: distutils.extension.Extension
name="yolov4.common._common",
sources=sorted(glob("c_src/**/*.cpp", recursive=True)),
include_dirs=["c_src/"], # -I
define_macros=[(("VERSION_INFO", __version__))], # -D<string>=<string>
undef_macros=[], # [string] -D<string>
library_dirs=[], # [string] -L<string>
libraries=[], # [string] -l<string>
runtime_library_dirs=[], # [string] -rpath=<string>
extra_objects=[], # [string]
extra_compile_args=[], # [string]
extra_link_args=[], # [string]
),
]
setup(
version=__version__,
ext_modules=ext_modules,
cmdclass={"build_ext": build_ext},
)

You can use __version__ in .cpp files.

#define MACRO_STRINGIFY(x) #x
PYBIND11_MODULE(_<package>, m) {
#ifdef VERSION_INFO
m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO);
#else
m.attr("__version__") = "dev";
#endif
}

setup.cfg#

[metadata]
name = yolov4
url = https://github.com/hhk7734/tensorflow-yolov4
project_urls =
Documentation = https://wiki.loliot.net/docs/lang/python/libraries/yolov4/python-yolov4-about
Source = https://github.com/hhk7734/tensorflow-yolov4
author = Hyeonki Hong
author_email = hhk7734@gmail.com
description = YOLOv4: Optimal Speed and Accuracy of Object Detection
long-description = file: README.md, CHANGELOG
long_description_content_type = text/markdown
keywords = tensorflow, yolo, AI, TPU
license = MIT
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
Operating System :: POSIX :: Linux
Intended Audience :: Developers
Intended Audience :: Science/Research
Topic :: Scientific/Engineering
Topic :: Scientific/Engineering :: Artificial Intelligence
Topic :: Software Development
Topic :: System :: Hardware
[options]
package_dir =
= py_src
packages = find:
zip_safe = False
install_requires =
numpy>=1.18.0
[options.packages.find]
where = py_src

pyproject.toml#

[build-system]
requires = [
"setuptools>=42",
"wheel",
"pybind11>=2.6.0",
]
build-backend = "setuptools.build_meta"

MANIFEST.in#

include LICENSE.txt
include README.md
include CHANGELOG
include c_src/*

Using setuptools#

The preferred approach to building an extension module for python is to compile it with setuptools, which comes with all recent versions of python.

python3 setup.py install
python3 -m pip uninstall <package>

pip 등둝#

dist#

python3 setup.py sdist

Test 등둝/μ„€μΉ˜#

python3 -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*
python3 -m pip install --index-url https://test.pypi.org/simple/ --verbose --user <package>

정식 등둝/μ„€μΉ˜#

python3 -m twine upload dist/*
python3 -m pip install <package>

Reference#

Last updated on