Skip to content
Snippets Groups Projects
Commit e3da23a4 authored by André Anjos's avatar André Anjos :speech_balloon:
Browse files

[scripts.pixi] More default functionality

parent 11fcc50a
No related branches found
No related tags found
1 merge request!14Provide support for auto-generating pixi configuration files
Pipeline #85256 passed
...@@ -18,7 +18,8 @@ logger = setup(__name__.split(".", 1)[0]) ...@@ -18,7 +18,8 @@ logger = setup(__name__.split(".", 1)[0])
epilog=""" epilog="""
Examples: Examples:
1. Creates a pixi configuration file for a project you just checked out: 1. Creates a **draft** pixi configuration file for a project you just checked
out:
.. code:: sh .. code:: sh
...@@ -27,7 +28,7 @@ Examples: ...@@ -27,7 +28,7 @@ Examples:
... ...
>>> >>>
2. Creates a pixi configuration file for a project you checked out at 2. Creates a draft pixi configuration file for a project you checked out at
directory my-project: directory my-project:
.. code:: sh .. code:: sh
...@@ -40,10 +41,10 @@ Examples: ...@@ -40,10 +41,10 @@ Examples:
.. tip:: .. tip::
You may hand-edit the output file ``pixi.toml`` to adjust for details, add You may hand-edit the output file ``pixi.toml`` to adjust for details,
conda or Python packages you'd like to complement your work environment. add conda or Python packages you'd like to complement your work
An example would be adding debuggers such as ``pdbpp`` to the installation environment. An example would be adding debuggers such as ``pdbpp`` to
plan. the installation plan.
""", """,
) )
...@@ -70,29 +71,11 @@ Examples: ...@@ -70,29 +71,11 @@ Examples:
show_default=True, show_default=True,
help="Version of python to build the environment for", help="Version of python to build the environment for",
) )
@click.option(
"-i/-I",
"--ignore-template/--no-ignore-template",
default=False,
show_default=True,
help="If set, then ignores any project-based templates found "
"on ``conda/pixi.toml.in``",
)
@click.option(
"-o",
"--output",
default="pixi.toml",
show_default=True,
help="The name of the environment plan file",
type=click.Path(path_type=pathlib.Path),
)
@verbosity_option(logger=logger) @verbosity_option(logger=logger)
def pixi( def pixi(
project_dir, project_dir,
profile, profile,
python, python,
ignore_template,
output,
**_, **_,
) -> None: ) -> None:
"""Create a pixi recipe for a project.""" """Create a pixi recipe for a project."""
...@@ -105,27 +88,34 @@ def pixi( ...@@ -105,27 +88,34 @@ def pixi(
the_profile = Profile(profile) the_profile = Profile(profile)
def _make_requirement_dict( python_to_conda = {
requirements: list[str], version: dict[str, str] v: k
) -> dict[str, str]: for k, v in the_profile.data["conda"]["to_python"].items()
if not k.startswith("__")
}
version = the_profile.conda_constraints(python)
assert version is not None
def _make_requirement_dict(requirements: list[str]) -> dict[str, str]:
retval: dict[str, str] = {} retval: dict[str, str] = {}
for k in requirements: for k in requirements:
pr = packaging.requirements.Requirement(k) pr = packaging.requirements.Requirement(k)
name = (
pr.name
if pr.name not in python_to_conda
else python_to_conda[pr.name]
)
if pr.name in version: if pr.name in version:
retval[pr.name] = version[pr.name] retval[name] = version[name]
if pr.specifier: if pr.specifier:
if pr.name in retval: if name in retval:
retval[pr.name] = ",".join( retval[name] = ",".join((retval[name], str(pr.specifier)))
(retval[pr.name], str(pr.specifier))
)
else: else:
retval[pr.name] = str(pr.specifier) retval[name] = str(pr.specifier)
retval.setdefault(pr.name, "*") retval.setdefault(name, "*")
return retval return retval
version = the_profile.conda_constraints(python)
assert version is not None
# loads the pyproject.toml file (easy) # loads the pyproject.toml file (easy)
pyproject = project_dir / "pyproject.toml" pyproject = project_dir / "pyproject.toml"
if pyproject.exists(): if pyproject.exists():
...@@ -143,7 +133,7 @@ def pixi( ...@@ -143,7 +133,7 @@ def pixi(
+ pyproject["project"].get("maintainers", []) + pyproject["project"].get("maintainers", [])
], ],
description=pyproject["project"]["description"], description=pyproject["project"]["description"],
license=pyproject["project"]["license"]["text"], # license=pyproject["project"]["license"]["text"],
readme="README.md", readme="README.md",
homepage=pyproject["project"]["urls"]["homepage"], homepage=pyproject["project"]["urls"]["homepage"],
repository=pyproject["project"]["urls"]["repository"], repository=pyproject["project"]["urls"]["repository"],
...@@ -159,7 +149,7 @@ def pixi( ...@@ -159,7 +149,7 @@ def pixi(
config["dependencies"] = {"python": python + ".*"} config["dependencies"] = {"python": python + ".*"}
config["dependencies"].update( config["dependencies"].update(
_make_requirement_dict( _make_requirement_dict(
pyproject.get("project", {}).get("dependencies", []), version pyproject.get("project", {}).get("dependencies", [])
) )
) )
...@@ -168,12 +158,12 @@ def pixi( ...@@ -168,12 +158,12 @@ def pixi(
# setup standardized build procedure # setup standardized build procedure
config.setdefault("feature", {}).setdefault("build", {})["dependencies"] = ( config.setdefault("feature", {}).setdefault("build", {})["dependencies"] = (
_make_requirement_dict( _make_requirement_dict(
pyproject.get("build-system", {}).get("requires", []), version pyproject.get("build-system", {}).get("requires", [])
) )
) )
# add pip so that the build works # add pip so that the build works
config["feature"]["build"]["dependencies"].update( config["feature"]["build"]["dependencies"].update(
_make_requirement_dict(["pip"], version) _make_requirement_dict(["pip"])
) )
config["feature"]["build"]["tasks"] = dict( config["feature"]["build"]["tasks"] = dict(
build="pip install --no-build-isolation --no-dependencies --editable ." build="pip install --no-build-isolation --no-dependencies --editable ."
...@@ -189,15 +179,31 @@ def pixi( ...@@ -189,15 +179,31 @@ def pixi(
): ):
config.setdefault("feature", {}).setdefault(feature, {})[ config.setdefault("feature", {}).setdefault(feature, {})[
"dependencies" "dependencies"
] = _make_requirement_dict(deps, version) ] = _make_requirement_dict(deps)
if "pre-commit" in config["feature"][feature]["dependencies"]: if "pre-commit" in config["feature"][feature]["dependencies"]:
config["feature"][feature]["tasks"] = { config["feature"][feature]["tasks"] = {
"qa": "pre-commit run --all-files" "qa": "pre-commit run --all-files",
"qa-install": "pre-commit install",
} }
# this feature can be separated from the rest # this feature can be separated from the rest
config.setdefault("environments", {})["qa"] = [feature] config.setdefault("environments", {})["qa"] = [feature]
cmds.append("To do quality-assurance, run: `pixi run qa`") cmds.append(
"To install pre-commit hook, run: `pixi run qa-install`"
)
cmds.append("To run quality-assurance, run: `pixi run qa`")
# if ruff is part of pre-commit configuration, then also add that
# dependence to the qa stack
precommit_config = project_dir / ".pre-commit-config.yaml"
if (
precommit_config.exists()
and "ruff" in precommit_config.open().read()
and "ruff" not in config["feature"][feature]["dependencies"]
):
config["feature"][feature]["dependencies"]["ruff"] = "*"
config["feature"][feature]["tasks"]["ruff"] = "ruff check"
cmds.append("To run a simple ruff check, run: `pixi run ruff`")
if "sphinx" in config["feature"][feature]["dependencies"]: if "sphinx" in config["feature"][feature]["dependencies"]:
config["feature"][feature]["tasks"] = { config["feature"][feature]["tasks"] = {
...@@ -226,6 +232,7 @@ def pixi( ...@@ -226,6 +232,7 @@ def pixi(
cmds.append("To do run test, run: `pixi run test`") cmds.append("To do run test, run: `pixi run test`")
# backup previous installation plan, if one exists # backup previous installation plan, if one exists
output = project_dir / "pixi.toml"
if output.exists(): if output.exists():
backup = output.parent / (output.name + "~") backup = output.parent / (output.name + "~")
shutil.copy(output, backup) shutil.copy(output, backup)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment