Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build Environment for PyPI Source Dependencies #1340

Open
tdejager opened this issue May 7, 2024 · 1 comment
Open

Build Environment for PyPI Source Dependencies #1340

tdejager opened this issue May 7, 2024 · 1 comment
Labels
conda Issue related to Conda dependencies enhancement New feature or request needs-decision Undecided if this should be done performance An issue related to performance pypi Issue related to PyPI dependencies

Comments

@tdejager
Copy link
Contributor

tdejager commented May 7, 2024

Problem description

The current implementation uses the conda environments as a base environments for the building of python packages source distributions. It provides the python interpreter to the uv solve that pixi triggers when solving pypi-dependencies. Even without needing to install an environment, if said environment contains pypi-dependencies the conda prefix for that environment still needs to be installed, this is because we do not know before-hand if there are any source dists that need to be built.

This results in multiple downsides:

Proposal

Give the user the ability to create a custom build environment for the pypi-dependencies which only contains a minimal set of requirements which can be installed on all systems. For example only python, skipping all other dependencies. This also allows for more options, like installing specific dependencies for building a package, and in conjunction with the addition of #1124 can avoid having to use pypi-dependencies for building a source dist altogether.

The following project can only be solved on a linux-64 machine:

[project]
name = "project"
platforms = ["linux-64", "osx-arm64"]
channels = ["conda-forge"]

[dependencies]
python = "3.12"

[feature.cuda]
platforms = ["linux-64"]
system-requirements = { cuda = "12.2" }

[feature.cuda.dependencies]
cuda = "*"

[pypi-dependencies]
local = {path = ".", editable = true}

[environments]
cuda = ["cuda"]

Specification in manifest

Option 1: Add build environment to environment

[feature.build.dependencies]
python = "3.10"
hatchling = "*"

[environments]
# define a specific build environment
python-build-env = { features =  ["build"], no-default-feature = true }
# Use said build environment in other environments
cuda = {features = ["cuda"], pypi-build-environment = "python-build-env"}

Pros:

  • Reuse a lot of the existing logic for environments.
  • Very explicit.

Cons:

  • You probably want no-default-feature = true .
  • Needs to specify a build environment per python interpreter.
  • If you forget to add the field explicitly per environment you automatically get the problems back.
  • Blowup lock file

Option 2: A pypi-build-environment table

[pypi-build-environment]
# Define which dependencies you want to reuse from the feature itself
reuse = ["python", "sdl2"]
# Define the dependencies you need to add in the build environment
dependencies = {hatch = "*"}

[dependencies]
sdl2 = "*"

[feature.py310]
dependencies = { python = "3.10"}

[environments]
cuda = {features = ["cuda", "py310"]}
py310 = ["py310"]

Pros:

  • Possibly only define it once in the default feature
  • The ability to specialize it in other features
  • Once you define the [pypi-build-environment] it is automatically inherited in all other environments.

Cons:

  • Adding another complex field to the manifest
  • Needs extra logic to override dependencies in features.
  • Blowup lock file
Option 3: Use host-dependencies
[host-dependencies]
python = {version = "3.10"}
sdl2 = {version = "*"}
pytest = "*"


[features.foo.host-dependencies]
sdl2 = {version = "2.1"}

This would traverse all environments and figure out the unique and re-usable build environments per environment.

Pros:

  • Quite simple specification

Cons:

  • How to handle system-requirements, sometimes you might want them in the build-env, but in the case of cuda you might not.
  • Might be difficult to find-out what is included in the environment by looking at the manifest.
  • This might be weird if it has two meanings for a possible pixi build although you will probably want those dependencies in that case

How is this going to be backwards compatible?

The current behavior doesn't change, so a pypi build environment will be an opt-in feature, as for simple use-cases it does work.

Alternative solutions to the described problems

There are some alternatives:

  • Configure features to be binary-only, this would allow only wheel files and no python interpreter is needed for installation.
    There are challenges:
    1. When a feature has an editable you need a python interpreter and this would require installing the prefix once again.
    2. Might be pretty slow when installing a large conda-prefix that would not be needed for the build
    3. You run into the limitation of not having sdists in the real world pretty quickly, binaries are already preferred by uv so are eagerly selected.
  • Do a double solve, first binary-only and in case of failure install the prefix and resolve with sdist support.
    • 1 and 3 from above hold
    1. You might get different behavior when running the solve from one day to the next.
  • Allow granular locking per-environment

We do think that wheel-only is a good idea nonetheless that we would want to implement.

@tdejager tdejager added the enhancement New feature or request label May 7, 2024
@ruben-arts ruben-arts added performance An issue related to performance needs-decision Undecided if this should be done pypi Issue related to PyPI dependencies conda Issue related to Conda dependencies labels May 7, 2024
@pavelzw
Copy link
Contributor

pavelzw commented May 13, 2024

I find 1. a bit verbose if you need to add it to every environment.
If we find a good way to incorporate the host dependencies with pixi build, I would be in favor of that.
My use cases (use conda packages for everything, only use uv for doing the editable install of .) could look as follows:

Building a library:

[host-dependencies] # or [pypi-build-environment]
python = "*"
hatchling = "*"

[pypi-dependencies]
polarify = {path = ".", editable = true, ignore-dependencies = true, build-isolation = false}

[environments]
default = ["test"]
pl014 = ["pl014", "py39", "test"]
pl015 = ["pl015", "py39", "test"]
# ...

here, ignore-dependencies results in the python interpreter not needing to be installed during solve time (dependencies of . are not added to the lockfile anyway)
For building the wheel, we use a separate host environment which contains only python and hatchling. hatchling is also not contained in the default, pl014, ... environments anymore (different (but imo better) to how it's working now)

Building an application:

[host-dependencies] # or [pypi-build-environment]
python = "*"
hatchling = "*"

[feature.dev.pypi-dependencies] # or [pypi-dependencies]
polarify = {path = ".", editable = true, ignore-dependencies = true, build-isolation = false}

[feature.prod.pypi-dependencies]
polarify = {path = ".", editable = false, ignore-dependencies = true, build-isolation = false}

[environments]
default = { features = ["dev"], solve-group = "prod" }
prod = { features = ["prod"], solve-group = "prod" }

here, hatchling also isn't in the prod and default environment as well and the wheel is built inside the specific host environment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
conda Issue related to Conda dependencies enhancement New feature or request needs-decision Undecided if this should be done performance An issue related to performance pypi Issue related to PyPI dependencies
Projects
None yet
Development

No branches or pull requests

3 participants