Skip to content

pixi-build-python#

The pixi-build-python backend is designed for building Python projects using standard Python packaging tools. It provides seamless integration with Pixi's package management workflow while supporting both PEP 517 and PEP 518 compliant projects.

Warning

pixi-build is a preview feature, and will change until it is stabilized. This is why we require users to opt in to that feature by adding "pixi-build" to workspace.preview.

[workspace]
preview = ["pixi-build"]

Overview#

This backend automatically generates conda packages from Python projects by:

  • PEP 517/518 compliance: Works with modern Python packaging standards including pyproject.toml
  • PyPI-to-conda mapping (opt-in): Maps project.dependencies and build-system.requires from pyproject.toml to conda packages (see ignore-pypi-mapping)
  • Automatic compiler detection: Detects build tools like maturin or setuptools-rust and automatically adds required compilers
  • Cross-platform support: Works consistently across Linux, macOS, and Windows
  • Flexible installation: Uses uv by default; pip can be selected with the installer option

Basic Usage#

To use the Python backend in your pixi.toml, add it to your package's build configuration:

[package]
name = "python_package"
version = "0.1.0"

[package.build]
backend = { name = "pixi-build-python", version = "*" }
channels = ["https://prefix.dev/conda-forge"]

Required Dependencies#

The backend automatically includes the following build tools:

  • python - The Python interpreter
  • uv - Python package installer used by default (or pip when installer = "pip" is configured)

You can add these to your host-dependencies if you need specific versions:

[package.build-dependencies]
python = "3.11"

The backend will be automatically selected by the automatic PyPI dependency mapping feature if you have pyproject.toml in your source directory. Otherwise, you need to explicitly add it to your package definition in the [host-dependencies]:

[package.host-dependencies]
hatchling = "*"

Configuration Options#

You can customize the Python backend behavior using the [package.build.config] section in your pixi.toml. The backend supports the following configuration options:

noarch#

  • Type: Boolean
  • Default: true (unless compilers are specified)
  • Target Merge Behavior: Overwrite - Platform-specific noarch setting takes precedence over base

Controls whether to build a platform-independent (noarch) package or a platform-specific package. The backend tries to derive whether the package can be built as noarch based on the presence of compilers. If compilers are specified, the backend assume that native extensions are build as part of the build process. Most of the time these are platform-specific, so the package will be built as a platform-specific package. If no compilers are specified, the default value for noarch is true, meaning the package will be built as a noarch python package.

[package.build.config]
noarch = false  # Build platform-specific package

For target-specific configuration, platform-specific noarch setting overrides the base:

[package.build.config]
noarch = true

[package.build.target.win-64.config]
noarch = false  # Windows needs platform build
# Result for win-64: false

env#

  • Type: Map<String, String>
  • Default: {}
  • Target Merge Behavior: Merge - Platform environment variables override base variables with same name, others are merged

Environment variables to set during the build process. These variables are available during package installation.

[package.build.config]
env = { SETUPTOOLS_SCM_PRETEND_VERSION = "1.0.0" }

For target-specific configuration, platform environment variables are merged with base variables:

[package.build.config]
env = { PYTHONPATH = "/base/path", COMMON_VAR = "base" }

[package.build.target.win-64.config]
env = { COMMON_VAR = "windows", WIN_SPECIFIC = "value" }
# Result for win-64: { PYTHONPATH = "/base/path", COMMON_VAR = "windows", WIN_SPECIFIC = "value" }

debug-dir#

The backend always writes JSON-RPC request/response logs and the generated intermediate recipe to the debug subdirectory inside the work directory (for example <work_directory>/debug). The deprecated debug-dir configuration option is ignored; if present, a warning is emitted to highlight that the setting no longer has any effect.

extra-input-globs#

  • Type: Array<String>
  • Default: []
  • Target Merge Behavior: Overwrite - Platform-specific globs completely replace base globs

Additional glob patterns to include as input files for the build process. These patterns are added to the default input globs that include Python source files, configuration files (setup.py, pyproject.toml, etc.), and other build-related files.

[package.build.config]
extra-input-globs = [
    "data/**/*",
    "templates/*.html",
    "*.md"
]

For target-specific configuration, platform-specific globs completely replace the base:

[package.build.config]
extra-input-globs = ["*.py"]

[package.build.target.win-64.config]
extra-input-globs = ["*.py", "*.dll", "*.pyd", "windows-resources/**/*"]
# Result for win-64: ["*.py", "*.dll", "*.pyd", "windows-resources/**/*"]

compilers#

  • Type: Array<String>
  • Default: [] (no compilers)
  • Target Merge Behavior: Overwrite - Platform-specific compilers completely replace base compilers

List of compilers to use for the build. Most pure Python packages don't need compilers, but this is useful for packages with C extensions or other compiled components. The backend automatically generates appropriate compiler dependencies using conda-forge's compiler infrastructure.

[package.build.config]
compilers = ["c", "cxx"]

For target-specific configuration, platform compilers completely replace the base configuration:

[package.build.config]
compilers = []

[package.build.target.win-64.config]
compilers = ["c", "cxx"]
# Result for win-64: ["c", "cxx"] (only on Windows)

Pure Python vs. Extension Packages

The Python backend defaults to no compilers ([]) since most Python packages are pure Python and don't need compilation. This is different from other backends like CMake which default to ["cxx"]. Only specify compilers if your package has C extensions or other compiled components:

# Pure Python package (default behavior)
[package.build.config]
# No compilers needed - defaults to []

# Python package with C extensions
[package.build.config]
compilers = ["c", "cxx"]

Automatic Compiler Detection

The backend automatically detects compilers required by certain build tools in your build-system.requires. For example:

  • maturin → "rust"
  • setuptools-rust → "rust"

These detected compilers are merged with any explicitly configured compilers. You only need to manually specify compilers if your package uses build tools that aren't auto-detected.

Comprehensive Compiler Documentation

For detailed information about available compilers, platform-specific behavior, and how conda-forge compilers work, see the Compilers Documentation.

abi3#

  • Type: Boolean
  • Default: false
  • Target Merge Behavior: Overwrite - Platform-specific setting takes precedence over base

Controls whether the package uses the Python Stable ABI (abi3). When set to true, pixi:

  • marks the recipe as build.python.version_independent: true
  • adds python-abi3 to the host requirements
  • suppresses the normal CPython ABI run exports from host: python

This follows CEP 20, which defines conda ecosystem support for abi3 Python packages.

The python-abi3 version is derived from the lower bound of requires-python:

  • requires-python = ">=3.9"python-abi3 3.9.*
  • requires-python = ">=3.11,<4"python-abi3 3.11.*
  • If requires-python is not specified, defaults to python-abi3 3.9.*, the oldest available python-abi3 package on conda-forge

If python-abi3 is already declared in your host requirements, pixi does not add a duplicate entry.

[package.build.config]
abi3 = true
compilers = ["c"]

Incompatible with noarch

Setting abi3 = true with noarch = true will produce an error, since the stable ABI is only meaningful for packages with compiled extensions.

installer#

  • Type: String ("uv" or "pip")
  • Default: "uv"
  • Target Merge Behavior: Overwrite - Platform-specific setting takes precedence over base

Selects the tool used to install the built wheel into the prefix. The selected installer is automatically added to the host dependencies.

[package.build.config]
installer = "pip"

Behavior change

Older versions of the backend selected pip when it was present in the build or host dependencies. Adding pip to the dependencies no longer has this effect; set installer = "pip" in the backend configuration instead.

extra-args#

  • Type: Array<String>
  • Default: []
  • Target Merge Behavior: Overwrite - Platform-specific globs completely replace base globs

Extra arguments to pass to the selected installer (uv by default, or pip if selected). A use-case could be pip's --config-settings parameter.

[package.build.config]
extra-args = ["-Cbuilddir=mybuilddir"]

For target-specific configuration, platform-specific globs completely replace the base:

[package.build.config]
extra-args = ["-Cbuilddir=mybuilddir"]

[package.build.target.win-64.config]
extra-args = ["-Cbuilddir=foo"]
# Result for win-64: ["-Cbuilddir=foo"]

skip-pyc-compilation#

  • Type: Boolean | Array<String>
  • Default: not set (no files are skipped)
  • Target Merge Behavior: Overwrite - Platform-specific setting takes precedence over base

Controls whether .py files are compiled to .pyc bytecode files during package installation. This can be useful for reducing package size or when bytecode caching is not needed.

Accepts either true to skip all .pyc compilation, or a list of glob patterns for selective skipping:

