-
Notifications
You must be signed in to change notification settings - Fork 28
CEP for the next evolution of Repodata (v2) and MatchSpec #111
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
base: main
Are you sure you want to change the base?
Conversation
Here are comments from the HackMD:
For default flags I think we might want to deprecate the Or, maybe a better thought: we could also make it so that if Regarding Spack and Conan: I would be super hyped if you can distill this on how it could work for the conda ecosystem. I haven't had the time yet to read into it deeply. I am not a fan of super arbitrary code execution though. |
- `release`: only packages with `release` flag are used | ||
- `~release`: disallow packages with `release` flag | ||
- `?release`: if release flag available, filter on it, otherwise use any other | ||
- `gpu:*`: any flag starting with `gpu:` will be matched |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add an example for the string matching for say blas:mkl
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would an exact match not work fine?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would an exact match not work fine?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would, we should just state how to do an exact match
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You coudl just ask for flags = ["blas:mkl"]
for an exact match :)
|
||
The proposed syntax is to extend the `MatchSpec` syntax by appending `; if <CONDITION>` after the current MatchSpec. | ||
|
||
We would like to also allow for AND and OR with the following syntax: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably need NOT
and parentheses for precedence overrides, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In regular MatchSepc, we have ,
and |
used for versions for and
and or
. I think we should keep thing similar, even if it means supporting and
and or
in versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If find this hard to distinguish when parsing the version. E.g.
python <3.8|>3.9 | numpy >=2.0
python <3.8|>3.9 or numpy >=2.0
I find the version with or
easier to read than the one with pipes.
- six; if python <3.8 | ||
``` | ||
|
||
The proposed syntax is to extend the `MatchSpec` syntax by appending `; if <CONDITION>` after the current MatchSpec. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if we didn't need the ;
because somehow conda
will happily ignore the if
parts while parsing MatchSpec
s.
>>> from conda.models.match_spec import MatchSpec as M
>>> M("python 3.8 * if python")
MatchSpec("python==3.8[build=*]")
>>> M("python 3.8 'if' if __win") # quote 'if' to force parse it as a build string
MatchSpec("python==3.8='if'")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will also ignore parenthesised blocks:
>>> M("python 3.8 * (__win)")
MatchSpec("python==3.8[build=*]")
>>> M("python 3.8 (__win)")
MatchSpec("python==3.8")
>>> M("python 3.8 (__win and __osx)")
MatchSpec("python==3.8")
>>> M("python 3.8 (if __win and __osx)")
MatchSpec("python==3.8")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
libmamba
also ignores parentheses:
>>> from libmambapy.specs import MatchSpec as LibmambaMatchSpec
>>>print(LibmambaMatchSpec.parse("python 3.8 * (__win and __osx)"))
python==3.8
>>> print(LibmambaMatchSpec.parse("python 3.8 * (if __win and __osx)"))
python==3.8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My suggestion would be to design the syntax like this:
name [version [build]] ('if' condition)
The if
literal could be omitted, or replaced with with
, if folks feel it's clearer that way. See discussion in #conda-maintainers > Conditional dependencies syntax in v2 environments & recipes @ 💬.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another idea (not sure if a good one) could be: rather than make MatchSpecs more complex than they already are, build on the idea of selectors from the new recipe format and allow depends
to contain objects like so:
depends:
- python >=3.8
- if: python <3.8
then: six
In the recipe format, this is expressed via a variation on the selector to avoid being process at build time (if(run):
for instance).
This disallow basically disallow conditionals outside of recipes (or format with selector), but makes a more consistent narrative around conditionals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was indeed also posted on zulip. One issue is that it then becomes harder for cli tools to adopt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose we use the when
syntax without a semicolon to force an error on older versions of conda that dont support it and the distinguish between the already established if
syntax in recipe v1. We can use the same keyword in a more expanded form as the "build spec"
foobar when python >=3.8
E.g. in a recipe:
- if: unix
then: foobar
when: python >=3.8
# OR
- when: python >=3.8
then: foobar
# OR
- "foobar when python >=3.8"
|
||
## Conditional dependencies | ||
|
||
Conditional dependencies are activated when the condition is true. The most straight-forward conditions are `__unix`, `__win` and other platform specifiers. However, we would also like to support matchspecs in conditions such as `python >=3`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I very much like this idea. An implementation note: while if __unix
is reasonably easy to implement because it is "static", if python <3.8
is conceptually much harder as it is not something that can be decided ahead of solving. I requires to be able to adapt the dependencies of a package as partial candidates are investigated during solve.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, this is a form of boolean dependencies in rpm (already supported by libsolv I believe)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the PR that implements this for resolvo: prefix-dev/resolvo#136
|
||
However, it would be nice if we could have a flexible, powerful and simple syntax to enable or disable "flags" on packages in order to select a variant. | ||
|
||
A RepodataRecord should get a new field "flags" that is a list of strings, such as: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do flags
and extra
mix and overlap? Wouldn't conditional dependencies on a flag be enough to generate the extra
category?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Flags are part of a variant. So there is no variation of flags for a single variant. E.g. flags could be used to say a particular variant is using cuda and another is used to target cpu. Extras are a way to select additional dependencies for a particular variant. If a variant also adds a CLI tool it provides the extra "cli". Only if that extra is requested by another package are particular dependencies also requested.
In technical terms extras can indeed be implemented as conditional dependencies. E.g. for a package my_package
we could express it as typer when my_package[cli]
. If there is a package that depends on my_package[cli]
typer
would also be required.
|
||
The proposed syntax is to extend the `MatchSpec` syntax by appending `; if <CONDITION>` after the current MatchSpec. | ||
|
||
We would like to also allow for AND and OR with the following syntax: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If find this hard to distinguish when parsing the version. E.g.
python <3.8|>3.9 | numpy >=2.0
python <3.8|>3.9 or numpy >=2.0
I find the version with or
easier to read than the one with pipes.
- six; if python <3.8 | ||
``` | ||
|
||
The proposed syntax is to extend the `MatchSpec` syntax by appending `; if <CONDITION>` after the current MatchSpec. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose we use the when
syntax without a semicolon to force an error on older versions of conda that dont support it and the distinguish between the already established if
syntax in recipe v1. We can use the same keyword in a more expanded form as the "build spec"
foobar when python >=3.8
E.g. in a recipe:
- if: unix
then: foobar
when: python >=3.8
# OR
- when: python >=3.8
then: foobar
# OR
- "foobar when python >=3.8"
|
||
However, it would be nice if we could have a flexible, powerful and simple syntax to enable or disable "flags" on packages in order to select a variant. | ||
|
||
A RepodataRecord should get a new field "flags" that is a list of strings, such as: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Flags are part of a variant. So there is no variation of flags for a single variant. E.g. flags could be used to say a particular variant is using cuda and another is used to target cpu. Extras are a way to select additional dependencies for a particular variant. If a variant also adds a CLI tool it provides the extra "cli". Only if that extra is requested by another package are particular dependencies also requested.
In technical terms extras can indeed be implemented as conditional dependencies. E.g. for a package my_package
we could express it as typer when my_package[cli]
. If there is a package that depends on my_package[cli]
typer
would also be required.
|
||
## Conditional dependencies | ||
|
||
Conditional dependencies are activated when the condition is true. The most straight-forward conditions are `__unix`, `__win` and other platform specifiers. However, we would also like to support matchspecs in conditions such as `python >=3`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the PR that implements this for resolvo: prefix-dev/resolvo#136
- pyxpgres >=8 | ||
``` | ||
|
||
When a user, or a dependency, selects an extra through a MatchSpec, the extra and it's dependencies are "activated". This is conceptually the same as having three packages with "exact" dependencies from the "extra" to the base package: `sqlalchemy`, `sqlalchemy-sqlite` and `sqlalchemy-postgres` – which is the workaround currently employed by a number of packages on conda-forge. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should clarify what happens if an extra is requested for a package but the selected variant doesnt provide that extra. E.g. what happens if I depend on a foobar[extras=["doesntexist"]]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I missed it but its also not defined how to depend on an extra?
This defines new repodata with two additional fields and a modfied matchspec syntax:
six; if python <3.8
)Some initial discussion happened over the past days on:
TODO:
~foo
,!foo
, or-foo
.