Skip to content

Add pixi support #204

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

Draft
wants to merge 49 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
4ce5816
Add pixi support
basnijholt Oct 4, 2024
ef59760
Merge 4ce58161b8cdfe1f0a78bea141f4df4b89aac898 into 334e7739cd296f317…
basnijholt Oct 4, 2024
63890d1
Update files from markdown-code-runner
github-actions[bot] Oct 4, 2024
75fc81a
wip
basnijholt Oct 4, 2024
f0b5c43
Merge 75fc81a010ff2e2e00d66e7ea5c9546eedffc8d9 into 334e7739cd296f317…
basnijholt Oct 4, 2024
44d677b
Update files from markdown-code-runner
github-actions[bot] Oct 4, 2024
fb07b23
wip
basnijholt Oct 4, 2024
737ff61
args
basnijholt Oct 4, 2024
ef7967f
Merge 737ff615b4bf484b1ca358adcef94060357f6bed into 24ac2e7a71fe63b7c…
basnijholt Oct 4, 2024
0ff0e85
Update files from markdown-code-runner
github-actions[bot] Oct 4, 2024
13d1494
wip
basnijholt Oct 4, 2024
f0fdcc1
wip
basnijholt Oct 4, 2024
0bd689a
wip
basnijholt Oct 4, 2024
7cd9608
p.
basnijholt Oct 4, 2024
212dc94
Merge 7cd96082bab4dadda5efcc83c10c525da1dd6300 into 24ac2e7a71fe63b7c…
basnijholt Oct 4, 2024
ab4821b
Update files from markdown-code-runner
github-actions[bot] Oct 4, 2024
37f138c
test
basnijholt Oct 4, 2024
e966f0b
wip
basnijholt Oct 4, 2024
189584d
wip
basnijholt Oct 5, 2024
d966163
.
basnijholt Oct 5, 2024
5be1b00
rev
basnijholt Oct 5, 2024
91f4dce
Merge 5be1b00cf69606566b1cbad7d66460e86c434d5f into 24ac2e7a71fe63b7c…
basnijholt Oct 5, 2024
cc21b5d
Update files from markdown-code-runner
github-actions[bot] Oct 5, 2024
280a8d2
wip
basnijholt Oct 5, 2024
0b6373c
.
basnijholt Oct 5, 2024
6b8ff99
Merge 0b6373c01ea8dc326255bce1e0d8b1ecfde2ee0f into 24ac2e7a71fe63b7c…
basnijholt Oct 5, 2024
b6c5b0d
Update files from markdown-code-runner
github-actions[bot] Oct 5, 2024
42d4d1e
wip
basnijholt Oct 5, 2024
2b33126
.
basnijholt Oct 5, 2024
2778303
v0
basnijholt Oct 6, 2024
3e82ada
.
basnijholt Oct 6, 2024
c193fb7
simplify
basnijholt Oct 6, 2024
bf56137
Merge remote-tracking branch 'origin/main' into pixi
basnijholt Jan 6, 2025
e934034
Merge remote-tracking branch 'origin/main' into pixi
basnijholt Feb 8, 2025
e797dbd
Merge e934034819cdb6f22952168c15febbe7b0476fd0 into 316a7d3107468d7ff…
basnijholt Feb 8, 2025
f801f80
Update files from markdown-code-runner
github-actions[bot] Feb 8, 2025
ce745cc
relock
basnijholt Feb 8, 2025
2730de9
set manifest file
basnijholt Feb 8, 2025
b1c8f75
ENH: Track the origin of each Spec
basnijholt Feb 8, 2025
2ef41e0
Extra test
basnijholt Feb 8, 2025
54e7a77
rename
basnijholt Feb 8, 2025
cc96122
Merge branch 'trace-deps' into pixi
basnijholt Feb 8, 2025
ac5cda9
Define as features
basnijholt Feb 8, 2025
cff66b4
new locking mechanism
basnijholt Feb 8, 2025
116be20
fixes
basnijholt Feb 8, 2025
8edd55c
format
basnijholt Feb 8, 2025
8c18dc5
convert name
basnijholt Feb 8, 2025
b7d701f
original origin
basnijholt Feb 8, 2025
b7d792f
alway pass
basnijholt Feb 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,6 @@ ENV/
# Rope project settings
.ropeproject