# Skip all .pyc compilation
[package.build.config]
skip-pyc-compilation = true
# Skip .pyc compilation only for specific paths
[package.build.config]
skip-pyc-compilation = ["tests/**", "benchmarks/**"]

For target-specific configuration, the platform-specific value completely replaces the base:

[package.build.config]
skip-pyc-compilation = true

[package.build.target.win-64.config]
skip-pyc-compilation = ["tests/**"]
# Result for win-64: ["tests/**"]

ignore-pyproject-manifest#

  • Type: Boolean
  • Default: false
  • Target Merge Behavior: Overwrite - Platform-specific setting takes precedence over base

Controls whether to ignore the pyproject.toml manifest file and rely solely on the project model for package metadata. When set to true, the backend will not extract metadata (name, version, description, license, URLs) from pyproject.toml and will use only the information provided in the Pixi project model.

[package.build.config]
ignore-pyproject-manifest = true  # Ignore pyproject.toml metadata

This option is useful when you want complete control over package metadata through the Pixi project configuration, or when the pyproject.toml contains metadata that conflicts with your conda package requirements.

For target-specific configuration, platform-specific setting overrides the base:

[package.build.config]
ignore-pyproject-manifest = false

[package.build.target.win-64.config]
ignore-pyproject-manifest = true  # Ignore pyproject.toml on Windows only
# Result for win-64: true

Metadata Extraction from pyproject.toml

By default (when ignore-pyproject-manifest is false), the backend automatically extracts package metadata from your pyproject.toml file, including:

  • name: Package name from project.name
  • version: Package version from project.version
  • description/summary: From project.description
  • license: From project.license (supports text, file, or SPDX formats)
  • homepage: From project.urls.Homepage
  • repository: From project.urls.Repository, project.urls.Source, or project.urls."Source Code"
  • documentation: From project.urls.Documentation or project.urls.Docs

This metadata is automatically included in the generated conda recipe. The pyproject.toml file itself is also added to the input globs for incremental build detection.

ignore-pypi-mapping#

  • Type: Boolean
  • Default: true
  • Target Merge Behavior: Overwrite - Platform-specific setting takes precedence over base

Controls whether to ignore the automatic PyPI-to-conda dependency mapping feature. When set to true (the default), dependencies from pyproject.toml will not be automatically mapped to conda packages. Set to false to enable automatic mapping.

[package.build.config]
ignore-pypi-mapping = false  # Enable automatic PyPI-to-conda mapping

Default Behavior

This option currently defaults to true (mapping disabled) to avoid breaking existing setups. In a future release, the default will change to false (mapping enabled). If you want to opt-in to automatic dependency mapping now, explicitly set ignore-pypi-mapping = false.

For target-specific configuration, platform-specific setting overrides the base:

[package.build.config]
ignore-pypi-mapping = false

[package.build.target.win-64.config]
ignore-pypi-mapping = true  # Disable mapping on Windows only
# Result for win-64: true

pypi-conda-map#

  • Type: Map<String, String | false>
  • Default: not set
  • Target Merge Behavior: Merge - Platform-specific entries override or extend base entries per key

User-defined overrides for the PyPI-to-conda name mapping, keyed by PyPI package name. A string value maps the package to that conda name; false omits the dependency from the generated recipe. Entries are consulted before the mapping service, so they never require network access. Packages not in the map fall back to the service as usual.

This option is only used when ignore-pypi-mapping = false; otherwise it has no effect and a warning is logged. The overrides apply to both mapping passes: project.dependencies (run dependencies) and build-system.requires (host dependencies).

Different shape than conda-pypi-map

pypi-conda-map is not a schema-level mirror of workspace conda-pypi-map. It is a flat PyPI-name to conda-name override map because the build backend converts each Python requirement into at most one conda recipe dependency. The workspace conda-pypi-map is per channel and can map one conda package to several PyPI names, because it is used to decide which installed conda packages satisfy PyPI requirements.

[package.build.config]
ignore-pypi-mapping = false
pypi-conda-map = { torch = "pytorch", my-internal-pkg = false }

Per-platform entries merge with the base map key-by-key:

[package.build.config]
pypi-conda-map = { torch = "pytorch" }

