🧩 Build System Integration¶
Tip
See example/ for working examples of using unidep with different build systems.
unidep seamlessly integrates with popular Python build systems to simplify dependency management in your projects.
Local Dependencies in Monorepos¶
Local dependencies are essential for monorepos and multi-package projects, allowing you to:
Share code between packages during development
Maintain separate releases for each package
Test changes across multiple packages simultaneously
Raw local paths would create non-portable packages that only work on the
original system, so UniDep treats them as development-only unless you provide a
publishable pypi: fallback.
PyPI Alternatives for Local Dependencies¶
UniDep solves this problem by letting you specify both local paths (for development) and PyPI packages (for distribution):
# requirements.yaml
dependencies:
- numpy
- pandas
local_dependencies:
# Standard string format for local dependencies
- ../shared-lib
# Dictionary format with optional PyPI alternative for build-time
- local: ../auth-lib
pypi: company-auth-lib>=1.0
- local: ../utils
pypi: company-utils~=2.0
use: pypi # see [Overriding Nested Vendor Copies](build-system-integration.md#overriding-nested-vendor-copies-with-use)
Or in pyproject.toml:
[tool.unidep]
dependencies = ["numpy", "pandas"]
local_dependencies = [
# Standard string format for local dependencies
"../shared-lib",
# Dictionary format with optional PyPI alternative for build-time
{local = "../auth-lib", pypi = "company-auth-lib>=1.0"},
{local = "../utils", pypi = "company-utils~=2.0", use = "pypi"},
]
How it works:
During development (e.g.,
unidep installorpip install -e .): Uses local paths when they existWhen generating build metadata (
setuptoolsorhatchling): Local paths are never published asfile://requirementsIf
pypi:is specified: The PyPI requirement is published insteadIf no
pypi:is specified: The local entry is omitted from build metadataThe standard string format continues to work as always for local dependencies
Tip
PyPI alternatives ensure your wheels are portable and can be installed anywhere, not just on the build system. Use the use field (see Overriding Nested Vendor Copies) to control whether UniDep installs the local path, forces PyPI, or skips the entry entirely.
Overriding Nested Vendor Copies with use¶
The Problem: When vendoring dependencies as git submodules, you often encounter conflicts where a submodule bundles its own copy of a dependency you also use, but at a different version.
The Solution: Use use: pypi to force your PyPI package instead of the vendored copy, with automatic propagation to all nested references.
Example: Override foo’s bundled bar with your PyPI build¶
Your project vendors foo as a submodule. Foo bundles bar@1.0, but you need bar@2.0:
project/
third_party/
foo/ # git submodule you don't control
third_party/
bar/ # foo bundles bar@1.0
Solution with use: pypi:
local_dependencies:
- ./third_party/foo # Keep foo editable for development
# Override: force YOUR PyPI build of bar
- local: ./third_party/foo/third_party/bar
pypi: my-bar>=2.0
use: pypi # Install from PyPI, skip local path
What happens:
foostays local (editable for development)my-bar>=2.0gets installed from PyPI (not foo’s bundled v1.0)Propagates: Every nested reference to
baruses your PyPI packageWorks with
unidep install,unidep conda-lock, all CLI commands
This is the key difference from just using pypi: as a build-time fallback - use: pypi forces the PyPI package during development while keeping other local dependencies editable.
All use values¶
Tell UniDep what to use for each entry in local_dependencies:
|
When to use |
Installs from |
Propagates override? |
|---|---|---|---|
|
Normal local development |
Local path |
- |
|
Force PyPI even when local exists |
|
Yes |
|
Ignore this path entirely |
Nothing |
Yes |
Common patterns:
local_dependencies:
# Standard local development (default)
- ../shared-lib
# Force PyPI to override nested vendor copy
- local: ./vendor/foo/nested/bar
pypi: my-bar>=2.0
use: pypi
# Skip a path without installing anything
- local: ./deprecated-module
use: skip
Note
Precedence: The use flag on the entry itself always wins. When UniDep encounters the same path in nested local_dependencies, it uses your override.
Caution
If use: pypi is set but no pypi: requirement is provided, UniDep exits with a clear error so you can supply the missing spec.
Build System Behavior¶
When UniDep generates build metadata for Setuptools or Hatchling, it always emits portable requirements:
Local path entries are never written as
file://dependenciesIf a local dependency has a
pypi:fallback, that requirement is publishedIf a local dependency has no
pypi:fallback, it is omitted from build metadataOptional sections that become empty after dropping local-only entries are still preserved as empty extras
The same publish-or-omit rule applies whether the local path is declared in
local_dependencies or nested inside optional_dependencies.
This keeps development workflows local-first while ensuring published metadata does not depend on the build machine’s filesystem.
Example packages¶
Explore these installable example packages to understand how unidep integrates with different build tools and configurations:
Project |
Build Tool |
|
|
|
|---|---|---|---|---|
|
✅ |
✅ |
✅ |
|
|
✅ |
✅ |
❌ |
|
|
✅ |
❌ |
❌ |
|
|
✅ |
✅ |
❌ |
|
|
✅ |
❌ |
❌ |
Setuptools Integration¶
For projects using setuptools, configure unidep in pyproject.toml and either specify dependencies in a requirements.yaml file or include them in pyproject.toml too.
Using
pyproject.tomlonly: The[project.dependencies]field inpyproject.tomlgets automatically populated fromrequirements.yamlor from the[tool.unidep]section inpyproject.toml.Using
setup.py: Theinstall_requiresfield insetup.pyautomatically reflects dependencies specified inrequirements.yamlorpyproject.toml.
Example pyproject.toml Configuration:
[build-system]
build-backend = "setuptools.build_meta"
requires = ["setuptools", "unidep"]
[project]
dynamic = ["dependencies"]
Hatchling Integration¶
For projects managed with Hatch, unidep can be configured in pyproject.toml to automatically process the dependencies from requirements.yaml or from the [tool.unidep] section in pyproject.toml.
Example Configuration for Hatch:
[build-system]
requires = ["hatchling", "unidep"]
build-backend = "hatchling.build"
[project]
dynamic = ["dependencies"]
# Additional project configurations
[tool.hatch.metadata.hooks.unidep]
# Enable the unidep plugin
[tool.hatch.metadata]
allow-direct-references = true
[tool.unidep]
# Your dependencies configuration