Skip to content

Commit e5441de

Browse files
EmDash00EmDash00
andauthored
Pip buildable (#196)
Credit: EmDash00 * feat. buildable and installable wheels * remove default(none) so clang v17+ doesn't complain * Upgrade pybind11 to v2.13.6, gain support for numpy 2.0 * Add alternate rpaths for normal builds without sckit-build * Make to to install the python files to the wheel * Make the bindings for RobustRegistrationSolver more Pythonic * export compile commands * Make python bindings overall more pythonic, add type interface * add python development dependencies * undo experimental package name change * Fix double declarations of enums while mantaining backwards compatability * Update example * Refactor, fix typos, and format python bindings; annotate OMP_MAX_THREADS as Final * Make python binding of RobustRegistrationSolver constructor backwards compatible with v1.0.0 * Add properties to make things more pythonic --------- Co-authored-by: EmDash00 <[email protected]>
1 parent 9ca20d9 commit e5441de

File tree

12 files changed

+1043
-96
lines changed

12 files changed

+1043
-96
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ project(teaserpp VERSION 1.0.0)
44
set(CMAKE_CXX_STANDARD 14)
55

66
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/" ${CMAKE_MODULE_PATH})
7+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
78

89
# Check build types
910
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
@@ -12,6 +13,10 @@ if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
1213
STRING "Choose the type of build." FORCE)
1314
endif ()
1415

16+
if (DEFINED SKBUILD)
17+
message(STATUS "Building with Scikit-Build")
18+
endif ()
19+
1520
# Options
1621
option(BUILD_TESTS "Build tests" ON)
1722
option(BUILD_TEASER_FPFH "Build TEASER++ wrappers for PCL FPFH estimation." OFF)

cmake/pybind11.CMakeLists.txt.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ project(pybind11-download NONE)
55
include(ExternalProject)
66
ExternalProject_Add(pmc
77
GIT_REPOSITORY https://github.com/pybind/pybind11.git
8-
GIT_TAG v2.11.1
8+
GIT_TAG v2.13.6
99
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/pybind11-src"
1010
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/pybind11-build"
1111
CONFIGURE_COMMAND ""
1212
BUILD_COMMAND ""
1313
INSTALL_COMMAND ""
1414
TEST_COMMAND ""
15-
)
15+
)