[package.build.target.linux-64.config]
pypi-conda-map = { nvidia-cublas-cu12 = false }
# Result for linux-64: { torch = "pytorch", nvidia-cublas-cu12 = false }

Automatic PyPI Dependency Mapping#

The Python backend can automatically map PyPI dependencies from your pyproject.toml to their corresponding conda packages. This means you don't need to manually duplicate your dependencies in both pyproject.toml and pixi.toml.

Opt-in Feature

This feature is currently disabled by default. To enable automatic PyPI-to-conda dependency mapping, set ignore-pypi-mapping = false in your build configuration:

[package.build.config]
ignore-pypi-mapping = false

How It Works#

The backend reads dependencies from two sources in your pyproject.toml:

  1. project.dependencies → Added to conda run dependencies
  2. build-system.requires → Added to conda host dependencies

For each PyPI package, the backend first consults the user-defined pypi-conda-map overrides, and then queries a mapping service to find the corresponding conda-forge package name. The mapping is cached locally for 24 hours to improve performance.

Example#

Given this pyproject.toml:

[project]
name = "my-package"
version = "1.0.0"
dependencies = [
    "requests>=2.28",
    "pydantic>=2.0,<3.0",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

The backend automatically adds:

  • requests >=2.28 and pydantic >=2.0,<3.0 to run dependencies
  • hatchling to host dependencies

Combining With Manifest Dependencies#

Dependencies specified in your pixi.toml are combined with those inferred from pyproject.toml:

  • If you specify requests = ">=2.30" in [package.run-dependencies], both that spec and the mapped requests>=2.28 from pyproject.toml end up in the recipe and intersect in the solver
  • Dependencies not in pixi.toml are added from pyproject.toml

To diverge from the bounds in pyproject.toml entirely, disable the mapping with ignore-pypi-mapping and declare the dependencies in pixi.toml.

Behavior change

Older versions of the backend skipped the mapped pyproject.toml spec when the same package was declared in pixi.toml. Both specs now land in the recipe and intersect; conflicting bounds surface as solver errors instead of being silently overridden.

Limitations#

  • Environment markers (e.g., requests>=2.28; python_version >= "3.8") are only partially supported. At the moment, only platform_system, os_name, platform_machine and sys_platforms are currently checked.
  • URL-based dependencies (e.g., package @ https://...) are skipped
  • Packages without a conda-forge mapping are logged as warnings and skipped; use pypi-conda-map to map them explicitly or omit them with false

Build Process#

The Python backend follows this build process:

  1. Installer Selection: Uses uv by default, or pip when installer = "pip" is configured
  2. Environment Setup: Configures Python environment variables for the build
  3. Package Installation: Executes the selected installer with the following options:
    • --no-deps: Don't install dependencies (handled by conda)
    • --no-build-isolation: Use the conda environment for building
    • -vv: Verbose output for debugging
  4. Package Creation: Creates either a noarch or platform-specific conda package

Installer Selection#

The installer is chosen with the installer configuration option and is automatically added to the host dependencies:

[package.build.config]
installer = "pip"

Editable Installations#

Until profiles are implemented, editable installations are not easily configurable. This is the current behaviour:

  • editable is true when installing the package (e.g. with pixi install)
  • editable is false when building the package (e.g. with pixi publish)
  • Set environment variable BUILD_EDITABLE_PYTHON to true or false to enforce a certain behavior

Default Variants#

On Windows platforms, the backend automatically sets the following default variants:

  • c_compiler: vs2022 - Visual Studio 2022 C compiler
  • cxx_compiler: vs2022 - Visual Studio 2022 C++ compiler

These variants are used when you specify compilers in your [package.build.config.compilers] configuration. Note that setting these default variants does not automatically add compilers to your build - you still need to explicitly configure which compilers to use.

This default is set to align with conda-forge's switch to Visual Studio 2022 and because mainstream support for Visual Studio 2019 ended in 2024. The vs2022 compiler is more widely supported on modern GitHub runners and build environments.

You can override these defaults by explicitly setting variants using [workspace.build-variants] in your pixi.toml:

[workspace.build-variants]
c_compiler = ["vs2019"]
cxx_compiler = ["vs2019"]

Limitations#

  • Requires a PEP 517/518 compliant Python project with pyproject.toml
  • Limited support for complex build customization compared to direct recipe-based approaches
  • Limited ways to configure editable installations

See Also#