When an unnormalized extra is requested, try to look up dependencies
with both its raw and normalized forms, to maintain compatibility when
an extra is both specified and requested in a non-standard form.
All extras from user input or dependant package metadata are properly
normalized for comparison and resolution. This ensures requests for
extras from a dependant can always correctly find the normalized extra
in the dependency, even if the requested extra name is not normalized.
Note that this still relies on the declaration of extra names in the
dependency's package metadata to be properly normalized when the package
is built, since correct comparison between an extra name's normalized
and non-normalized forms requires change to the metadata parsing logic,
which is only available in packaging 22.0 and up, which pip does not use
at the moment.
A new enum class is implemented for the link evaluator to use instead
of a simple boolean to better distinguish between various evaluation
errors. This allows the caller to better distinguish error sources with
a structured check instead of fragile error string comparison.
This behaviour is more forgiving when a source distribution cannot be
installed (eg: due to missing build dependencies or platform
incompatibility) and favours early eager failures instead of trying to
ensure that a package is installed regardless of the amount of effort it
takes.
This always prepares each explicit requirements when the resolver is
run, instead of when they are being resolved. But we always want to
prepare them anyway (an explicit requirement has only one candidate, so
it's either do or die), so this is not really a problem.
The resolver collects previously known incompatibilites and sends them
to the provider. But previously the provider does not correctly exclude
the currently-installed candidate if it is present in that
incompatibility list, causing the resolver to enter a loop trying that
same candidate. This patch correctly applies incompat_ids when producing
an AlreadyInstalledCandidate and exclude it if its id() is in the set.
When a requirement is requested multiple times, some via a direct URL
("req @ URL") and some not but with extras ("req[extra] VERSION"), the
resolver previous could not correctly find "req[extra]" if "req" is
available in an index.
This additional logic makes the resolver, when encountering a
requirement with identifier "req[extra]", to also look for explicit
candidates listed under "req", and add them as found matches for
"req[extra]".