# other
# pixi environments
.pixi
*.egg-info
31 changes: 20 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,16 +420,17 @@ See [example](example/) for more information or check the output of `unidep -h`
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
```bash
usage: unidep [-h]
{merge,install,install-all,conda-lock,pip-compile,pip,conda,version} ...
{merge,install,install-all,conda-lock,pixi-lock,pip-compile,pip,conda,version} ...

Unified Conda and Pip requirements management.

positional arguments:
{merge,install,install-all,conda-lock,pip-compile,pip,conda,version}
{merge,install,install-all,conda-lock,pixi-lock,pip-compile,pip,conda,version}
Subcommands
merge Combine multiple (or a single) `requirements.yaml` or
`pyproject.toml` files into a single Conda installable
`environment.yaml` file.
`environment.yaml` file or Pixi installable
`pixi.toml` file.
install Automatically install all dependencies from one or
more `requirements.yaml` or `pyproject.toml` files.
This command first installs dependencies with Conda,
Expand All @@ -448,6 +449,11 @@ positional arguments:
lock.yml` files for each `requirements.yaml` or
`pyproject.toml` file consistent with the global lock
file.
pixi-lock Generate a global `pixi.lock` file for a collection of
`requirements.yaml` or `pyproject.toml` files.
Additionally, create individual `pixi.lock` files for
each `requirements.yaml` or `pyproject.toml` file
consistent with the global lock file.
pip-compile Generate a fully pinned `requirements.txt` file from
one or more `requirements.yaml` or `pyproject.toml`
files using `pip-compile` from `pip-tools`. This
Expand Down Expand Up @@ -482,30 +488,33 @@ See `unidep merge -h` for more information:
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
```bash
usage: unidep merge [-h] [-o OUTPUT] [-n NAME] [--stdout]
[--selector {sel,comment}] [-d DIRECTORY] [--depth DEPTH]
[-v]
[--selector {sel,comment}] [--pixi] [-d DIRECTORY]
[--depth DEPTH] [-v]
[-p {linux-64,linux-aarch64,linux-ppc64le,osx-64,osx-arm64,win-64}]
[--skip-dependency SKIP_DEPENDENCY]
[--ignore-pin IGNORE_PIN] [--overwrite-pin OVERWRITE_PIN]

Combine multiple (or a single) `requirements.yaml` or `pyproject.toml` files
into a single Conda installable `environment.yaml` file. Example usage:
`unidep merge --directory . --depth 1 --output environment.yaml` to search for
`requirements.yaml` or `pyproject.toml` files in the current directory and its
subdirectories and create `environment.yaml`. These are the defaults, so you
can also just run `unidep merge`.
into a single Conda installable `environment.yaml` file or Pixi installable
`pixi.toml` file. Example usage: `unidep merge --directory . --depth 1
--output environment.yaml` to search for `requirements.yaml` or
`pyproject.toml` files in the current directory and its subdirectories and
create `environment.yaml`. These are the defaults, so you can also just run
`unidep merge`.

options:
-h, --help show this help message and exit
-o, --output OUTPUT Output file for the conda environment, by default
`environment.yaml`
`environment.yaml` or `pixi.toml` if `--pixi` is used
-n, --name NAME Name of the conda environment, by default `myenv`
--stdout Output to stdout instead of a file
--selector {sel,comment}
The selector to use for the environment markers, if
`sel` then `- numpy # [linux]` becomes `sel(linux):
numpy`, if `comment` then it remains `- numpy #
[linux]`, by default `sel`
--pixi Generate a `pixi.toml` file instead of
`environment.yaml`
-d, --directory DIRECTORY
Base directory to scan for `requirements.yaml` or
`pyproject.toml` file(s), by default `.`
Expand Down
3 changes: 2 additions & 1 deletion example/environment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ dependencies:
- pytest
- pytest-cov
- pip:
- unidep
- unidep; sys_platform == 'linux' and platform_machine == 'x86_64'
- unidep; sys_platform == 'darwin'
- markdown-code-runner
- numthreads
- yaml2bib; sys_platform == 'linux' and platform_machine == 'x86_64'
Expand Down
2 changes: 1 addition & 1 deletion example/hatch_project/requirements.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ channels:
- conda-forge
dependencies:
- conda: adaptive-scheduler # [linux64]
- pip: unidep
- pip: unidep # [linux64]
- numpy >=1.21
- hpc05 # [linux64]
- pandas >=1,<3
Expand Down
154 changes: 154 additions & 0 deletions pixi_create_sub_lock_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
"""Create a subset of a lock file with a subset of packages."""

from __future__ import annotations

import asyncio
import json
import os
import tempfile
from collections import defaultdict

from rattler import (
Environment,
GenericVirtualPackage,
LockFile,
Platform,
Version,
solve_with_sparse_repodata,
)
from rattler.channel import Channel, ChannelConfig
from rattler.match_spec import MatchSpec
from rattler.repo_data import SparseRepoData


def create_repodata_from_pixi_lock(lock_file_path: str) -> dict[str, dict]:
"""Create repodata from a pixi lock file."""
lock_file = LockFile.from_path(lock_file_path)
env = lock_file.default_environment()
repodata = {}
for platform in env.platforms():
subdir = str(platform)
packages = env.conda_repodata_records_for_platform(platform)
if not packages:
continue

repodata[subdir] = {
"info": {
"subdir": subdir,
"base_url": f"https://conda.anaconda.org/conda-forge/{subdir}",
},
"packages": {
f"{pkg.name.normalized}-{pkg.version}-{pkg.build}.conda": {
"build": pkg.build,
"build_number": pkg.build_number,
"depends": pkg.depends,
"constrains": pkg.constrains,
"license": pkg.license,
"license_family": pkg.license_family,
"md5": pkg.md5.hex() if pkg.md5 else None,
"name": pkg.name.normalized,
"sha256": pkg.sha256.hex() if pkg.sha256 else None,
"size": pkg.size,
"subdir": pkg.subdir,
"timestamp": int(pkg.timestamp.timestamp() * 1000)
if pkg.timestamp
else None,
"version": str(pkg.version),
}
for pkg in packages
},
"repodata_version": 2,
}
return repodata


def _version_requirement_to_lowest_version(version: str | None) -> str | None:
if version is None:
return None
if version.startswith(">="):
version = version[2:]
if version.startswith("=="):
version = version[2:]
version = version.split(",")[0]
return version # noqa: RET504


def all_virtual_packages(env: Environment) -> dict[Platform, set[str]]:
"""Get all virtual packages from an environment."""
virtual_packages = defaultdict(set)
for platform, packages in env.packages_by_platform().items():
for package in packages:
if not package.is_conda:
continue
repo_record = package.as_conda()
for dep in repo_record.depends:
spec = MatchSpec(dep)
if spec.name.normalized.startswith("__"):
version = _version_requirement_to_lowest_version(spec.version)
virtual_package = GenericVirtualPackage(
spec.name,
version=Version(version or "0"),
build_string=spec.build or "*",
)
virtual_packages[platform].add(virtual_package)
return virtual_packages


async def create_subset_lock_file(
original_lock_file_path: str,
required_packages: list[str],
platform: Platform,
) -> LockFile:
"""Create a new lock file with a subset of packages from original lock file."""
original_lock_file = LockFile.from_path(original_lock_file_path)
env = original_lock_file.default_environment()
conda_records = env.conda_repodata_records_for_platform(platform)
if conda_records is None:
msg = f"No conda records found for platform {platform}"
raise ValueError(msg)
repodata = create_repodata_from_pixi_lock(original_lock_file_path)
platform_repodata = repodata.get(str(platform))
if platform_repodata is None:
msg = f"No repodata found for platform {platform}"
raise ValueError(msg)

with tempfile.NamedTemporaryFile(
mode="w",
delete=False,
suffix=".json",
) as temp_file:
json.dump(platform_repodata, temp_file)
temp_file_path = temp_file.name
print(f"Temporary repodata file: {temp_file_path}")
dummy_channel = Channel("dummy", ChannelConfig())
sparse_repo_data = SparseRepoData(dummy_channel, str(platform), temp_file_path)
specs = [MatchSpec(pkg) for pkg in required_packages]
virtual_packages = all_virtual_packages(env)[platform]

solved_records = await solve_with_sparse_repodata(
specs=specs,
sparse_repodata=[sparse_repo_data],
locked_packages=conda_records,
virtual_packages=virtual_packages,
)
new_env = Environment("new_env", {platform: solved_records})
new_lock_file = LockFile({"new_env": new_env})
os.unlink(temp_file_path) # noqa: PTH108
return new_lock_file


async def main() -> None:
"""Example usage of create_subset_lock_file."""
original_lock_file_path = "pixi.lock"
required_packages = ["tornado", "scipy", "ipykernel", "adaptive"]
platform = Platform("linux-64")
new_lock_file = await create_subset_lock_file(
original_lock_file_path,
required_packages,
platform,
)
new_lock_file.to_path("new_lock_file.lock")


if __name__ == "__main__":
asyncio.run(main())
3 changes: 3 additions & 0 deletions tests/simple_monorepo/common-requirements.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ channels:
- conda-forge
dependencies:
- conda: python_abi
platforms:
- osx-64
- osx-arm64
97 changes: 97 additions & 0 deletions tests/simple_monorepo/pixi.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading