Skip to main content
Learn how to build Python packages with Pixi, leveraging the conda ecosystem’s cross-language capabilities.
pixi-build is a preview feature and will change until stabilized. Keep this in mind for production projects.

Why Build Python Packages with Pixi?

Building Python packages with Pixi offers unique advantages:
  1. Cross-language support - Manage Python packages alongside Rust, C++, R, and other languages
  2. Unified tooling - Build both conda and Python packages with the same tool
  3. Conda ecosystem - Access packages from conda-forge, not just PyPI
  4. Workspace support - Develop multiple interdependent packages together

Creating Your First Python Package

1
Set Up the Project Structure
2
Create a Python package with standard layout:
3
mkdir -p python_rich/src/python_rich
cd python_rich
4
python_rich/
├── src/
│   └── python_rich/
│       └── __init__.py
└── pyproject.toml
5
This project uses a src-layout, but Pixi supports both flat- and src-layouts.
6
Create the Python Code
7
Add functionality to your package:
8
from dataclasses import dataclass, fields
from rich.console import Console
from rich.table import Table


@dataclass
class Person:
    name: str
    age: int
    city: str


def main() -> None:
    console = Console()

    people = [
        Person("John Doe", 30, "New York"),
        Person("Jane Smith", 25, "Los Angeles"),
        Person("Tim de Jager", 35, "Utrecht"),
    ]

    table = Table()

    for column in fields(Person):
        table.add_column(column.name)

    for person in people:
        table.add_row(person.name, str(person.age), person.city)

    console.print(table)
9
Configure Python Metadata
10
Create the pyproject.toml for Python-specific configuration:
11
[project]
name = "python_rich"
version = "0.1.0"
requires-python = ">= 3.11"
dependencies = ["rich"]  # (1)!

[project.scripts]
rich-example-main = "python_rich:main"  # (2)!

[build-system]  # (3)!
build-backend = "hatchling.build"
requires = ["hatchling"]
12
  • Dependencies for PyPI distribution
  • Creates an executable that calls the main function
  • Choose a PEP 517 build backend - hatchling works well with minimal config
  • 13
    Initialize Pixi
    14
    Create the Pixi manifest:
    15
    pixi init --format pixi
    
    16
    This generates a pixi.toml file. You can also integrate everything into pyproject.toml by prepending tool.pixi. to each table.
    17
    Configure the Pixi Manifest
    18
    Edit pixi.toml to configure building:
    19
    [workspace]  # (1)!
    channels = ["https://prefix.dev/conda-forge"]
    platforms = ["linux-64", "osx-64", "osx-arm64", "win-64"]
    preview = ["pixi-build"]
    
    [dependencies]  # (2)!
    python_rich = { path = "." }
    
    [tasks]  # (3)!
    start = "rich-example-main"
    
    [package]  # (4)!
    name = "python_rich"
    version = "0.1.0"
    
    [package.build]  # (5)!
    backend = { name = "pixi-build-python", version = "0.4.*" }
    
    [package.host-dependencies]  # (6)!
    hatchling = "==1.26.3"
    
    [package.run-dependencies]  # (7)!
    rich = "13.9.*"
    
    20
  • Workspace-level configuration shared across all packages
  • Add your package as a dependency to include it in environments
  • Define tasks using the executable from pyproject.toml
  • Package metadata used when others depend on it or for uploads
  • The pixi-build-python backend converts Python packages to conda packages
  • Build-time Python dependencies from conda (not PyPI)
  • Runtime dependencies from conda channels
  • 21
    Build and Run
    22
    Build and test your package:
    23
    pixi run start
    
    You should see:
    ┏━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━┓
    ┃ name         ┃ age ┃ city        ┃
    ┡━━━━━━━━━━━━━━╇━━━━━╇━━━━━━━━━━━━━┩
    │ John Doe     │ 30  │ New York    │
    │ Jane Smith   │ 25  │ Los Angeles │
    │ Tim de Jager │ 35  │ Utrecht     │
    └──────────────┴─────┴─────────────┘
    

    Advanced Configuration

    Using Custom Backend Channels

    For the latest backend features:
    [package.build.backend]
    channels = [
      "https://prefix.dev/pixi-build-backends",
      "https://prefix.dev/conda-forge",
    ]
    name = "pixi-build-python"
    version = "0.4.*"
    

    Noarch Packages

    For pure Python packages without compiled extensions:
    [package.build.config]
    noarch = true
    
    This creates a single package that works across all platforms.

    Custom Build Configuration

    [package.build.config]
    editable = true
    

    Real-World Example

    Here’s a complete example from the array-api-extra package:
    pixi.toml
    [workspace]
    channels = ["https://prefix.dev/conda-forge"]
    name = "array-api-extra-build"
    platforms = ["osx-arm64", "linux-64", "osx-64", "win-64"]
    preview = ["pixi-build"]
    version = "0.1.0"
    
    [dependencies]
    array-api-extra = { path = "." }
    
    [tasks]
    test = "python -c 'import array_api_extra'"
    
    [package]
    name = "array-api-extra"
    version = "0.8.0"
    
    [package.build.backend]
    channels = [
      "https://prefix.dev/pixi-build-backends",
      "https://prefix.dev/conda-forge",
    ]
    name = "pixi-build-python"
    version = "0.4.*"
    

    Understanding Host vs Run Dependencies

    Python packages need to declare dependencies in two places:In pyproject.toml (for PyPI):
    [project]
    dependencies = ["rich"]
    
    In pixi.toml (for conda):
    [package.run-dependencies]
    rich = "13.9.*"
    
    This allows the same package to work in both ecosystems.
    Host dependencies are needed during the build process. For Python:
    [package.host-dependencies]
    hatchling = "*"  # Build backend
    uv = "*"          # Optional: faster installer
    python = "3.11.*" # Python version for building
    
    These come from conda channels, ensuring consistent build environments.See dependency types for details.

    Integration with pyproject.toml

    You can integrate Pixi configuration into pyproject.toml:
    pyproject.toml
    [project]
    name = "python_rich"
    version = "0.1.0"
    requires-python = ">= 3.11"
    dependencies = ["rich"]
    
    [build-system]
    build-backend = "hatchling.build"
    requires = ["hatchling"]
    
    [tool.pixi.workspace]
    channels = ["https://prefix.dev/conda-forge"]
    platforms = ["linux-64", "osx-64", "osx-arm64", "win-64"]
    preview = ["pixi-build"]
    
    [tool.pixi.dependencies]
    python_rich = { path = "." }
    
    [tool.pixi.package]
    name = "python_rich"
    version = "0.1.0"
    
    [tool.pixi.package.build]
    backend = { name = "pixi-build-python", version = "0.4.*" }
    

    Next Steps

    Workspaces

    Combine multiple packages in one workspace

    Dependency Types

    Understand build, host, and run dependencies

    C++ Integration

    Mix Python with C++ packages

    Build Variants

    Build against multiple Python versions

    Common Issues

    Technical Limitation: Dependencies like hatchling, pip, and uv must be in host-dependencies (not build-dependencies) due to how the build process currently works. This ensures the correct Python prefix is used.
    Ensure your package is added to workspace dependencies:
    [dependencies]
    your_package = { path = "." }
    
    Add missing dependencies to run-dependencies:
    [package.run-dependencies]
    missing_package = "*"