pyproject.toml

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
[build-system]
2+
requires = ["scikit_build_core", "pybind11"]
3+
build-backend = "scikit_build_core.build"
4+
5+
[project]
6+
name = "teaserpp_python"
7+
version = "1.1.0"
8+
description = "Python binding for TEASER++"
9+
readme = "README.md"
10+
authors = [
11+
{ name = "Jingnan Shi", email = "[email protected]" },
12+
]
13+
keywords = [
14+
"Point cloud",
15+
"Registration",
16+
"Non-minimal solver",
17+
"Solver",
18+
]
19+
classifiers = [
20+
"Intended Audience :: Developers",
21+
"Intended Audience :: Education",
22+
"Intended Audience :: Other Audience",
23+
"Intended Audience :: Science/Research",
24+
"License :: OSI Approved :: MIT License",
25+
"Operating System :: MacOS",
26+
"Operating System :: Microsoft :: Windows",
27+
"Operating System :: Unix",
28+
"Programming Language :: C++",
29+
"Programming Language :: Python :: 3",
30+
"Programming Language :: Python :: 3.6",
31+
"Programming Language :: Python :: 3.7",
32+
"Programming Language :: Python :: 3.8",
33+
"Programming Language :: Python :: 3.9",
34+
"Programming Language :: Python :: 3.10",
35+
"Programming Language :: Python :: 3.11",
36+
"Programming Language :: Python :: 3.12",
37+
"Programming Language :: Python :: 3.13",
38+
]
39+
dependencies = [
40+
"numpy",
41+
"typing-extensions~=4.2",
42+
]
43+
44+
[project.urls]
45+
Homepage = "https://github.com/MIT-SPARK/TEASER-plusplus"
46+
47+
[tool.scikit-build]
48+
build-dir = "build/{wheel_tag}"
49+
build.verbose = false
50+
cmake.version = ">=3.16"
51+
wheel.install-dir = "teaserpp_python.libs"
52+
53+
[tool.cibuildwheel]
54+
archs = ["auto64"]
55+
skip = ["*-musllinux*", "pp*", "cp36-*"]
56+
57+
[tool.cibuildwheel.macos]
58+
environment = "MACOSX_DEPLOYMENT_TARGET=10.14"
59+
archs = ["auto64", "arm64"]
60+
61+
[tool.pyright]
62+
venvPath = "."
63+
venv = ".venv"
64+
65+
typeCheckingMode = "standard"
66+
reportUnusedImport = "none"
67+
reportUnusedFunction = "none"
68+
reportUnusedVariable = "none"
69+
reportUndefinedVariable = "none"
70+
71+
72+
[tool.ruff]
73+
# Exclude a variety of commonly ignored directories.
74+
exclude = [
75+
".bzr",
76+
".direnv",
77+
".eggs",
78+
".git",
79+
".git-rewrite",
80+
".hg",
81+
".ipynb_checkpoints",
82+
".mypy_cache",
83+
".nox",
84+
".pants.d",
85+
".pyenv",
86+
".pytest_cache",
87+
".pytype",
88+
".ruff_cache",
89+
".svn",
90+
".tox",
91+
".venv",
92+
".vscode",
93+
"__pypackages__",
94+
"_build",
95+
"buck-out",
96+
"build",
97+
"dist",
98+
"node_modules",
99+
"site-packages",
100+
"venv",
101+
]
102+
103+
# Same as Black.
104+
line-length = 88
105+
indent-width = 4
106+
107+
# Assume Python 3.9
108+
target-version = "py39"
109+
110+
[tool.ruff.lint]
111+
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
112+
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
113+
# McCabe complexity (`C901`) by default.
114+
select = ["E", "F", "I001"]
115+
ignore = []
116+
117+
# Allow fix for all enabled rules (when `--fix`) is provided.
118+
fixable = ["ALL"]
119+
unfixable = []
120+
121+
# Allow unused variables when underscore-prefixed.
122+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
123+
124+
[tool.ruff.format]
125+
# Like Black, use double quotes for strings.
126+
quote-style = "double"
127+
128+
# Like Black, indent with spaces, rather than tabs.
129+
indent-style = "space"
130+
131+
# Like Black, respect magic trailing commas.
132+
skip-magic-trailing-comma = false
133+
134+
# Like Black, automatically detect the appropriate line ending.
135+
line-ending = "auto"
136+
137+
# Enable auto-formatting of code examples in docstrings. Markdown,
138+
# reStructuredText code/literal blocks and doctests are all supported.
139+
#
140+
# This is currently disabled by default, but it is planned for this
141+
# to be opt-out in the future.
142+
docstring-code-format = true
143+
144+
# Set the line length limit used when formatting code snippets in
145+
# docstrings.
146+
#
147+
# This only has an effect when the `docstring-code-format` setting is
148+
# enabled.
149+
docstring-code-line-length = "dynamic"
150+
151+
[dependency-groups]
152+
dev = [
153+
"basedpyright>=1.22.0",
154+
"ruff>=0.8.1",
155+
]

python/CMakeLists.txt

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
cmake_minimum_required(VERSION 3.10)
2+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
23

34
project(teaser_python_bindings)
45

@@ -24,16 +25,23 @@ endif ()
2425

2526
# make sure to output the build file to teaserpp_python folder
2627
SET_TARGET_PROPERTIES(teaserpp_python
27-
PROPERTIES
28-
PREFIX ""
29-
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/teaserpp_python"
30-
)
28+
PROPERTIES
29+
OUTPUT_NAME "_teaserpp"
30+
PREFIX ""
31+
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/teaserpp_python"
32+
INSTALL_RPATH "$ORIGIN/../teaserpp_python.libs/lib;$ORIGIN/../../teaser/"
33+
BUILD_WITH_INSTALL_RPATH TRUE
34+
)
3135

