Versioning
This document describes version literals as used in the conda ecosystem, and their ordering in comparisons.
Specification
Version literals
- Version literals MUST only consist of digits, periods, lowercase ASCII letters, underscores, plus symbols, and exclamation marks. The maximum length of a version string MUST NOT exceed 64 characters.
- Version literals MUST be composed of alphanumeric characters
[A-Za-z0-9], separated into segments by periods.and underscores_. Dashes-are historically allowed and interpreted as underscores, but SHOULD NOT be used because they break filename conventions. - Consecutive runs of digits MUST NOT exceed a value of
2^31-1. - Empty segments (i.e. two consecutive periods, or a period plus an underscore) SHOULD NOT be allowed.
- A single trailing underscore MAY be used exceptionally for comparisons against
openssl 1.x-like version schemes (e.g.1.0.1_ < 1.0.1a). - A single epoch number (a positive integer followed by
!) MAY prefix the rest of the string. - A single local version string MAY be added at the end, separated by a plus symbol
+.
Ordering
Before being compared, version literals MUST be parsed into a list of segments (with each segment being a list of components) as follows:
- They are first split into epoch, main version, and local version at
!and+respectively.- If there is no
!, the epoch is set to0. - If there is no
+, the local version is empty.
- If there is no
- The main version part is then split into components at
.,_, and-.- Each component is split again into consecutive runs of numerals and non-numerals.
- Subcomponents containing only numerals are converted to integers.
- Strings are converted to lowercase, with special treatment for
devandpost. - Trailing underscores are considered part of the preceding string, if any.
- When a component starts with a letter, the fill value
0is inserted before the letter. - Leading zeros in a component are removed.
- The epoch and main version segments are concatenated.
- The same is repeated for the local version part, and stored as a separate list of segments.
For example:
>>> parse("1.2g.beta15.rc")
[[0], [1], [2, 'g'], [0, 'beta', 15], [0, 'rc']], []
>>> parse("1!2.15.1_ALPHA")
[[1], [2], [15], [1], [0, 'alpha']], []
>>> parse("1!2.15.1alpha_")
[[1], [2], [15], [1, 'alpha_']], []
>>> parse("1!2.15.1_alpha+1.2.3h123")
[[1], [2], [15], [1], [0, 'alpha']], [[1], [2], [3, 'h', 123]]
The resulting list of components MUST be compared as follows:
- Integers are compared numerically.
- Strings are compared lexicographically, case-insensitive. The substring
devis 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. A version without a local part is treated as having an implicit local version of 0.
Warning: Pre-releases markers are sensitive to leading zeros and periods. While
"1.1.0" == "1.1.0.0" == "1.1", the rule "When a component starts with a letter, the fill value0is inserted" results in"1.1.0rc" == "1.1.rc" > "1.1rc". See conda#12568.