From c892051aeffcf3eb36549ef6091120142a64987a Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Mon, 19 Aug 2024 17:47:06 -0500 Subject: [PATCH] Move pip compatibility guide to the documentation (#6213) First, I synced the documents manually following the output of `diff` then replaced the old one with a link. --- PIP_COMPATIBILITY.md | 459 +------------------------------------- docs/pip/compatibility.md | 60 ++++- 2 files changed, 58 insertions(+), 461 deletions(-) diff --git a/PIP_COMPATIBILITY.md b/PIP_COMPATIBILITY.md index fea23ff74..559b463df 100644 --- a/PIP_COMPATIBILITY.md +++ b/PIP_COMPATIBILITY.md @@ -1,457 +1,2 @@ -# Compatibility with `pip` and `pip-tools` - -uv is designed as a drop-in replacement for common `pip` and `pip-tools` workflows. - -Informally, the intent is such that existing `pip` and `pip-tools` users can switch to uv without -making meaningful changes to their packaging workflows; and, in most cases, swapping out -`pip install` for `uv pip install` should "just work". - -However, uv is _not_ intended to be an _exact_ clone of `pip`, and the further you stray from common -`pip` workflows, the more likely you are to encounter differences in behavior. In some cases, those -differences may be known and intentional; in others, they may be the result of implementation -details; and in others, they may be bugs. - -This document outlines the known differences between uv and `pip`, along with rationale, -workarounds, and a statement of intent for compatibility in the future. - -## Configuration files and environment variables - -uv does not read configuration files or environment variables that are specific to `pip`, like -`pip.conf` or `PIP_INDEX_URL`. - -Reading configuration files and environment variables intended for other tools has a number of -drawbacks: - -1. It requires bug-for-bug compatibility with the target tool, since users end up relying on bugs in - the format, the parser, etc. -2. If the target tool _changes_ the format in some way, uv is then locked-in to changing it in - equivalent ways. -3. If that configuration is versioned in some way, uv would need to know _which version_ of the - target tool the user is expecting to use. -4. It prevents uv from introducing any settings or configuration that don't exist in the target - tool, since otherwise `pip.conf` (or similar) would no longer be usable with `pip`. -5. It can lead to user confusion, since uv would be reading settings that don't actually affect its - behavior, and many users may _not_ expect uv to read configuration files intended for other - tools. - -Instead, uv supports its own environment variables, like `UV_INDEX_URL`. In the future, uv will also -support persistent configuration in its own configuration file format (e.g., `pyproject.toml` or -`uv.toml` or similar). For more, see [#651](https://github.com/astral-sh/uv/issues/651). - -## Pre-release compatibility - -By default, uv will accept pre-release versions during dependency resolution in two cases: - -1. If the package is a direct dependency, and its version markers include a pre-release specifier - (e.g., `flask>=2.0.0rc1`). -1. If _all_ published versions of a package are pre-releases. - -If dependency resolution fails due to a transitive pre-release, uv will prompt the user to re-run -with `--prerelease=allow`, to allow pre-releases for all dependencies. - -Alternatively, you can add the transitive dependency to your `requirements.in` file with pre-release -specifier (e.g., `flask>=2.0.0rc1`) to opt in to pre-release support for that specific dependency. - -In sum, uv needs to know upfront whether the resolver should accept pre-releases for a given -package. `pip`, meanwhile, _may_ respect pre-release identifiers in transitive dependencies -depending on the order in which the resolver encounters the relevant specifiers -([#1641](https://github.com/astral-sh/uv/issues/1641#issuecomment-1981402429)). - -Pre-releases are -[notoriously difficult](https://pubgrub-rs-guide.netlify.app/limitations/prerelease_versions) to -model, and are a frequent source of bugs in packaging tools. Even `pip`, which is viewed as a -reference implementation, has a number of open questions around pre-release handling -([#12469](https://github.com/pypa/pip/issues/12469), -[#12470](https://github.com/pypa/pip/issues/12470), -[#40505](https://discuss.python.org/t/handling-of-pre-releases-when-backtracking/40505/20), etc.). -uv's pre-release handling is _intentionally_ limited and _intentionally_ requires user opt-in for -pre-releases, to ensure correctness. - -In the future, uv _may_ support pre-release identifiers in transitive dependencies. However, it's -likely contingent on evolution in the Python packaging specifications. The existing PEPs -[do not cover "dependency resolution"](https://discuss.python.org/t/handling-of-pre-releases-when-backtracking/40505/17) -and are instead focused on behavior for a _single_ version specifier. As such, there are unresolved -questions around the correct and intended behavior for pre-releases in the packaging ecosystem more -broadly. - -## Local version identifiers - -uv does not implement spec-compliant handling of local version identifiers (e.g., `1.2.3+local`). -This is considered a known limitation. Although local version identifiers are rare in published -packages (and, e.g., disallowed on PyPI), they're common in the PyTorch ecosystem, and uv's approach -to local versions _does_ support typical PyTorch workflows to succeed out-of-the-box. - -[PEP 440](https://peps.python.org/pep-0440/#version-specifiers) specifies that the local version -segment should typically be ignored when evaluating version specifiers, with a few exceptions. For -example, `foo==1.2.3` should accept `1.2.3+local`, but `foo==1.2.3+local` should _not_ accept -`1.2.3`. These asymmetries are hard to model in a resolution algorithm. As such, uv treats `1.2.3` -and `1.2.3+local` as entirely separate versions, but respects local versions provided as direct -dependencies throughout the resolution, such that if you provide `foo==1.2.3+local` as a direct -dependency, `1.2.3+local` _will_ be accepted for any transitive dependencies that request -`foo==1.2.3`. - -To take an example from the PyTorch ecosystem, it's common to specify `torch==2.0.0+cu118` and -`torchvision==0.15.1+cu118` as direct dependencies. `torchvision @ 0.15.1+cu118` declares a -dependency on `torch==2.0.0`. In this case, uv would recognize that `torch==2.0.0+cu118` satisfies -the specifier, since it was provided as a direct dependency. - -As compared to pip, the main differences in observed behavior are as follows: - -- In general, local versions must be provided as direct dependencies. Resolution may succeed for - transitive dependencies that request a non-local version, but this is not guaranteed. -- If _only_ local versions exist for a package `foo` at a given version (e.g., `1.2.3+local` exists, - but `1.2.3` does not), `uv pip install foo==1.2.3` will fail, while `pip install foo==1.2.3` will - resolve to an arbitrary local version. - -## Packages that exist on multiple indexes - -In both uv and `pip`, users can specify multiple package indexes from which to search for the -available versions of a given package. However, uv and `pip` differ in how they handle packages that -exist on multiple indexes. - -For example, imagine that a company publishes an internal version of `requests` on a private index -(`--extra-index-url`), but also allows installing packages from PyPI by default. In this case, the -private `requests` would conflict with the public [`requests`](https://pypi.org/project/requests/) -on PyPI. - -When uv searches for a package across multiple indexes, it will iterate over the indexes in order -(preferring the `--extra-index-url` over the default index), and stop searching as soon as it finds -a match. This means that if a package exists on multiple indexes, uv will limit its candidate -versions to those present in the first index that contains the package. - -`pip`, meanwhile, will combine the candidate versions from all indexes, and select the best version -from the combined set, though it makes -[no guarantees around the order](https://github.com/pypa/pip/issues/5045#issuecomment-369521345) in -which it searches indexes, and expects that packages are unique up to name and version, even across -indexes. - -uv's behavior is such that if a package exists on an internal index, it should always be installed -from the internal index, and never from PyPI. The intent is to prevent "dependency confusion" -attacks, in which an attacker publishes a malicious package on PyPI with the same name as an -internal package, thus causing the malicious package to be installed instead of the internal -package. See, for example, -[the `torchtriton` attack](https://pytorch.org/blog/compromised-nightly-dependency/) from -December 2022. - -As of v0.1.39, users can opt in to `pip`-style behavior for multiple indexes via the -`--index-strategy` command-line option, or the `UV_INDEX_STRATEGY` environment variable, which -supports the following values: - -- `first-match` (default): Search for each package across all indexes, limiting the candidate - versions to those present in the first index that contains the package, prioritizing the - `--extra-index-url` indexes over the default index URL. -- `unsafe-first-match`: Search for each package across all indexes, but prefer the first index with - a compatible version, even if newer versions are available on other indexes. -- `unsafe-best-match`: Search for each package across all indexes, and select the best version from - the combined set of candidate versions. - -While `unsafe-best-match` is the closest to `pip`'s behavior, it exposes users to the risk of -"dependency confusion" attacks. - -In the future, uv will support pinning packages to dedicated indexes (see: -[#171](https://github.com/astral-sh/uv/issues/171)). Additionally, -[PEP 708](https://peps.python.org/pep-0708/) is a provisional standard that aims to address the -"dependency confusion" issue across package registries and installers. - -## PEP 517 build isolation - -uv uses [PEP 517](https://peps.python.org/pep-0517/) build isolation by default (akin to -`pip install --use-pep517`), following `pypa/build` and in anticipation of `pip` defaulting to PEP -517 builds in the future ([pypa/pip#9175](https://github.com/pypa/pip/issues/9175)). - -If a package fails to install due to a missing build-time dependency, try using a newer version of -the package; if the problem persists, consider filing an issue with the package maintainer, -requesting that they update the packaging setup to declare the correct PEP 517 build-time -dependencies. - -As an escape hatch, you can preinstall a package's build dependencies, then run `uv pip install` -with `--no-build-isolation`, as in: - -```shell -uv pip install wheel && uv pip install --no-build-isolation biopython==1.77 -``` - -For a list of packages that are known to fail under PEP 517 build isolation, see -[#2252](https://github.com/astral-sh/uv/issues/2252). - -## Transitive direct URL dependencies for constraints and overrides - -While uv does support URL dependencies (e.g., `black @ https://...`), it does not support -_transitive_ (i.e., "nested") direct URL dependencies for constraints and overrides. - -Specifically, if a constraint or override is defined using a direct URL dependency, and the -constrained package has a direct URL dependency of its own, uv _may_ reject that transitive direct -URL dependency during resolution. - -uv also makes the assumption that non-URL dependencies won't introduce URL dependencies (i.e., that -dependencies fetched from a registry will not themselves have direct URL dependencies). If a non-URL -dependency _does_ introduce a URL dependency, uv will reject the URL dependency during resolution. - -If uv rejects a transitive URL dependency in either case, the best course of action is to provide -the URL dependency as a direct dependency in the `requirements.in` file, rather than as a -constraint, override, or transitive dependency. - -## Virtual environments by default - -`uv pip install` and `uv pip sync` are designed to work with virtual environments by default. - -Specifically, uv will always install packages into the currently active virtual environment, or -search for a virtual environment named `.venv` in the current directory or any parent directory -(even if it is not activated). - -This differs from `pip`, which will install packages into a global environment if no virtual -environment is active, and will not search for inactive virtual environments. - -In uv, you can install into non-virtual environments by providing a path to a Python executable via -the `--python /path/to/python` option, or via the `--system` flag, which installs into the first -Python interpreter found on the `PATH`, like `pip`. - -In other words, uv inverts the default, requiring explicit opt-in to installing into the system -Python, which can lead to breakages and other complications, and should only be done in limited -circumstances. - -For more, see -["Installing into arbitrary Python environments"](./README.md#installing-into-arbitrary-python-environments). - -## Resolution strategy - -For a given set of dependency specifiers, it's often the case that there is no single "correct" set -of packages to install. Instead, there are many valid sets of packages that satisfy the specifiers. - -Neither `pip` nor uv make any guarantees about the _exact_ set of packages that will be installed; -only that the resolution will be consistent, deterministic, and compliant with the specifiers. As -such, in some cases, `pip` and uv will yield different resolutions; however, both resolutions -_should_ be equally valid. - -For example, consider: - -```text title="requirements.txt" -starlette -fastapi -``` - -At time of writing, the most recent `starlette` version is `0.37.2`, and the most recent `fastapi` -version is `0.110.0`. However, `fastapi==0.110.0` also depends on `starlette`, and introduces an -upper bound: `starlette>=0.36.3,<0.37.0`. - -If a resolver prioritizes including the most recent version of `starlette`, it would need to use an -older version of `fastapi` that excludes the upper bound on `starlette`. In practice, this requires -falling back to `fastapi==0.1.17`: - -```text -# This file was autogenerated by uv via the following command: -# uv pip compile - -annotated-types==0.6.0 - # via pydantic -anyio==4.3.0 - # via starlette -fastapi==0.1.17 -idna==3.6 - # via anyio -pydantic==2.6.3 - # via fastapi -pydantic-core==2.16.3 - # via pydantic -sniffio==1.3.1 - # via anyio -starlette==0.37.2 - # via fastapi -typing-extensions==4.10.0 - # via - # pydantic - # pydantic-core -``` - -Alternatively, if a resolver prioritizes including the most recent version of `fastapi`, it would -need to use an older version of `starlette` that satisfies the upper bound. In practice, this -requires falling back to `starlette==0.36.3`: - -```text -# uv pip compile - -annotated-types==0.6.0 - # via pydantic -anyio==4.3.0 - # via starlette -fastapi==0.110.0 -idna==3.6 - # via anyio -pydantic==2.6.3 - # via fastapi -pydantic-core==2.16.3 - # via pydantic -sniffio==1.3.1 - # via anyio -starlette==0.36.3 - # via fastapi -typing-extensions==4.10.0 - # via - # fastapi - # pydantic - # pydantic-core -``` - -When uv resolutions differ from `pip` in undesirable ways, it's often a sign that the specifiers are -too loose, and that the user should consider tightening them. For example, in the case of -`starlette` and `fastapi`, the user could require `fastapi>=0.110.0`. - -## `pip check` - -At present, `uv pip check` will surface the following diagnostics: - -- A package has no `METADATA` file, or the `METADATA` file can't be parsed. -- A package has a `Requires-Python` that doesn't match the Python version of the running - interpreter. -- A package has a dependency on a package that isn't installed. -- A package has a dependency on a package that's installed, but at an incompatible version. -- Multiple versions of a package are installed in the virtual environment. - -In some cases, `uv pip check` will surface diagnostics that `pip check` does not, and vice versa. -For example, unlike `uv pip check`, `pip check` will _not_ warn when multiple versions of a package -are installed in the current environment. - -## `--user` and the `user` install scheme - -uv does not support the `--user` flag, which installs packages based on the `user` install scheme. -Instead, we recommend the use of virtual environments to isolate package installations. - -Additionally, pip will fall back to the `user` install scheme if it detects that the user does not -have write permissions to the target directory, as is the case on some systems when installing into -the system Python. uv does not implement any such fallback. - -For more, see [#2077](https://github.com/astral-sh/uv/issues/2077). - -## `--only-binary` enforcement - -The `--only-binary` argument is used to restrict installation to pre-built binary distributions. -When `--only-binary :all:` is provided, both pip and uv will refuse to build source distributions -from PyPI and other registries. - -However, when a dependency is provided as a direct URL (e.g., `uv pip install https://...`), pip -does _not_ enforce `--only-binary`, and will build source distributions for all such packages. - -uv, meanwhile, _does_ enforce `--only-binary` for direct URL dependencies, with one exception: given -`uv pip install https://... --only-binary flask`, uv _will_ build the source distribution at the -given URL if it cannot infer the package name ahead of time, since uv can't determine whether the -package is "allowed" in such cases without building its metadata. - -Both pip and uv allow editables requirements to be built and installed even when `--only-binary` is -provided. For example, `uv pip install -e . --only-binary :all:` is allowed. - -## `--no-binary` enforcement - -The `--no-binary` argument is used to restrict installation to source distributions. When -`--no-binary` is provided, uv will refuse to install pre-built binary distributions, but _will_ -reuse any binary distributions that are already present in the local cache. - -Additionally, and in contrast to pip, uv's resolver will still read metadata from pre-built binary -distributions when `--no-binary` is provided. - -## Bytecode compilation - -Unlike pip, uv does not compile `.py` files to `.pyc` files during installation by default (i.e., uv -does not create or populate `__pycache__` directories). To enable bytecode compilation during -installs, pass the `--compile-bytecode` flag to `uv pip install` or `uv pip sync`. - -## Strictness and spec enforcement - -uv tends to be stricter than `pip`, and will often reject packages that `pip` would install. For -example, uv omits packages with invalid version specifiers in its metadata, which `pip` similarly -plans to exclude in a [future release](https://github.com/pypa/pip/issues/12063). - -In some cases, uv implements lenient behavior for popular packages that are known to have specific -spec compliance issues. - -If uv rejects a package that `pip` would install due to a spec violation, the best course of action -is to first attempt to install a newer version of the package; and, if that fails, to report the -issue to the package maintainer. - -## `pip` command-line options and subcommands - -uv does not support the complete set of `pip`'s command-line options and subcommands, although it -does support a large subset. - -Missing options and subcommands are prioritized based on user demand and the complexity of the -implementation, and tend to be tracked in individual issues. For example: - -- [`--trusted-host`](https://github.com/astral-sh/uv/issues/1339) -- [`--user`](https://github.com/astral-sh/uv/issues/2077) - -If you encounter a missing option or subcommand, please search the issue tracker to see if it has -already been reported, and if not, consider opening a new issue. Feel free to upvote any existing -issues to convey your interest. - -## Registry authentication - -uv does not support `pip`'s `auto` or `import` options for `--keyring-provider`. At present, only -the `subproces` option is supported. - -Unlike `pip`, uv does not enable keyring authentication by default. - -Unlike `pip`, uv does not wait until a request returns a HTTP 401 before searching for -authentication. uv attaches authentication to all requests for hosts with credentials available. - -## `egg` support - -uv does not support features that are considered legacy or deprecated in `pip`. For example, uv does -not support `.egg`-style distributions. - -However, uv does have partial support for (1) `.egg-info`-style distributions (which are -occasionally found in Docker images and Conda environments) and (2) legacy editable -`.egg-link`-style distributions. - -Specifically, uv does not support installing new `.egg-info`- or `.egg-link`-style distributions, -but will respect any such existing distributions during resolution, list them with `uv pip list` and -`uv pip freeze`, and uninstall them with `uv pip uninstall`. - -## Build constraints - -When constraints are provided via `--constraint` (or `UV_CONSTRAINT`), uv will _not_ apply the -constraints when resolving build dependencies (i.e., to build a source distribution). Instead, build -constraints should be provided via the dedicated `--build-constraint` (or `UV_BUILD_CONSTRAINT`) -setting. - -pip, meanwhile, applies constraints to build dependencies when specified via `PIP_CONSTRAINT`, but -not when provided via `--constraint` on the command line. - -For example, to ensure that `setuptools 60.0.0` is used to build any packages with a build -dependency on `setuptools`, use `--build-constraint`, rather than `--constraint`. - -## `pip compile` defaults - -There are a few small but notable differences in the default behaviors of `pip compile` and -`pip-tools`. - -By default, uv does not write the compiled requirements to an output file. Instead, uv requires that -the user specify an output file explicitly with the `-o` or `--output-file` option. - -By default, uv strips extras when outputting the compiled requirements. In other words, uv defaults -to `--strip-extras`, while `pip-compile` defaults to `--no-strip-extras`. `pip-compile` is scheduled -to change this default in the next major release (v8.0.0), at which point both tools will default to -`--strip-extras`. To retain extras with uv, pass the `--no-strip-extras` flag to `uv pip compile`. - -By default, uv does not write any index URLs to the output file, while `pip-compile` outputs any -`--index-url` or `--extra-index-url` that does not match the default (PyPI). To include index URLs -in the output file, pass the `--emit-index-url` flag to `uv pip compile`. Unlike `pip-compile`, uv -will include all index URLs when `--emit-index-url` is passed, including the default index URL. - -## `requires-python` enforcement - -When evaluating Python versions against `requires-python` specifiers, uv truncates the candidate -version to the major, minor, and patch components, ignoring (e.g.) pre-release and post-release -identifiers. - -For example, a project that declares `requires-python: >=3.13` will accept Python 3.13.0b1. While -3.13.0b1 is not strictly greater than 3.13, it is greater than 3.13 when the pre-release identifier -is omitted. - -While this is not strictly compliant with [PEP 440](https://peps.python.org/pep-0440/), it _is_ -consistent with -[pip](https://github.com/pypa/pip/blob/24.1.1/src/pip/_internal/resolution/resolvelib/candidates.py#L540). - -## Package priority - -There are usually many possible solutions given a set of requirements — a resolver must choose -between the solutions. Unlike pip, uv's resolver uses the ordering provided of packages to determine -the default priority. This means that uv's resolution can differ based on the order of the packages. -For example, `uv pip install foo bar` would prioritize a newer version of `foo` over `bar`. -Similarly, this applies to the ordering of requirements in input files to `uv pip compile`. +This reference document has moved to the +[documentation website](https://docs.astral.sh/uv/pip/compatibility/). diff --git a/docs/pip/compatibility.md b/docs/pip/compatibility.md index e3747c04f..5e77c35ff 100644 --- a/docs/pip/compatibility.md +++ b/docs/pip/compatibility.md @@ -153,6 +153,27 @@ In the future, uv will support pinning packages to dedicated indexes (see: [PEP 708](https://peps.python.org/pep-0708/) is a provisional standard that aims to address the "dependency confusion" issue across package registries and installers. +## PEP 517 build isolation + +uv uses [PEP 517](https://peps.python.org/pep-0517/) build isolation by default (akin to +`pip install --use-pep517`), following `pypa/build` and in anticipation of `pip` defaulting to PEP +517 builds in the future ([pypa/pip#9175](https://github.com/pypa/pip/issues/9175)). + +If a package fails to install due to a missing build-time dependency, try using a newer version of +the package; if the problem persists, consider filing an issue with the package maintainer, +requesting that they update the packaging setup to declare the correct PEP 517 build-time +dependencies. + +As an escape hatch, you can preinstall a package's build dependencies, then run `uv pip install` +with `--no-build-isolation`, as in: + +```shell +uv pip install wheel && uv pip install --no-build-isolation biopython==1.77 +``` + +For a list of packages that are known to fail under PEP 517 build isolation, see +[#2252](https://github.com/astral-sh/uv/issues/2252). + ## Transitive direct URL dependencies for constraints and overrides While uv does support URL dependencies (e.g., `black @ https://...`), it does not support @@ -316,6 +337,15 @@ package is "allowed" in such cases without building its metadata. Both pip and uv allow editables requirements to be built and installed even when `--only-binary` is provided. For example, `uv pip install -e . --only-binary :all:` is allowed. +## `--no-binary` enforcement + +The `--no-binary` argument is used to restrict installation to source distributions. When +`--no-binary` is provided, uv will refuse to install pre-built binary distributions, but _will_ +reuse any binary distributions that are already present in the local cache. + +Additionally, and in contrast to pip, uv's resolver will still read metadata from pre-built binary +distributions when `--no-binary` is provided. + ## Bytecode compilation Unlike pip, uv does not compile `.py` files to `.pyc` files during installation by default (i.e., uv @@ -343,7 +373,6 @@ does support a large subset. Missing options and subcommands are prioritized based on user demand and the complexity of the implementation, and tend to be tracked in individual issues. For example: -- [`--prefix`](https://github.com/astral-sh/uv/issues/3076) - [`--trusted-host`](https://github.com/astral-sh/uv/issues/1339) - [`--user`](https://github.com/astral-sh/uv/issues/2077) @@ -374,6 +403,19 @@ Specifically, uv does not support installing new `.egg-info`- or `.egg-link`-sty but will respect any such existing distributions during resolution, list them with `uv pip list` and `uv pip freeze`, and uninstall them with `uv pip uninstall`. +## Build constraints + +When constraints are provided via `--constraint` (or `UV_CONSTRAINT`), uv will _not_ apply the +constraints when resolving build dependencies (i.e., to build a source distribution). Instead, build +constraints should be provided via the dedicated `--build-constraint` (or `UV_BUILD_CONSTRAINT`) +setting. + +pip, meanwhile, applies constraints to build dependencies when specified via `PIP_CONSTRAINT`, but +not when provided via `--constraint` on the command line. + +For example, to ensure that `setuptools 60.0.0` is used to build any packages with a build +dependency on `setuptools`, use `--build-constraint`, rather than `--constraint`. + ## `pip compile` defaults There are a few small but notable differences in the default behaviors of `pip compile` and @@ -392,9 +434,19 @@ By default, uv does not write any index URLs to the output file, while `pip-comp in the output file, pass the `--emit-index-url` flag to `uv pip compile`. Unlike `pip-compile`, uv will include all index URLs when `--emit-index-url` is passed, including the default index URL. -By default, uv does not write any `--no-build` or `--only-binary` options to the output file, unlike -`pip-compile`. To include these options in the output file, pass the `--emit-build-options` flag to -`uv pip compile`. +## `requires-python` enforcement + +When evaluating Python versions against `requires-python` specifiers, uv truncates the candidate +version to the major, minor, and patch components, ignoring (e.g.) pre-release and post-release +identifiers. + +For example, a project that declares `requires-python: >=3.13` will accept Python 3.13.0b1. While +3.13.0b1 is not strictly greater than 3.13, it is greater than 3.13 when the pre-release identifier +is omitted. + +While this is not strictly compliant with [PEP 440](https://peps.python.org/pep-0440/), it _is_ +consistent with +[pip](https://github.com/pypa/pip/blob/24.1.1/src/pip/_internal/resolution/resolvelib/candidates.py#L540). ## Package priority