Skip to content

Conversation

@jaimergp
Copy link
Contributor

@jaimergp jaimergp commented Sep 26, 2025

Checklist for submitter

  • I am submitting a new CEP: Version strings and their ordering.
    • I am using the CEP template by creating a copy cep-0000.md named cep-XXXX.md in the root level.
  • I am submitting modifications to CEP XX.
  • Something else: (add your description here).

@jaimergp jaimergp moved this to In Progress in STA conda & conda-forge Sep 26, 2025
@jaimergp jaimergp self-assigned this Sep 26, 2025
@jaimergp jaimergp changed the title PEP XXXX: "Version strings and their ordering" CEP XXXX: "Version strings and their ordering" Sep 26, 2025
@jaimergp jaimergp linked an issue Sep 26, 2025 that may be closed by this pull request

- Integers are compared numerically.
- Strings are compared lexicographically, case-insensitive. The substring `dev` is always smaller.
- Strings are considered smaller than integers, except for `post`, which is always greater.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that strings and number can never be compared, because of this:

When a component starts with a letter, the fill value 0 is inserted.

So, starting at 0, even indices will always contain a number and odd always a string

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I think you are right here 🤔 I wonder if this bit in the docs didn't get updated once they introduced the "fill value" solution.

Copy link
Contributor Author

@jaimergp jaimergp Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, given this script

from conda.models.version import VersionOrder as V

for version in (
    "0.dev1beta1.b1aa",
    "0.rc1BETA1.b1aa",
    "0.post0beta1.b1aa",
    "0.post1beta1.b1aa",
    "0post1beta1.b1aa",
):
    print(version, "->\n   ", V(version).version)

We get:

0.dev1beta1.b1aa ->
    [[0], [0], [0, 'DEV', 1, 'beta', 1], [0, 'b', 1, 'aa']]
0.rc1BETA1.b1aa ->
    [[0], [0], [0, 'rc', 1, 'beta', 1], [0, 'b', 1, 'aa']]
0.post0beta1.b1aa ->
    [[0], [0], [0, inf, 0, 'beta', 1], [0, 'b', 1, 'aa']]
0.post1beta1.b1aa ->
    [[0], [0], [0, inf, 1, 'beta', 1], [0, 'b', 1, 'aa']]
0post1beta1.b1aa ->  # I really hate this one
    [[0], [0, inf, 1, 'beta', 1], [0, 'b', 1, 'aa']]

See how dev is special cased to uppercase (every other string gets lowercased!), and post gets converted to inf. I guess this is a hack to make it work with Python's default ordering (uppercase sorts before lowercase, and inf is always larger). However, we do see a case here where letters and numbers need to be compared (albeit after internal conversions, so I don't think the rule applies and I guess we can remove it from the spec? But it doesn't hurt to keep it either).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both the DEV and inf here should be considered implementation details.
In mamba this is handled with a custom comparison function for literals (it checks a few special cases and then defers to string comparison). In both C++ (mamba) and Rust (rattler), comparing a number and a string is not the "naive" thing to do.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also surprised about this. Pretty sure we don't have this behaviour in mamba. Should this be part of the spec? what should be the rule?

0post1beta1.b1aa ->  # I really hate this one
    [[0], [0, inf, 1, 'beta', 1], [0, 'b', 1, 'aa']]
     ☝️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That first [0] is the omitted 0! epoch, which is covered in the spec, I think?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha yes sorry, I got confused (the epoch is stored separately in mamba). I thought it was the first segment and that there was some sort of rule that "post" cannot be in the first segment...

- Strings are compared lexicographically, case-insensitive. The substring `dev` is always smaller.
- Strings are considered smaller than integers, except for `post`, which is always greater.
- When a component has no correspondent, the missing component is assumed to be `0`.
- Local versions are only compared when the main versions are identical.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: if we could find some example of how local versions are used out in the wild

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found these in conda-forge/(osx-arm64,noarch):

