Skip to content

Commit

Permalink
Revert to boolean no-default-feature
Browse files Browse the repository at this point in the history
  • Loading branch information
olivier-lacroix committed Apr 27, 2024
1 parent 7661d8a commit 2c7d9fc
Show file tree
Hide file tree
Showing 13 changed files with 111 additions and 303 deletions.
18 changes: 6 additions & 12 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,29 +617,23 @@ platforms = ["linux-64", "osx-arm64"]

The `environments` table allows you to define environments that are created using the features defined in the `feature` tables.

Each environment is defined using the following fields:
The environments table is defined using the following fields:

- `features`: The features that are included in the environment. Unless `include-default` or `exclude-default` are set, all components of the default feature are included by default.
- `features`: The features that are included in the environment. Unless `no-default-feature` is set to `true`; the default feature is always included.
- `solve-group`: The solve group is used to group environments together at the solve stage.
This is useful for environments that need to have the same dependencies but might extend them with additional dependencies.
For instance when testing a production environment with additional test dependencies.
These dependencies will then be the same version in all environments that have the same solve group.
But the different environments contain different subsets of the solve-groups dependencies set.
- `include-default`: It is used to list the components of the default feature to include in that environment. Other components of the default feature are excluded.
- `exclude-default`: It is used to list the components of the default feature to exclude from that environment. Other components of the default feature are included.

Note that fields `include-default` and `exclude-default`:
- are mutually exclusive; only one of them can be specified for a given environment.
- can contain any of "system-requirements", "channels", "platforms", "dependencies", "pypi-dependencies", "activation", "tasks".
- `no-default-feature`: Whether to include the default feature in that environment. The default is to include the default feature.

```toml title="Full environments table specification"
[environments]
test = {features = ["test"], solve-group = "test"}
prod = {features = ["prod"], solve-group = "test"}
lint = {features = ["lint"], include-default = ["channels", "platforms"]}
prod = ["lint"]
```

In the simplest of cases, it is possible to define an environment only by listing its features:
As shown in the example above, in the simplest of cases, it is possible to define an environment only by listing its features:

```toml title="Simplest example"
[environments]
Expand All @@ -648,7 +642,7 @@ test = ["test"]

Which is equivalent to

```toml title="Simplest example"
```toml title="Simplest example expanded"
[environments]
test = {features = ["test"]}
```
Expand Down
2 changes: 1 addition & 1 deletion examples/polarify/pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ py39 = ["py39", "test"]
py310 = ["py310", "test"]
py311 = ["py311", "test"]
py312 = ["py312", "test"]
lint = { features=["lint"], include-default=["platforms","channels"] }
lint = { features=["lint"], no-default-feature=true }

## test this with:
#pixi run test
Expand Down
98 changes: 53 additions & 45 deletions schema/model.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""A canonical schema definition for the ``pixi.toml`` manifest file."""

from __future__ import annotations

import json
Expand Down Expand Up @@ -62,6 +63,7 @@ class Config:

class ChannelInlineTable(StrictBaseModel):
"""A precise description of a `conda` channel, with an optional priority."""

channel: ChannelName = Field(description="The channel the packages needs to be fetched from")
priority: int | None = Field(None, description="The priority of the channel")

Expand All @@ -71,21 +73,28 @@ class ChannelInlineTable(StrictBaseModel):

class Project(StrictBaseModel):
"""The project's metadata information."""

name: NonEmptyStr = Field(
description="The name of the project; we advise use of the name of the repository"
)
version: NonEmptyStr | None = Field(
None, description="The version of the project; we advise use of [SemVer](https://semver.org)", examples=["1.2.3"]
None,
description="The version of the project; we advise use of [SemVer](https://semver.org)",
examples=["1.2.3"],
)
description: NonEmptyStr | None = Field(None, description="A short description of the project")
authors: list[NonEmptyStr] | None = Field(
None, description="The authors of the project", examples=["John Doe <[email protected]>"]
)
channels: list[Channel] = Field(
None, description="The `conda` channels that can be used in the project. Unless overridden by `priority`, the first channel listed will be preferred."
None,
description="The `conda` channels that can be used in the project. Unless overridden by `priority`, the first channel listed will be preferred.",
)
platforms: list[Platform] = Field(description="The platforms that the project supports")
license: NonEmptyStr | None = Field(None, description="The license of the project; we advise using an [SPDX](https://spdx.org/licenses/) identifier.")
license: NonEmptyStr | None = Field(
None,
description="The license of the project; we advise using an [SPDX](https://spdx.org/licenses/) identifier.",
)
license_file: PathNoBackslash | None = Field(
None, alias="license-file", description="The path to the license file of the project"
)
Expand All @@ -111,6 +120,7 @@ class Project(StrictBaseModel):

