Repository: pdm-project/pdm

Issue:
PDM 1.15.4+ unable to update lockfile when package with extras is pinned but extras are not pinned
## Summary

When using 1.15.4 we have seen some projects with large lists of dependencies unable to lock, and PDM eventually quits when it hits the max number of rounds. I think it's due to PDM's algorithm for handling extra dependencies. See the following comment for details.

#### Background
~PDM is unable to find dependency resolution and update the lockfile when run from a docker container in CI, eventually the job times out after 1 hour. When run on a workstation the problem doesn't occur, and the lockfile is able to be successfully updated.~
 
_edit: I thought the issue was the CI environment but it was because our developers were using v1.15.3 on their workstations._

A) One project A, I was unable to figure out why the lock was unsuccessful but when I updated to PDM v2.0.0a it was able to successfully lock both locally and in CI.... 🤔 

B) On project B, a google-cloud-functions project I was able to investigate more, but I'm still confused and think the behavior is a bug.

report about project B continued in below

Discussion:
codecov-commenter: # [Codecov](https://codecov.io/gh/pdm-project/pdm/pull/1187?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) Report
> Merging [#1187](https://codecov.io/gh/pdm-project/pdm/pull/1187?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) (d6ce6b9) into [main](https://codecov.io/gh/pdm-project/pdm/commit/96710e62b63973dfec467c80f07d04fc51074c59?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) (96710e6) will **increase** coverage by `0.19%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main    #1187      +/-   ##
==========================================
+ Coverage   85.24%   85.43%   +0.19%     
==========================================
  Files          78       78              
  Lines        6831     6831              
  Branches     1564     1564              
==========================================
+ Hits         5823     5836      +13     
+ Misses        671      661      -10     
+ Partials      337      334       -3     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `85.21% <100.00%> (+0.19%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/pdm-project/pdm/pull/1187?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project) | Coverage Δ | |
|---|---|---|
| [pdm/models/repositories.py](https://codecov.io/gh/pdm-project/pdm/pull/1187/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project#diff-cGRtL21vZGVscy9yZXBvc2l0b3JpZXMucHk=) | `78.53% <100.00%> (ø)` | |
| [pdm/models/candidates.py](https://codecov.io/gh/pdm-project/pdm/pull/1187/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project#diff-cGRtL21vZGVscy9jYW5kaWRhdGVzLnB5) | `88.19% <0.00%> (+0.69%)` | :arrow_up: |
| [pdm/models/caches.py](https://codecov.io/gh/pdm-project/pdm/pull/1187/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project#diff-cGRtL21vZGVscy9jYWNoZXMucHk=) | `87.71% <0.00%> (+1.16%)` | :arrow_up: |
| [pdm/installers/synchronizers.py](https://codecov.io/gh/pdm-project/pdm/pull/1187/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project#diff-cGRtL2luc3RhbGxlcnMvc3luY2hyb25pemVycy5weQ==) | `86.26% <0.00%> (+3.86%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pdm-project/pdm/pull/1187?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pdm-project/pdm/pull/1187?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project). Last update [96710e6...d6ce6b9](https://codecov.io/gh/pdm-project/pdm/pull/1187?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pdm-project).


Patch:
diff --git a/news/1173.bugfix.md b/news/1173.bugfix.md
new file mode 100644
index 00000000..d6271486
--- /dev/null
+++ b/news/1173.bugfix.md
@@ -0,0 +1,1 @@
+Fix a bug that requirement extras and underlying are resolved to the different version
diff --git a/pdm/models/repositories.py b/pdm/models/repositories.py
index 1565d40c..76cf7a45 100644
--- a/pdm/models/repositories.py
+++ b/pdm/models/repositories.py
@@ -78,7 +78,11 @@ class BaseRepository:
             # XXX: If the requirement has extras, add the original candidate
             # (without extras) as its dependency. This ensures the same package with
             # different extras resolve to the same version.
-            self_req = dataclasses.replace(candidate.req, extras=None, marker=None)
+            self_req = dataclasses.replace(
+                candidate.req.as_pinned_version(candidate.version),
+                extras=None,
+                marker=None,
+            )
             reqs.append(self_req)
         # Store the metadata on the candidate for caching
         candidate.requires_python = requires_python


Test patch:
diff --git a/tests/resolver/test_resolve.py b/tests/resolver/test_resolve.py
index 26106963..ac2b3acb 100644
--- a/tests/resolver/test_resolve.py
+++ b/tests/resolver/test_resolve.py
@@ -311,3 +311,14 @@ def test_resolve_extra_requirements_no_break_constraints(resolve, repository):
     result = resolve(["foo[chardet]<0.2.0"])
     assert "chardet" in result
     assert result["foo"].version == "0.1.0"
+
+
+def test_resolve_extra_and_underlying_to_the_same_version(resolve, repository):
+    repository.add_candidate("foo", "0.1.0")
+    repository.add_dependencies("foo", "0.1.0", ["chardet; extra=='enc'"])
+    repository.add_candidate("foo", "0.2.0")
+    repository.add_dependencies("foo", "0.2.0", ["chardet; extra=='enc'"])
+    repository.add_candidate("bar", "0.1.0")
+    repository.add_dependencies("bar", "0.1.0", ["foo[enc]>=0.1.0"])
+    result = resolve(["foo==0.1.0", "bar"])
+    assert result["foo"].version == result["foo[enc]"].version == "0.1.0"