$ conda search "*[version='*+*']"
Loading channels: done
# Name                       Version           Build  Channel
py-sirius-ms         2.1+sirius6.0.3    pyhd8ed1ab_0  conda-forge
py-sirius-ms         2.1+sirius6.0.4    pyhd8ed1ab_0  conda-forge
py-sirius-ms         2.1+sirius6.0.5    pyhd8ed1ab_0  conda-forge
py-sirius-ms         2.1+sirius6.0.6    pyhd8ed1ab_0  conda-forge
py-sirius-ms         2.1+sirius6.0.7    pyhd8ed1ab_0  conda-forge
py-sirius-ms         2.1+sirius6.0.7    pyhd8ed1ab_1  conda-forge
py-sirius-ms         3.0+sirius6.1.0    pyhd8ed1ab_0  conda-forge
py-sirius-ms         3.0.1+sirius6.1.0    pyhd8ed1ab_0  conda-forge
py-sirius-ms         3.1+sirius6.1.1    pyhd8ed1ab_0  conda-forge
r-sirius-ms          2.1+sirius6.0.4   r44h57928b3_1  conda-forge
r-sirius-ms          2.1+sirius6.0.4   r44h694c41f_1  conda-forge
r-sirius-ms          2.1+sirius6.0.4   r44ha770c72_1  conda-forge
r-sirius-ms          2.1+sirius6.0.5   r44h57928b3_1  conda-forge
r-sirius-ms          2.1+sirius6.0.5   r44h694c41f_1  conda-forge
r-sirius-ms          2.1+sirius6.0.5   r44ha770c72_1  conda-forge
r-sirius-ms          2.1+sirius6.0.6   r44h57928b3_1  conda-forge
r-sirius-ms          2.1+sirius6.0.6   r44h694c41f_1  conda-forge
r-sirius-ms          2.1+sirius6.0.6   r44ha770c72_1  conda-forge
r-sirius-ms          2.1+sirius6.0.7   r44h57928b3_0  conda-forge
r-sirius-ms          2.1+sirius6.0.7   r44h57928b3_1  conda-forge
r-sirius-ms          2.1+sirius6.0.7   r44h694c41f_0  conda-forge
r-sirius-ms          2.1+sirius6.0.7   r44h694c41f_1  conda-forge
r-sirius-ms          2.1+sirius6.0.7   r44ha770c72_0  conda-forge
r-sirius-ms          2.1+sirius6.0.7   r44ha770c72_1  conda-forge
r-sirius-ms          3.0.1+sirius6.1.0   r44h57928b3_0  conda-forge
r-sirius-ms          3.0.1+sirius6.1.0   r44h694c41f_0  conda-forge
r-sirius-ms          3.0.1+sirius6.1.0   r44ha770c72_0  conda-forge
r-sirius-ms          3.1+sirius6.1.1   r44h57928b3_0  conda-forge
r-sirius-ms          3.1+sirius6.1.1   r44h694c41f_0  conda-forge
r-sirius-ms          3.1+sirius6.1.1   r44ha770c72_0  conda-forge
r-sirius-ms          3.1+sirius6.1.1   r45h57928b3_1  conda-forge
r-sirius-ms          3.1+sirius6.1.1   r45h694c41f_1  conda-forge
r-sirius-ms          3.1+sirius6.1.1   r45ha770c72_1  conda-forge
typst-test           0.0.0.post105+699b871      h6e96688_0  conda-forge
typst-test           0.0.0.post105+699b871      h6e96688_1  conda-forge
typst-test           0.0.0.post106+2b4e689      h6e96688_0  conda-forge

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added it in a new Examples section.


## Examples

The ordering specification results in the following versions sorted in this way:
Copy link

@h-vetinari h-vetinari Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use a comment/example of how non-empty local versions are always ordered after (right?!) the ones with empty local versions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CEP: Version strings

4 participants