class MatchspecTable(StrictBaseModel):
"""A precise description of a `conda` package version."""

version: NonEmptyStr | None = Field(
None,
description="The version of the package in [MatchSpec](https://github.com/conda/conda/blob/078e7ee79381060217e1ec7f9b0e9cf80ecc8f3f/conda/models/match_spec.py) format",
Expand All @@ -134,7 +144,10 @@ class MatchspecTable(StrictBaseModel):


class _PyPIRequirement(StrictBaseModel):
extras: list[NonEmptyStr] | None = Field(None, description="The [PEP 508 extras](https://peps.python.org/pep-0508/#extras) of the package")
extras: list[NonEmptyStr] | None = Field(
None,
description="The [PEP 508 extras](https://peps.python.org/pep-0508/#extras) of the package",
)


class _PyPiGitRequirement(_PyPIRequirement):
Expand Down Expand Up @@ -198,7 +211,8 @@ class PyPIVersion(_PyPIRequirement):
HostDependenciesField = Field(
None,
alias="host-dependencies",
description="The host `conda` dependencies, used in the build process", examples=[{"python": ">=3.8"}]
description="The host `conda` dependencies, used in the build process",
examples=[{"python": ">=3.8"}],
)
BuildDependenciesField = Field(
None,
Expand All @@ -212,21 +226,26 @@ class PyPIVersion(_PyPIRequirement):
################
TaskName = Annotated[str, Field(pattern=r"^[^\s\$]+$", description="A valid task name.")]


class TaskInlineTable(StrictBaseModel):
"""A precise definition of a task."""

cmd: list[NonEmptyStr] | NonEmptyStr | None = Field(
None, description="A shell command to run the task in the limited, but cross-platform `bash`-like `deno_task_shell`. See the documentation for [supported syntax](https://pixi.sh/latest/features/advanced_tasks/#syntax)"
None,
description="A shell command to run the task in the limited, but cross-platform `bash`-like `deno_task_shell`. See the documentation for [supported syntax](https://pixi.sh/latest/features/advanced_tasks/#syntax)",
)
cwd: PathNoBackslash | None = Field(None, description="The working directory to run the task")
depends_on: list[TaskName] | TaskName | None = Field(
None, description="The tasks that this task depends on. Environment variables will **not** be expanded."
None,
description="The tasks that this task depends on. Environment variables will **not** be expanded.",
)
inputs: list[Glob] | None = Field(
None,
description="A list of `.gitignore`-style glob patterns that should be watched for changes before this command is run. Environment variables _will_ be expanded.",
)
outputs: list[Glob] | None = Field(
None, description="A list of `.gitignore`-style glob patterns that are generated by this command. Environment variables _will_ be expanded."
None,
description="A list of `.gitignore`-style glob patterns that are generated by this command. Environment variables _will_ be expanded.",
)
env: dict[NonEmptyStr, NonEmptyStr] | None = Field(
None,
Expand All @@ -247,6 +266,7 @@ class LibcFamily(StrictBaseModel):

class SystemRequirements(StrictBaseModel):
"""Platform-specific requirements"""

linux: PositiveFloat | NonEmptyStr | None = Field(
None, description="The minimum version of the Linux kernel"
)
Expand All @@ -269,19 +289,11 @@ class SystemRequirements(StrictBaseModel):
EnvironmentName = Annotated[str, Field(pattern=r"^[a-z\d\-]+$")]
FeatureName = NonEmptyStr
SolveGroupName = NonEmptyStr
Component = (
Literal["system-requirements"]
| Literal["channels"]
| Literal["platforms"]
| Literal["dependencies"]
| Literal["pypi-dependencies"]
| Literal["activation"]
| Literal["tasks"]
)


class Environment(StrictBaseModel):
"""A composition of the dependencies of features which can be activated to run tasks or provide a shell"""

features: list[FeatureName] | None = Field(
None, description="The features that define the environment"
)
Expand All @@ -290,15 +302,10 @@ class Environment(StrictBaseModel):
alias="solve-group",
description="The group name for environments that should be solved together",
)
include_default: list[Component] | None = Field(
False,
alias="include-default",
description="Components of the default feature to include",
)
exclude_default: list[Component] | None = Field(
no_default_feature: Optional[bool] = Field(
False,
alias="exclude-default",
description="Components of the default feature to exclude",
alias="no-default-feature",
description="Whether to add the default feature to this environment",
)


Expand All @@ -307,6 +314,7 @@ class Environment(StrictBaseModel):
######################
class Activation(StrictBaseModel):
"""A description of steps performed when an environment is activated"""

scripts: list[NonEmptyStr] | None = Field(
None,
description="The scripts to run when the environment is activated",
Expand All @@ -322,6 +330,7 @@ class Activation(StrictBaseModel):

class Target(StrictBaseModel):
"""A machine-specific configuration of dependencies and tasks"""

dependencies: Dependencies = DependenciesField
host_dependencies: Dependencies = HostDependenciesField
build_dependencies: Dependencies = BuildDependenciesField
Expand All @@ -341,8 +350,10 @@ class Target(StrictBaseModel):
###################
class Feature(StrictBaseModel):
"""A composable aspect of the project which can contribute dependencies and tasks to an environment"""

channels: list[Channel] | None = Field(
None, description="The `conda` channels that can be considered when solving environments containing this feature"
None,
description="The `conda` channels that can be considered when solving environments containing this feature",
)
platforms: list[NonEmptyStr] | None = Field(
None,
Expand Down Expand Up @@ -377,19 +388,20 @@ class Feature(StrictBaseModel):

class BaseManifest(StrictBaseModel):
"""The configuration for a [`pixi`](https://pixi.sh) project."""

class Config:
json_schema_extra = {
"$id": SCHEMA_URI,
"$schema": SCHEMA_DRAFT,
"title": "`pixi.toml` manifest file"
"title": "`pixi.toml` manifest file",
}

schema_: str | None = Field(
SCHEMA_URI,
alias="$schema",
title="Schema",
description="The schema identifier for the project's configuration",
format="uri-reference"
format="uri-reference",
)

project: Project = Field(..., description="The project's metadata information")
Expand All @@ -406,7 +418,8 @@ class Config:
None, alias="system-requirements", description="The system requirements of the project"
)
environments: dict[EnvironmentName, Environment | list[FeatureName]] | None = Field(
None, description="The environments of the project, defined as a full object or a list of feature names."
None,
description="The environments of the project, defined as a full object or a list of feature names.",
)
feature: dict[FeatureName, Feature] | None = Field(
None, description="The features of the project"
Expand All @@ -419,7 +432,9 @@ class Config:
description="The targets of the project",
examples=[{"linux": {"dependencies": {"python": "3.8"}}}],
)
tool: dict[str, Any] = Field(None, description="Third-party tool configurations, ignored by pixi")
tool: dict[str, Any] = Field(
None, description="Third-party tool configurations, ignored by pixi"
)


#########################
Expand All @@ -429,6 +444,7 @@ class Config:

class SchemaJsonEncoder(json.JSONEncoder):
"""A custom schema encoder for normalizing schema to be used with TOML files."""

HEADER_ORDER = [
"$schema",
"$id",
Expand All @@ -440,8 +456,7 @@ class SchemaJsonEncoder(json.JSONEncoder):
"required",
"additionalProperties",
"default",
"items"
"properties",
"items" "properties",
"patternProperties",
"allOf",
"anyOf",
Expand Down Expand Up @@ -483,7 +498,7 @@ class SchemaJsonEncoder(json.JSONEncoder):
def encode(self, obj):
"""Overload the default ``encode`` behavior."""
if isinstance(obj, dict):
obj = self.normalize_schema(deepcopy(obj))
obj = self.normalize_schema(deepcopy(obj))

return super().encode(obj)

Expand Down Expand Up @@ -534,7 +549,8 @@ def strip_nulls(self, obj: dict[str, Any]) -> dict[str, Any]:

for nest in self.SORT_NESTED_ARR:
some_of = [
self.normalize_schema(option) for option in obj.get(nest, [])
self.normalize_schema(option)
for option in obj.get(nest, [])
if option.get("type") != "null"
]

Expand All @@ -552,22 +568,14 @@ def sort_nested(self, obj: dict[str, Any], key: str) -> dict[str, Any]:
return obj
obj[key] = {
k: self.normalize_schema(v) if isinstance(v, dict) else v
for k, v in sorted(
obj[key].items(),
key=lambda kv: kv[0]
)
for k, v in sorted(obj[key].items(), key=lambda kv: kv[0])
}
return obj


##########################
# Command Line Interface #
##########################

if __name__ == "__main__":
print(
json.dumps(
BaseManifest.model_json_schema(),
indent=2,
cls=SchemaJsonEncoder
)
)
print(json.dumps(BaseManifest.model_json_schema(), indent=2, cls=SchemaJsonEncoder))
Loading

0 comments on commit 2c7d9fc

Please sign in to comment.