Fix for handling of version ranges - #645
Conversation
don't include first prerelease in the allowed max for an exclusive range.
|
Kudos, SonarCloud Quality Gate passed! |
|
I now think that the real problem - or anyway among the real problems - is ambiguity in poetry-core's treatment of the max in a strict inequality range.
A possible approach is to handle this at parsing and abandon This also solves those issues reported in poetry - but causes carnage in the test scripts: the main (but probably not only) problem being that parsing and then printing |
If we went down this path, maybe we should also change |
|
that seems plausible and I have a branch in which I tried it. Among the chaos in the unit tests I see at least these wrinkles
probably there's more that I haven't yet spotted This feels like the right direction conceptually but I've no idea when or whether I'm likely to find the time or energy to do anything about it |
Pre-releases must be excluded by |
|
You are mistaken, but thanks anyway https://peps.python.org/pep-0440/#summary-of-permitted-suffixes-and-relative-ordering |
|
Ah, I see. Apologies for wasting your time. In hindsight everything makes much more sense now. I interpreted this part of the PEP incorrectly, "Developmental releases are ordered by their numerical component, immediately before the corresponding release (and before any pre-releases with the same release segment), and following any previous release (including any post-releases)." I had incorrectly taken the parenthetical parts to be solely about how developmental releases for pre-/post-releases are ordered before their respective pre-/post-release. And then I somehow managed to repeatedly miss the full example list at the end of the section you linked. |
After parser-only canonicalization of `<V`, an interior split such as
`>=1,<10` minus `{2}` produces a union of two raw ranges
`>=1,<2 || >2,<10` whose seam at 2 excludes only the single point V=2
(both bounds raw exclusive). Re-parsing the naive `||` rendering
re-canonicalizes the inner `<2` to `<2.dev0`, silently shrinking the
allowed set -- breaking lockfile round-trips (cf. PR python-poetry#645 discussion).
Detect single-point puncture seams (`ranges[i].max == ranges[i+1].min`)
and group consecutive punctures into a single `>=A,!=V1,!=V2,<B`
rendering. Mixed unions (some seams puncture, others gap) partition
naturally into groups joined by `||`.
This also subsumes the previous `<V || >V` -> `!=V` collapse, which is
itself just the singleton-group case.
Also fix RUF002 (ambiguous minus sign in parser docstring) and
RUF007 (`zip(xs, xs[1:])` -> `itertools.pairwise`) flagged by ruff.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`python_versions` (on Dependency, Package, ProjectPackage) is implemented as a PEP 508 `python_version` marker. Marker comparisons treat versions as concrete environment values rather than as PEP 440 ordered comparisons, so the canonicalization of `<V` -> `<V.dev0` introduced for *requirement* parsing must not apply here -- otherwise e.g. `python = "<3.7"` no longer admits a 3.7.dev environment, with follow-on solver fallout (cf. python-poetry#645). Route every `python_versions` parse through `parse_marker_version_constraint`, which already exists for exactly this purpose (and which is also why marker parsing has its own `pep440=False` switch for non-PEP-440 fields like `platform_release`). Update one factory test that was asserting parse-equality with the canonical (requirement) parser. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ons (python-poetry#939) After parser-only canonicalization of `<V`, an interior split such as `>=1,<10` minus `{2}` produces a union of two raw ranges `>=1,<2 || >2,<10` whose seam at 2 excludes only the single point V=2 (both bounds raw exclusive). Re-parsing the naive `||` rendering re-canonicalizes the inner `<2` to `<2.dev0`, silently shrinking the allowed set -- breaking lockfile round-trips (cf. PR python-poetry#645 discussion). Detect single-point puncture seams (`ranges[i].max == ranges[i+1].min`) and group consecutive punctures into a single `>=A,!=V1,!=V2,<B` rendering. Mixed unions (some seams puncture, others gap) partition naturally into groups joined by `||`. This also subsumes the previous `<V || >V` -> `!=V` collapse, which is itself just the singleton-group case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`python_versions` (on Dependency, Package, ProjectPackage) is implemented as a PEP 508 `python_version` marker. Marker comparisons treat versions as concrete environment values rather than as PEP 440 ordered comparisons, so the canonicalization of `<V` -> `<V.dev0` introduced for *requirement* parsing must not apply here -- otherwise e.g. `python = "<3.7"` no longer admits a 3.7.dev environment, with follow-on solver fallout (cf. python-poetry#645). Route every `python_versions` parse through `parse_marker_version_constraint`, which already exists for exactly this purpose (and which is also why marker parsing has its own `pep440=False` switch for non-PEP-440 fields like `platform_release`). Update one factory test that was asserting parse-equality with the canonical (requirement) parser. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ons (#939) After parser-only canonicalization of `<V`, an interior split such as `>=1,<10` minus `{2}` produces a union of two raw ranges `>=1,<2 || >2,<10` whose seam at 2 excludes only the single point V=2 (both bounds raw exclusive). Re-parsing the naive `||` rendering re-canonicalizes the inner `<2` to `<2.dev0`, silently shrinking the allowed set -- breaking lockfile round-trips (cf. PR #645 discussion). Detect single-point puncture seams (`ranges[i].max == ranges[i+1].min`) and group consecutive punctures into a single `>=A,!=V1,!=V2,<B` rendering. Mixed unions (some seams puncture, others gap) partition naturally into groups joined by `||`. This also subsumes the previous `<V || >V` -> `!=V` collapse, which is itself just the singleton-group case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`python_versions` (on Dependency, Package, ProjectPackage) is implemented as a PEP 508 `python_version` marker. Marker comparisons treat versions as concrete environment values rather than as PEP 440 ordered comparisons, so the canonicalization of `<V` -> `<V.dev0` introduced for *requirement* parsing must not apply here -- otherwise e.g. `python = "<3.7"` no longer admits a 3.7.dev environment, with follow-on solver fallout (cf. #645). Route every `python_versions` parse through `parse_marker_version_constraint`, which already exists for exactly this purpose (and which is also why marker parsing has its own `pep440=False` switch for non-PEP-440 fields like `platform_release`). Update one factory test that was asserting parse-equality with the canonical (requirement) parser. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Superseded by #939 |








don't include first prerelease in the allowed max for an exclusive range.
Fixes python-poetry/poetry#8475, python-poetry/poetry#8405, python-poetry/poetry#8202
(actually 8202 seems to have been resolved by changes in the available dependencies on pypi and so I can't reproduce it now: but I'm pretty sure these will all be the same)
Absurdly I can't find a simple testcase that takes two versions or version ranges and compares or intersects them or something to demonstrate the point. So I confess that I don't fully understand what was going wrong before this fix.
However I do understand that those issues are
=2.*) which poetry interprets as having a lower bound that is the first dev-release (eg2.dev0)<2so I've made this fix somewhat on gut:
<2certainly shouldn't allow2.dev0and so it's weird thatallowed_maxwould return that value.What can I say? I'm confused, but this looks like a good fix and it does resolve those issues...