Development Guide
This guide covers the development aspects of ACCV-Lab: how the project is structured, how to add new namespace packages, and how to work with the build system.
Note
For installation instructions, see the Installation Guide.
How It Works
The project uses a shared configuration system where namespace packages are explicitly defined in
namespace_packages_config.py (please also note the comments in the code snippet for more details):
# List of all ACCV-Lab namespace packages
# Each namespace package should:
# - Be a directory under the packages/ subdirectory
# - Have a pyproject.toml and setup.py file for building
# - Be added to this list to be included in builds and documentation
# Please note that:
# - Packages that are not listed here will be ignored when installing all packages, building the
# documentation, running the tests, etc.
# - The order in which the packages are listed here is the order in which they will be installed, and in which
# they will appear in the documentation.
NAMESPACE_PACKAGES = [
# The commented out packages below this line are examples (see the development guide):
#'accvlab.example_package',
#'accvlab.example_skbuild_package',
'accvlab.on_demand_video_decoder',
'accvlab.batching_helpers',
'accvlab.dali_pipeline_framework',
'accvlab.draw_heatmap',
'accvlab.optim_test_tools',
# Add new namespace packages in the same way as above
]
Each namespace package is self-contained with its own setup.py and pyproject.toml files and can be built
and installed independently. The build configuration is handled directly in each package’s setup.py using
shared build utilities from the accvlab_build_config package (located in the build_config/ directory
and installed as part of the ACCV-Lab installation) as well as the pyproject.toml file for the package;
see the Installation Guide for more details on how to build and the
Build Configuration section for more details on how to use the shared build
utilities inside the package’s setup.py file.
Package Structure Overview & Adding new Packages
There are two example projects which showcase how a namespace package is structured. These are
packages/example_package: Showcases a package containing PyTorch extensions built usingCppExtensionandCUDAExtensionprovided by PyTorch as well as an external implementation (see External Implementations section for more details on external implementations) as described below.packages/example_skbuild_package: Showcases a package usingscikit-buildfor C++/CUDA implementation (see the Alternative: SKBuild-Based Packages section for more details on this approach).
First, we will focus on the example_package, and explain how to set it up from scratch.
For scikit-build based packages, the setup is similar. The SKBuild-based approach is described in the
Alternative: SKBuild-Based Packages section.
Overview
To add a new namespace package (e.g., example_package), you need to create:
Component |
Directory |
Purpose |
|---|---|---|
Implementation |
|
Python package with your actual code |
External Implementation |
|
Parts of the package which are built externally (e.g. using CMake) and then copied to the main package |
Documentation |
|
API reference and user guides (template will be auto-generated) |
Tests |
|
Unit tests (will be automatically picked up by the test runner) |
Setup |
|
Package build configuration |
Project Config |
|
Modern Python project configuration and authoritative dependency definition |
Documentation include list (optional) |
|
List additional directories referenced by the docs (besides |
Note
Apart from the above, further folders/files can be included (and made use of manually or added to the
documentation) if needed. A typical use case is to include e.g. an examples directory which is:
Referenced by the documentation (added to
docu_referenced_dirs.txt) and from which code snippets are included in the documentation.Can be used directly by navigating to the
examples/directory and running the contained code.
Structure
The following diagram shows the relevant project structure containing the folders which correspond to the
example_package namespace package:
accvlab/
├── packages/ # Namespace packages directory
│ ├── optim_test_tools/...
│ ├── batching_helpers/...
│ └── example_package/ # ← New namespace package
│ ├── accvlab/ # ← Namespace root
│ │ └── example_package/ # ← Implementation for "example_package" package
│ │ ├── __init__.py
│ │ ├── csrc/ # ← C++/CUDA sources
│ │ └── include/ # ← Headers
│ ├── ext_impl/ # ← Optional: external implementation
│ │ ├── build_and_copy.sh
│ │ └── ...
│ ├── tests/ # ← Tests for "example_package" package
│ ├── docs/ # ← Documentation for "example_package" package
│ ├── setup.py # ← Package build configuration
│ ├── pyproject.toml # ← Project configuration (including dependencies)
│ └── docu_referenced_dirs.txt # ← Optional: list additional directories referenced by the docs (besides `docs/`)
├── build_config/ # Shared build utilities
├── docs/ # Main documentation
└── namespace_packages_config.py # ← Namespace package needs to be listed here
Note that inside the package, there is the directory structure accvlab/example_package. This is where the
Python implementation of the namespace package is located, and it is named according to the package name (in
this case accvlab.example_package). Other packages are structured in the same way, with example_package
replaced by the name of the respective package.
Adding a new Package: Step-by-Step Process
To add a new namespace package (e.g., example_package):
1. Create the Directory Structure
# Create the namespace package directory
mkdir -p packages/example_package
mkdir -p packages/example_package/accvlab/example_package # For the implementation
mkdir -p packages/example_package/accvlab/example_package/csrc # For C++/CUDA sources
mkdir -p packages/example_package/accvlab/example_package/include # For headers
# External implementation directory (optional - see External Implementations section)
mkdir -p packages/example_package/ext_impl
# Documentation include list file (optional - see Documentation Setup Guide)
touch packages/example_package/docu_referenced_dirs.txt
# Examples directory (optional - see Documentation Setup Guide)
mkdir -p packages/example_package/examples
# Tests directory
mkdir -p packages/example_package/tests
# Documentation directory created automatically by docs system as:
# packages/example_package/docs/
2. Create the Implementation
Add your implementation in the packages/example_package/accvlab/example_package folder (including the
package’s Python code and native extensions using the CppExtension and CUDAExtension approach if
applicable).
If more complex C++/CUDA implementations are used, an external implementation can be added in
packages/example_package/ext_impl.
Note
For complex C++/CUDA implementations that require custom build procedures, third-party libraries, or CMake-based builds, there are two alternatives:
External Implementations - Uses custom build scripts and CMake (used in this example)
SKBuild-based packages - Uses
scikit-buildfor CMake integration
Both approaches are described in detail later in this guide. The example_package uses the External
Implementations approach. For more details, see:
External Implementations section for the approach used in this example
Alternative: SKBuild-Based Packages section for the SKBuild approach
Comparison: External Implementations vs. SKBuild section for a detailed comparison of both approaches
3. Create setup.py
Create the setup configuration file that defines how to compile C++/CUDA extensions and configure any external
builds. See packages/example_package/setup.py for a complete working example.
Important
If your package uses external builds (see External Implementations section), you need to:
Call
run_external_build()before callingsetup()to ensure that the external binaries are included in the resulting package.Include the parameters as shown below to the call to
setup()(withexample_packagereplaced by your package’s name).
...
run_external_build(str(Path(__file__).parent))
setup(
...
include_package_data=True,
package_data={
'accvlab.example_package': ['*.so'],
},
zip_safe=False,
....
)
The first 2 arguments ensure that the external binaries are included, while the last argument ensures that the package is not stored as a zip-file, which would prevent the external binaries from being imported correctly.
4. Create pyproject.toml
Create the Python project configuration. This file also defines the package’s runtime and optional dependencies.
See packages/example_package/pyproject.toml for a complete working example.
5. Define Dependencies in pyproject.toml
Add your package dependencies to the [project] section of pyproject.toml. For example, the
packages/example_package/pyproject.toml file contains:
[project]
name = "accvlab.example_package"
version = "0.1.0"
description = "ACCV-Lab Example Package"
requires-python = ">=3.8"
dependencies = [
"torch>=2.0.0",
"numpy>=1.22.2",
"accvlab-build-config>=0.1.0",
]
[project.optional-dependencies]
optional = [
"pytest",
]
Use this pattern for your own namespace package, adapting the dependency names and versions as needed.
6. Add to NAMESPACE_PACKAGES List
This ensures that your package is included in various places, e.g.
in the installation by the
package_manager.shscriptin the documentation
in the code formatting by the
format.shscript
# In namespace_packages_config.py
NAMESPACE_PACKAGES = [
'accvlab.optim_test_tools',
'accvlab.batching_helpers',
'accvlab.dali_pipeline_framework',
'accvlab.on_demand_video_decoder',
'accvlab.example_package', # Add your new namespace package here
]
7. Create Tests
Create test files for your namespace package. See packages/example_package/tests/ for examples.
Note that this includes only tests for the Python functionality. If you use C++ tests, please ensure
that they are performed during the build and in case of external implementation, that
the build_and_copy.sh script returns an error code (e.g. a value != 0) if tests fail.
8. Set Up Documentation
The documentation system will automatically create the structure when you add the namespace package and build the documentation (see the Documentation Setup Guide for more details).
Alternatively, you can generate the documentation structure manually using the following commands:
# Generate documentation structure for the new namespace package
cd docs # ← Assuming you are in the main docs directory (not the package's docs directory)
python3 generate_new_namespace_package_docs.py
python3 update_docs_index.py
This creates:
packages/example_package/docs/index.rst- Table of contents (needs to be present; can be edited if needed)packages/example_package/docs/intro.rst- Manual introduction (customize this!)packages/example_package/docs/api.rst- Auto-generated API reference (can be edited if needed)
Customize the introduction:
Edit packages/example_package/docs/intro.rst to add
Package overview and purpose
Basic usage examples
Performance characteristics
Integration notes
Note that you are mostly free to modify, add, or remove the files & their contents of the documentation.
However, index.rst serves as the “entry point” for the documentation and needs to be present.
Most of the contained packages extend this basic structure considerably to provide more detailed documentation. Please see the Documentation Setup Guide for more details on the documentation system and how to set it up.
9. Test Your Package
# 1. Install the package with your new namespace package
cd packages/example_package
# IMPORTANT: Do not use editable installation (`-e`) for SKBuild-based packages (e.g.
# `on_demand_video_decoder` or `dali_pipeline_framework`), as it would lead to missing binaries and import
# errors.
# It is ok to use `-e` for other packages. However, keep in mind that for any changes in C++ code to take
# effect, the package needs to be re-installed regardless of whether it is installed in editable mode or not.
pip install . --no-build-isolation
# 2. Run tests
pytest tests/ -v
10. Build the Documentation
Important
Ensure all configured namespace packages are installed before building the docs. For detailed instructions (commands and development targets), see the Documentation Setup Guide.
Summary Checklist
When adding a new namespace package, ensure you have:
Implementation:
packages/example_package/accvlab/example_package/with__init__.pyand source codeSetup:
packages/example_package/setup.pywith build configurationProject Config:
packages/example_package/pyproject.tomlwith project metadataConfiguration: Added to
namespace_packages_config.pyTests:
packages/example_package/tests/with test filesDocumentation: Generated with docs scripts and customized intro
Documentation include list (optional):
docu_referenced_dirs.txtcreated and populated if extra folders (e.g.examples/) are referencedExamples (optional):
packages/<package_name>/examples/created and referenced from docs if usedDependencies: Declared runtime and optional dependencies in
pyproject.tomlExternal implementation: (Optional)
packages/example_package/ext_impl/for external buildsExternal build binaries: (If using external builds)
include_package_data=Trueandpackage_dataconfiguration insetup.pyVerification: Package installs, tests pass, docs build
External Implementations
External implementations provide a way to build complex components outside of the standard Python/pybind11 workflow and then integrate them into your namespace package. This is particularly useful for:
Complex CMake projects with multiple dependencies
Third-party libraries that need specific build configurations
Other cases with the need for flexible manual setups
For relatively small and self-contained C++/CUDA implementations, consider using PyTorch’s CppExtension and
CUDAExtension instead.
Note
This section describes the External Implementations approach used in the example_package. For an
alternative approach using scikit-build, see the
Alternative: SKBuild-Based Packages section. A detailed comparison of
both approaches is available in the
Comparison: External Implementations vs. SKBuild section.
External Implementation Structure
External implementations are located in the packages/example_package/ext_impl/ directory. An example is
shown below.
accvlab/
├── packages/
│ └── example_package/
│ ├── ...
│ ├── setup.py
│ ├── pyproject.toml
│ └── ext_impl/
│ ├── build_and_copy.sh
│ ├── CMakeLists.txt
│ ├── src/
│ │ ├── external_algo.cpp
│ │ └── external_algo.cu
│ ├── include/
│ │ └── external_algo.h
│ ├── third_party/
│ └── build/
Required Components
1. Build and Copy Script (build_and_copy.sh)
Every external implementation must have this script,
which handles the external build process and copies results to the main package. See
packages/example_package/ext_impl/build_and_copy.sh for a complete working example.
2. Build Configuration (CMakeLists.txt)
Typically, the external build is performed using CMake (although this is not strictly necessary).
See packages/example_package/ext_impl/CMakeLists.txt for a complete working example.
Integration with Build System
External implementations are automatically handled by the main setup.py in a generic manner (by calling
accvlab_build_config.run_external_build()). Please ensure that you set up the setup.py correctly (as
described in the setup.py configuration section above).
Note
External implementations can be used alongside standard C++/CUDA extensions in the same namespace package. You can combine both approaches - use external implementations for complex components while still having standard extensions for simpler functionality.
The setup process works as follows:
Selective Detection:
setup.pydetects whether an external implementation is present and calls thebuild_and_copy.shscript (which is responsible for building & inserting the binaries into the package).Standard Processing: After the external build completes for a namespace package, the obtained binaries are inside the package, and standard installation (including extension processing) can be used. Note that the build binaries are copied to the installed package if
setup()is called with the correct parameters (see the setup.py configuration section above).
This means your setup.py remains simple, and only the changes described in the
setup.py configuration section above are needed to use external implementations.
Key Points:
External implementations are only built for namespace packages listed in
namespace_packages_config.py(as installation is only triggered for those).External builds are executed before each namespace package’s call to
setup(), i.e. before potential extension processing.The external build script copies its outputs to the main package directory.
External implementations can coexist with standard extensions - you can have both in the same namespace package.
No special external build logic is needed in
setup.py, only ensure to callrun_external_build()and ensure the resulting files are included in the package (see setup.py configuration).
Note
The fact that external builds are performed before calls to setup() and are copied into the
package means that it is possible to use the external builds inside standard PyTorch extensions built with
CppExtension and CUDAExtension.
External Implementation Best Practices
Error Handling
Use
set -ein build scripts to fail fast on errorsProvide clear error messages for build failures
Check for required tools and dependencies before building
Path Management
Make scripts work from any working directory
Use variables for commonly referenced paths
Build Reproducibility
Pin dependency versions where possible
Use consistent compiler flags and options
Clean build directories before building
Including External Build Binaries
When using external builds, you must configure setup.py to include the built binaries in the installed
package:
setup(
# ... other configuration ...
# `include_package_data` and `package_data` are needed for the binaries created by the external build
# to be included in the package.
include_package_data=True,
package_data={
'accvlab.example_package': ['*.so'], # Include all .so files in the package
},
zip_safe=False,
# ... rest of configuration ...
)
Important
Without this configuration, the binaries created by external builds will not be included in the installed package, causing import errors when users try to use your package.
Pattern matching: The package_data configuration uses glob patterns to match files. Common patterns:
['*.so']- Include all shared object files['lib/*.so']- Include .so files in a lib subdirectory
Alternative: SKBuild-Based Packages
You can also use SKBuild (scikit-build) instead of the external implementation approach discussed above.
See packages/example_skbuild_package/ for a complete example. Note that SKBuild-based packages cannot be
used in combination with PyTorch’s CppExtension and CUDAExtension in setup.py. All PyTorch extensions
must be set up as targets in the CMakeLists.txt instead.
SKBuild packages:
Use
from skbuild import setupinstead offrom setuptools import setupInclude
cmake_source_dirandcmake_install_dirparameters insetup.py(see below for details)Use CMake for building C++/CUDA extensions
Don’t require additional
build_and_copy.shscripts (and calls torun_external_build()), simplifying the setup
SKBuild Package Structure
An SKBuild-based package follows this structure:
packages/example_skbuild_package/
├── ...
├── setup.py
├── pyproject.toml
└── ext_impl/
├── CMakeLists.txt
├── src/
│ ├── external_cuda_ops.cpp
│ └── external_cuda_ops.cu
└── include/
└── external_cuda_ops.h
SKBuild Setup Configuration
1. Setting up setup.py with SKBuild
See packages/example_skbuild_package/setup.py for a complete working example.
Key Differences from the external implementation-based approach:
No
run_external_build()callsNo
package_dataconfiguration (SKBuild handles this automatically)Uses
cmake_source_dirandcmake_install_dirparameters insetup.py. Please see the important note for cmake install configuration section below for details on how these parameters need to be configured.Cannot be used in combination with PyTorch’s
CppExtensionandCUDAExtensioninsetup.py. The extensions must be set up as targets in theCMakeLists.txt.
2. CMakeLists.txt Configuration
See packages/example_skbuild_package/ext_impl/CMakeLists.txt for a complete working example.
Key Differences from External Implementations:
The overall setup is simpler - no need for
build_and_copy.sh, i.e. no manual path calculations or copying of filesInstall target needs to be set up (see the important note for cmake install configuration).
3. Setting up pyproject.toml with SKBuild
See packages/example_skbuild_package/pyproject.toml for a complete working example.
Key Difference from External Implementations: Includes scikit-build and pybind11 as build
requirements.
4. Python Package Structure
See packages/example_skbuild_package/accvlab/example_skbuild_package/__init__.py and
packages/example_skbuild_package/accvlab/example_skbuild_package/functions/functions.py for complete working
examples.
This is similar to the external implementation-based case in that a binary is present in the final package,
which can be imported in Python and exposes relevant functionality as defined in the C++ implementation using
pybind11.
SKBuild Best Practices
1. Package Discovery Configuration & CMake Install Configuration
Package Discovery Configuration
SKBuild requires careful configuration to ensure extensions are placed correctly in the final package:
# In setup.py
setup(
# ... other configuration ...
cmake_install_dir="accvlab/example_skbuild_package", # Must match package structure
)
CMake Install Configuration
The CMake install target must match the cmake_install_dir. Note that this directory is the working as used
when setting up the install target in the CMakeLists.txt, so that the path needs to be set to .:
# Install target - destination must be relative to cmake_install_dir
install(TARGETS accvlab_example_skbuild_package_ext
LIBRARY DESTINATION . # Installs to cmake_install_dir
RUNTIME DESTINATION .
)
Important Note for CMake Install Configuration
Note that the cmake_install_dir (set to accvlab/example_skbuild_package) in setup.py and the CMake
installation directory (set to .) in CMakeLists.txt must point to the same directory in order for the
binary to be included in the wheel in the correct way (i.e. not as data). If this is not the case, it cannot
be imported from the installed package.
However, the paths in setup.py and CMakeLists.txt both use relative paths, but relative to different base
locations. While the cmake_install_dir in setup.py relative to that setup.py, the CMake install
configuration is relative to the package root. The package root is located at
accvlab/example_skbuild_package (relative to the setup.py) in this case (and in general at
accvlab/<package_name> for the setup used in this repo). Therefore, the general guideline is:
In
setup.py, setcmake_install_dirto the root of the package (i.e.accvlab/<package_name>)In the
CMakeLists.txt, set the installation directory to..
2. Extension Module Naming
Ensure the extension module name matches Python import expectations:
# Set output name to match Python import
set_target_properties(accvlab_example_skbuild_package_ext PROPERTIES
OUTPUT_NAME "_ext" # Results in _ext.so
PREFIX "" # No lib prefix
)
3. PyTorch Integration
For PyTorch extensions, ensure proper CMake configuration:
# Find PyTorch CMake files
execute_process(
COMMAND "python3" -c "import torch; import os; print(os.path.join(os.path.dirname(torch.__file__), 'share', 'cmake'))"
OUTPUT_VARIABLE TORCH_CMAKE_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
list(APPEND CMAKE_PREFIX_PATH "${TORCH_CMAKE_PATH}")
# Add PyTorch extension definitions
target_compile_definitions(accvlab_example_skbuild_package_ext PRIVATE
TORCH_EXTENSION_NAME=_ext
TORCH_API_INCLUDE_EXTENSION_H
)
Comparison: External Implementations vs. SKBuild
Both External Implementations and SKBuild use CMake for building C++/CUDA extensions, but they differ significantly in how they integrate with Python packaging:
SKBuild vs. External Implementations (CMake-Based)
Aspect |
External Implementations |
SKBuild |
|---|---|---|
Build Integration |
Automated via |
Integrated with pip/setuptools |
Package Discovery |
Automated file copying via build script |
Automatic package inclusion |
Development Workflow |
Single |
Single |
Complexity |
Medium to High (automated build script + setup.py integration) |
Medium (CMake + Python integration) |
Use Case |
Complex external projects with custom build procedures |
CMake-based Python extensions |
Build Scripts |
Required custom |
No custom scripts needed |
File Management |
Copying of |
Automatic placement in package |
SKBuild vs. External Implementations: Workflow Comparison
External Implementations Workflow:
# Single step - setup.py automatically calls build_and_copy.sh
cd packages/example_package
pip install {-e} . --no-build-isolation
SKBuild Workflow:
# Single step - SKBuild handles everything
cd packages/example_skbuild_package
pip install . --no-build-isolation
Note
{-e} means that this argument is optional. It is not present in the SKBuild-based example as
SKBuild does not support editable installs.
Note
Both approaches use a single pip install command. The difference is that external implementations
automatically execute the build_and_copy.sh script during the setup process, while SKBuild handles the CMake
build process internally.
When to Use SKBuild vs. External Implementations
Use SKBuild when:
You have CMake-based C++/CUDA code that you want to integrate as Python extensions
You want to avoid writing custom build scripts and rely on SKBuild’s built-in CMake integration
You prefer a more standardized approach to CMake-based Python packaging
You want to leverage CMake’s features while maintaining Python packaging standards
Use External Implementations when:
You want to use CMakeLists for parts of the implementation while defining PyTorch extensions directly in
setup.pyYou have very complex external projects with their own build systems
You need to integrate with third-party libraries that require specific build procedures
You want maximum flexibility in build configuration and file placement
You have existing build scripts that you want to preserve and integrate
You need to build components that exist outside the Python package structure
You require custom build steps that go beyond standard CMake workflows
The Build Configuration System
ACCV-Lab uses a centralized build configuration provided by the accvlab_build_config package to ensure
consistency across packages and simplify adding new ones.
Installation
The build_config package is a build dependency of the contained namespace packages. It is installed as part
of the ACCV-Lab installation using the unified installer script. However, it can also be built and installed
manually as follows:
cd build_config
# Inside the build_config directory, call:
pip install . --no-build-isolation
Purpose and Benefits
The build_config/ package serves several key purposes:
Shared Build Utilities: Provides common functions for C++/CUDA extension building, dependency management, and configuration handling
Consistency: Ensures all namespace packages use the same build logic, compiler flags, and configuration patterns
Maintainability: Centralizes build logic so bug fixes and improvements benefit all packages
Simplicity: Reduces amount of boilerplate code needed in the
setup.pyfiles of individual packages
Usage in Namespace Packages
Each namespace package’s setup.py imports and uses these shared utilities, for example:
from torch.utils.cpp_extension import BuildExtension, CppExtension, CUDAExtension
from accvlab_build_config import (
load_config,
detect_cuda_info,
get_compile_flags,
)
# Load config and generate flags
config = load_config()
cuda_info = detect_cuda_info()
compile_flags = get_compile_flags(config, cuda_info)
# Example: wire flags into extensions
ext_modules = [
CppExtension(
name="accvlab.<pkg>._cpp",
sources=["accvlab/<pkg>/csrc/cpp_functions.cpp"],
extra_compile_args=compile_flags['cxx'],
language="c++",
verbose=config["VERBOSE_BUILD"],
),
CUDAExtension(
name="accvlab.<pkg>._cuda",
sources=[
"accvlab/<pkg>/csrc/cuda_functions.cpp",
"accvlab/<pkg>/csrc/cuda_functions.cu",
],
extra_compile_args={"cxx": compile_flags["cxx"], "nvcc": compile_flags["nvcc"]},
language="c++",
verbose=config["VERBOSE_BUILD"],
),
]
This approach ensures that:
All packages use the same build logic
Configuration is consistent across packages
Build improvements benefit all packages automatically
Individual package setup.py files remain simple and focused
Please see the setup.py files of the example packages (e.g. packages/example_package/setup.py and
packages/example_skbuild_package/setup.py) for complete working examples for different package types.
How Build Variables Are Picked Up
Depending on the package type, build variables are consumed as follows:
Setuptools (PyTorch extensions):
In
setup.py, callconfig = load_config()andcuda_info = detect_cuda_info(), then passcompile_flags = get_compile_flags(config, cuda_info)toCppExtension/CUDAExtension:
config = load_config() cuda_info = detect_cuda_info() compile_flags = get_compile_flags(config, cuda_info) ext = CUDAExtension( name='accvlab.<pkg>._ext', sources=[...], extra_compile_args={'cxx': compile_flags['cxx'], 'nvcc': compile_flags['nvcc']}, language='c++', verbose=config['VERBOSE_BUILD'], )
This forwards
DEBUG_BUILD,OPTIMIZE_LEVEL,CPP_STANDARD,VERBOSE_BUILD,CUSTOM_CUDA_ARCHS,USE_FAST_MATH, andENABLE_PROFILINGto host and device compilers.
External implementation (manual CMake):
In
ext_impl/build_and_copy.sh, forward variables using the helper to producecmake -Dargs:
readarray -t CMAKE_ARGS < <(python -c "from accvlab_build_config.helpers.cmake_args import build_cmake_args_from_env; print('\n'.join(build_cmake_args_from_env()))") cmake .. "${CMAKE_ARGS[@]}" ... cmake --build . --parallel
In
CMakeLists.txt, avoid hardcoding and guard defaults so passed values win:
if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 17) endif() if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES) set(CMAKE_CUDA_ARCHITECTURES native) endif()
Scikit-build packages:
In
setup.py, pass CMake arguments from the helper:
from accvlab_build_config.helpers.cmake_args import build_cmake_args_from_env _cmake_args = build_cmake_args_from_env() setup( ..., cmake_source_dir="ext_impl", cmake_install_dir="accvlab/<pkg>", cmake_args=_cmake_args, )
In
ext_impl/CMakeLists.txt, guard defaults:
if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 17) endif() if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES) set(CMAKE_CUDA_ARCHITECTURES native) endif()
Configuration Options
For the list of supported environment variables, their defaults, and descriptions, see the Available Build Variables in the installation guide. These variables can be set per-package during installation or globally for all packages.
Development Workflow
Testing During Development
Running Tests
Tests of a package can be performed with:
cd packages/<package_name>/tests
pytest .
Important
Note that the tests do not necessarily need to be run from within the tests directory.
However, it is advised to not run tests from the root directory of a namespace package (i.e.
not from packages/<package_name>, as in this way, importing the package is ambiguous.
For example, the import import accvlab.<package_name> could mean either refer to the installed package,
or to the original source files inside the current directory).
Note
See the section Repository test runner for how to run tests across all packages in the repository.
Install in Development Mode
This can be done with
pip install -e . --no-build-isolation
Note that
The editable mode refers to the Python code. For any changes in C++ code to take effect, the package needs to be re-installed regardless whether it is installed in editable mode or not.
SKBuild does not support editable installs, and instead, the SKBuild-based packages need to be always be installed without the
-eflag.
Documentation Development
Important
Ensure all configured namespace packages are installed before building the docs. For detailed instructions (commands and development targets), see the Documentation Setup Guide.
Code Formatting
The ACCV-Lab project uses automated code formatting to maintain consistent code style across all namespace packages. Please see the Formatting Guide for details.
Script Features
As all scripts, the formatting script automatically discovers configured namespace packages from
namespace_packages_config.py.
Formatting for the whole repo can be run as:
bash scripts/format.sh
It is also possible to run the formatting for individual namespace packages. To list available packages and format one of them:
# List available namespace packages
python3 -c "from namespace_packages_config import get_package_names; print('\n'.join(get_package_names()))"
# Format a specific package (example)
./scripts/format.sh --package on_demand_video_decoder
You can also format only a specific language:
# Format Python code only (common + all packages)
./scripts/format.sh --python
# Format C++/CUDA code only (all packages)
./scripts/format.sh --cpp
# Combine with a package filter
./scripts/format.sh --python --package example_package
./scripts/format.sh --cpp --package batching_helpers
Typical Workflows
During Development
# Format the namespace package you're working on
./scripts/format.sh --package example_package
Before Committing
# Format everything to ensure consistency
./scripts/format.sh
Repository Test Runner (scripts/run_tests.sh)
The repository provides a convenience script to run pytest for all configured namespace packages:
./scripts/run_tests.sh # run all tests
./scripts/run_tests.sh -- -k smoke # pass arguments after -- directly to pytest (`-k smoke` used as an example)
How it discovers and runs tests:
Discovers package names from
namespace_packages_config.py(functionget_package_names()).For each package
<name>, looks forpackages/<name>/tests. If missing, it warns and skips.Executes
pytestfrom inside eachpackages/<name>/testsdirectory to avoid importing local sources.Exits non-zero if any package test run fails.
To ensure your tests are picked up:
Place tests under
packages/<package_name>/tests.Name files following pytest conventions (e.g.,
test_*.pyor*_test.py).If you need custom pytest flags, pass them after
--, e.g../scripts/run_tests.sh -- -q -k "gpu and not slow".
Additional notes:
Inside your test scripts, import the installed package (e.g.,
import accvlab.<package_name>), not local source paths.Ensure the package is installed in the current environment before running the script (editable or standard install). This can be done with
pip install -e .[optional] --no-build-isolationorpip install .[optional] --no-build-isolation(see the Installation Guide for more details). Note that for SKBuild-based packages, the editable install is not supported and will result in missing binaries & import errors.
Important
The tests often rely on optional dependencies. Therefore, it is recommended to install the package with optional dependencies, as described in the Installation Guide.
Namespace Package Structure and Configuration
Understanding the Package Structure
ACCV-Lab uses implicit namespace packages where each package directory maps to a namespace under accvlab.
For example:
packages/example_package/accvlab/example_package/→accvlab.example_packagepackages/batching_helpers/accvlab/batching_helpers/→accvlab.batching_helpers
Note that e.g. the directory packages/example_package/ is the root directory of the package, containing
the pyproject.toml & setup.py files, external C++ implementations or CMake projects as used by SKBuild,
documentation, etc. The remainder of the path (i.e. [...]/accvlab/example_package/) reflects the package
name as installed (accvlab.example_package in this case) and contains the actual Python package
implementation as will be included in the installation.
Package Discovery Configuration
The pyproject.toml configuration is crucial for proper package discovery:
[tool.setuptools.packages.find]
where = ["."]
include = ["accvlab.example_package*"]
This configuration:
where = ["."]- Searches in the current directoryinclude = ["accvlab.example_package*"]- Includes all packages starting withaccvlab.example_package
Handling Subpackages
If your namespace package contains subpackages (like accvlab.example_package.functions), the
include = ["accvlab.example_package*"] pattern will automatically discover and include all subpackages.
Notes
Namespace package structure: The directory structure must match the namespace structure:
packages/example_package/ ├── accvlab/ # ← Namespace root │ └── example_package/ # Maps to accvlab.example_package │ ├── __init__.py │ ├── functions/ # Maps to accvlab.example_package.functions │ │ └── __init__.py │ └── ... ├── setup.py ├── pyproject.toml └── ...
As usual for modules,
__init__.pyfiles need to be present and otherwise, e.g. the automatic package discovery may fail.Build artifacts: Always clean build artifacts (
build/,dist/,*.egg-info/) when testing package installation to ensure that the installation can be performed from a clean state.