3236
# copy package __init__.py file
3337
configure_file(teaserpp_python/__init__.py
3438
${CMAKE_CURRENT_BINARY_DIR}/teaserpp_python/__init__.py
3539
)
3640

41+
configure_file(teaserpp_python/__init__.py
42+
${CMAKE_CURRENT_BINARY_DIR}/teaserpp_python/_teaserpp.pyi
43+
)
44+
3745
# copy setup.py file
3846
configure_file(setup.py.in
3947
${CMAKE_CURRENT_BINARY_DIR}/setup.py
@@ -43,3 +51,13 @@ file(COPY .
4351
DESTINATION .
4452
FILES_MATCHING
4553
PATTERN *.py)
54+
55+
if (DEFINED SKBUILD)
56+
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/teaserpp/
57+
DESTINATION "../teaserpp"
58+
FILES_MATCHING PATTERN "*.py"
59+
PATTERN "*.pyi"
60+
PATTERN "*.so"
61+
)
62+
install(TARGETS teaserpp_python DESTINATION "../teaserpp_python")
63+
endif ()

python/teaserpp_python/__init__.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,58 @@
1-
from .teaserpp_python import *
1+
from functools import wraps
2+
from typing import Callable, NamedTuple
3+
4+
from ._teaserpp import (
5+
OMP_MAX_THREADS,
6+
CertificationResult,
7+
DRSCertifier,
8+
EigSolverType,
9+
InlierGraphFormulation,
10+
InlierSelectionMode,
11+
RegistrationSolution,
12+
RobustRegistrationSolver,
13+
RotationEstimationAlgorithm,
14+
)
15+
16+
# Backwards compatibility with v1.0 so we don't break code
17+
RobustRegistrationSolver.ROTATION_ESTIMATION_ALGORITHM = RotationEstimationAlgorithm
18+
RobustRegistrationSolver.INLIER_SELECTION_MODE = InlierSelectionMode
19+
RobustRegistrationSolver.INLIER_GRAPH_FORMULATION = InlierGraphFormulation
20+
DRSCertifier.EIG_SOLVER_TYPE = EigSolverType
21+
22+
23+
class RobustRegistrationSolverParams(NamedTuple):
24+
noise_bound: float = 0.01
25+
cbar2: float = 1
26+
estimate_scaling: bool = True
27+
rotation_estimation_algorithm: RotationEstimationAlgorithm = (
28+
RotationEstimationAlgorithm.GNC_TLS
29+
)
30+
rotation_gnc_factor: float = 1.4
31+
rotation_max_iterations: int = 100
32+
rotation_cost_threshold: float = 1e-6
33+
rotation_tim_graph: InlierGraphFormulation = InlierGraphFormulation.CHAIN
34+
inlier_selection_mode: InlierSelectionMode = InlierSelectionMode.PMC_EXACT
35+
kcore_heuristic_threshold: float = 0.5
36+
use_max_clique: bool = True
37+
max_clique_exact_solution: bool = True
38+
max_clique_time_limit: int = 3000
39+
max_clique_num_threads: int = OMP_MAX_THREADS
40+
41+
42+
# Do some Python magic
43+
def __init_deco(f: Callable[..., None]):
44+
@wraps(f)
45+
def wrapper(self, *args, **kwargs):
46+
f(self, *args, **kwargs)
47+
self._params = args
48+
49+
return wrapper
50+
51+
52+
@property
53+
def __params_getter(self) -> RobustRegistrationSolverParams:
54+
return self._params
55+
56+
57+
RobustRegistrationSolver.__init__ = __init_deco(RobustRegistrationSolver.__init__)
58+
setattr(RobustRegistrationSolver, "params", __params_getter)

0 commit comments

Comments
 (0)