Repository: pex-tool/pex

Issue:
pex3 lock create includes build requirements
```
$ pex3 lock create pantsbuild.pants
```

Includes `setuptools` twice in the locked resolves.

> Looks like PEP-517 build requirements are getting recorded: https://github.com/pantsbuild/pex/blob/6720d4c64fb033e8418b2e14535512baa01b9a2d/pex/vendor/_vendored/pip/pip/_internal/pyproject.py#L118-L130
-- @jsirois 

```
{
  "artifacts": [
    {
      "algorithm": "sha256",
      "hash": "60d78588f15b048f86e35cdab73003d8b21dd45108ee61a6693881a427f22073",
      "url": "https://files.pythonhosted.org/packages/4b/b9/71687c5d2034c863db1db2e0704f5e27581ff3cb44d7f293968c5e08ceb3/setuptools-57.5.0-py3-none-any.whl"
    }
  ],
  "project_name": "setuptools",
  "requirement": "setuptools<58.0,>=56.0.0",
  "version": "57.5",
  "via": [
    "pantsbuild.pants"
  ]
}
{
  "artifacts": [
    {
      "algorithm": "sha256",
      "hash": "68eb94073fc486091447fcb0501efd6560a0e5a1839ba249e5ff3c4c93f05f90",
      "url": "https://files.pythonhosted.org/packages/eb/53/0dd4c7960579da8be13fa9b2c2591643d37f323e3d79f8bc8b1b6c8e6217/setuptools-60.5.0-py3-none-any.whl"
    }
  ],
  "project_name": "setuptools",
  "requirement": "setuptools>=40.8.0",
  "version": "60.5",
  "via": []
}
```


Discussion:
@kaos can you provide more details similar to below? I don't repro (I happen to use pex main below, but that's == 2.1.61. I verified by running `pex3 lock create pantsbuild.pants --python python3.9 -o lock2.json && diff lock.json lock2.json` which gives no diff.):
```
$ python3.9 -mpex.cli lock create pantsbuild.pants -o lock.json
```

```json
{
  "allow_builds": true,
  "allow_prereleases": false,
  "allow_wheels": true,
  "constraints": [],
  "locked_resolves": [
    {
      "locked_requirements": [
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "00d2dde5a675579325902536738dd27e4fac1fd68f773fe36c21044eb559e187",
              "url": "https://files.pythonhosted.org/packages/53/18/a56e2fe47b259bb52201093a3a9d4a32014f9d85071ad07e9d60600890ca/ansicolors-1.1.8-py2.py3-none-any.whl"
            }
          ],
          "project_name": "ansicolors",
          "requirement": "ansicolors==1.1.8",
          "version": "1.1.8",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569",
              "url": "https://files.pythonhosted.org/packages/37/45/946c02767aabb873146011e665728b680884cd8fe70dde973c640e45b775/certifi-2021.10.8-py2.py3-none-any.whl"
            }
          ],
          "project_name": "certifi",
          "requirement": "certifi>=2017.4.17",
          "version": "2021.10.8",
          "via": [
            "requests",
            "humbug==0.2.6",
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455",
              "url": "https://files.pythonhosted.org/packages/84/3e/1037abe6498e65d645ce7a22d3402605d49a3b2c7f20c3abb027760da4f0/charset_normalizer-2.0.10-py3-none-any.whl"
            }
          ],
          "project_name": "charset-normalizer",
          "requirement": "charset-normalizer~=2.0.0; python_version >= \"3\"",
          "version": "2.0.10",
          "via": [
            "requests",
            "humbug==0.2.6",
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "74b6847e0a6bb3b56c8511af8e24c40e4cf7a774dfff5b251c260ed338096a4b",
              "url": "https://files.pythonhosted.org/packages/78/20/c862d765287e9e8b29f826749ebae8775bdca50b2cb2ca079346d5fbfd76/fasteners-0.16-py2.py3-none-any.whl"
            }
          ],
          "project_name": "fasteners",
          "requirement": "fasteners==0.16",
          "version": "0.16",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "beae9dbd069ca04eb3fa9d85a535ed35195bfd86579024bf92151a74c7789579",
              "url": "https://files.pythonhosted.org/packages/be/f7/c03c0ebd9bfcaaaf28786f6103615040f688848c406116c549eb01799668/humbug-0.2.6-py3-none-any.whl"
            }
          ],
          "project_name": "humbug",
          "requirement": "humbug==0.2.6",
          "version": "0.2.6",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
              "url": "https://files.pythonhosted.org/packages/04/a2/d918dcd22354d8958fe113e1a3630137e0fc8b44859ade3063982eacd2a4/idna-3.3-py3-none-any.whl"
            }
          ],
          "project_name": "idna",
          "requirement": "idna<4,>=2.5; python_version >= \"3\"",
          "version": "3.3",
          "via": [
            "requests",
            "humbug==0.2.6",
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "2a64c66a08f56ed45a805691c2fd2e1caef00edd6ccf4c4e5eff02cd94ad8364",
              "url": "https://files.pythonhosted.org/packages/8d/44/c30dd1a23b80efefe6cfd1942131faba7fa1a97d932d464afade148e0613/ijson-3.1.4-cp39-cp39-manylinux2010_x86_64.whl"
            }
          ],
          "project_name": "ijson",
          "requirement": "ijson==3.1.4",
          "version": "3.1.4",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14",
              "url": "https://files.pythonhosted.org/packages/3c/77/e2362b676dc5008d81be423070dd9577fa03be5da2ba1105811900fda546/packaging-21.0-py3-none-any.whl"
            }
          ],
          "project_name": "packaging",
          "requirement": "packaging==21.0",
          "version": "21",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "115c865bb33dd1e2a61c75bce4a9f8236a218edc723143621ee888c3e89dc1cb",
              "url": "https://files.pythonhosted.org/packages/4c/4b/667dec4b34377717ee1ada3a6be5805893f7b29ea9705e4329d8c2ee33a9/pantsbuild.pants-2.8.0-cp39-cp39-manylinux2014_x86_64.whl"
            }
          ],
          "project_name": "pantsbuild-pants",
          "requirement": "pantsbuild.pants",
          "version": "2.8",
          "via": []
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "e60b006abe8abfd3c3377128e22c33f30cc6dea89e2beb463cf8360e3626db62",
              "url": "https://files.pythonhosted.org/packages/fa/c4/5dbdce75117b60b6ffec65bc92ac25ee873b84158a55cfbffa1d49db6eb1/pex-2.1.54-py2.py3-none-any.whl"
            }
          ],
          "project_name": "pex",
          "requirement": "pex==2.1.54",
          "version": "2.1.54",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b",
              "url": "https://files.pythonhosted.org/packages/91/4d/033cc02ae3a47197d0ced818814e4bb8d9d29ebed4f1eb55badedec160f7/psutil-5.8.0-cp39-cp39-manylinux2010_x86_64.whl"
            }
          ],
          "project_name": "psutil",
          "requirement": "psutil==5.8.0",
          "version": "5.8",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4",
              "url": "https://files.pythonhosted.org/packages/a0/34/895006117f6fce0b4de045c87e154ee4a20c68ec0a4c9a36d900888fb6bc/pyparsing-3.0.6-py3-none-any.whl"
            }
          ],
          "project_name": "pyparsing",
          "requirement": "pyparsing>=2.0.2",
          "version": "3.0.6",
          "via": [
            "packaging==21.0",
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
              "url": "https://files.pythonhosted.org/packages/12/fc/a4d5a7554e0067677823f7265cb3ae22aed8a238560b5133b58cda252dad/PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl"
            }
          ],
          "project_name": "pyyaml",
          "requirement": "PyYAML<7.0,>=6.0",
          "version": "6",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d",
              "url": "https://files.pythonhosted.org/packages/2d/61/08076519c80041bc0ffa1a8af0cbd3bf3e2b62af10435d269a9d0f40564d/requests-2.27.1-py2.py3-none-any.whl"
            }
          ],
          "project_name": "requests",
          "requirement": "requests",
          "version": "2.27.1",
          "via": [
            "humbug==0.2.6",
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "970798d948f0c90a3eb0f8750f08cb215b89dcbee1b55ffb353ad62d9361daeb",
              "url": "https://files.pythonhosted.org/packages/5e/59/f9fef4d0682ff03a392365322d160d8ca5257a0a782b93cea7cb7658e53e/setproctitle-1.2.2-cp39-cp39-manylinux1_x86_64.whl"
            }
          ],
          "project_name": "setproctitle",
          "requirement": "setproctitle==1.2.2",
          "version": "1.2.2",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "60d78588f15b048f86e35cdab73003d8b21dd45108ee61a6693881a427f22073",
              "url": "https://files.pythonhosted.org/packages/4b/b9/71687c5d2034c863db1db2e0704f5e27581ff3cb44d7f293968c5e08ceb3/setuptools-57.5.0-py3-none-any.whl"
            }
          ],
          "project_name": "setuptools",
          "requirement": "setuptools<58.0,>=56.0.0",
          "version": "57.5",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254",
              "url": "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl"
            }
          ],
          "project_name": "six",
          "requirement": "six",
          "version": "1.16",
          "via": [
            "fasteners==0.16",
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
              "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl"
            }
          ],
          "project_name": "toml",
          "requirement": "toml==0.10.2",
          "version": "0.10.2",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "bca83cbfc0be48600a8abf1e3d87fb762a91e6d35d724029a3321dd2dce2ceb1",
              "url": "https://files.pythonhosted.org/packages/b8/86/b957c28571005b1f13b92703400453c6a3a7fddd3f75c4c83bd2e7dc4d47/types_PyYAML-5.4.3-py2.py3-none-any.whl"
            }
          ],
          "project_name": "types-pyyaml",
          "requirement": "types-PyYAML==5.4.3",
          "version": "5.4.3",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "71ed0f4c71d8fb5f3026a90ae82d163c13749b110e157d82126725ac8f714360",
              "url": "https://files.pythonhosted.org/packages/0d/14/3a722babb1c472f91b088c2622ba4e547fa668a58d892892278091390862/types_setuptools-57.0.0-py3-none-any.whl"
            }
          ],
          "project_name": "types-setuptools",
          "requirement": "types-setuptools==57.0.0",
          "version": "57",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "d4add39a90993173d49ff0b069edd122c66ad4cf5c01082b590e380ca670ee1a",
              "url": "https://files.pythonhosted.org/packages/e8/9b/97adb9cf0b76b2f9f8a93d6f0e850bcfb1af1c358ec9f4188d3b83ea6755/types_toml-0.1.3-py2.py3-none-any.whl"
            }
          ],
          "project_name": "types-toml",
          "requirement": "types-toml==0.1.3",
          "version": "0.1.3",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
              "url": "https://files.pythonhosted.org/packages/60/7a/e881b5abb54db0e6e671ab088d079c57ce54e8a01a3ca443f561ccadb37e/typing_extensions-3.7.4.3-py3-none-any.whl"
            }
          ],
          "project_name": "typing-extensions",
          "requirement": "typing-extensions==3.7.4.3",
          "version": "3.7.4.3",
          "via": [
            "pantsbuild.pants"
          ]
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed",
              "url": "https://files.pythonhosted.org/packages/4e/b8/f5a25b22e803f0578e668daa33ba3701bb37858ec80e08a150bd7d2cf1b1/urllib3-1.26.8-py2.py3-none-any.whl"
            }
          ],
          "project_name": "urllib3",
          "requirement": "urllib3<1.27,>=1.21.1",
          "version": "1.26.8",
          "via": [
            "requests",
            "humbug==0.2.6",
            "pantsbuild.pants"
          ]
        }
      ],
      "platform_tag": [
        "cp39",
        "cp39",
        "manylinux_2_33_x86_64"
      ]
    }
  ],
  "pex_version": "2.1.61",
  "requirements": [
    "pantsbuild.pants"
  ],
  "resolver_version": "pip-legacy-resolver",
  "style": "strict",
  "transitive": true
}
```
Ok @kaos I've forced a repro using the branch from #1561. The setuptools and wheel artifacts below are just fallback PEP-517 build requirements of ansicolors and not installation requirements of ansicolors:
```console
$ python -mpex.cli lock create ansicolors==1.0.2 --indent 2 --force-pep517
{
  "allow_builds": true,
  "allow_prereleases": false,
  "allow_wheels": true,
  "build_isolation": true,
  "constraints": [],
  "locked_resolves": [
    {
      "locked_requirements": [
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "7664530bb992e3847b61e3aab1580b4df9ed00c5898e80194a9933bc9c80950a",
              "url": "https://files.pythonhosted.org/packages/ac/c1/e21f0a1258ff927d124a72179669dcc7efcb57b22df8cd0e49ed8f1a308c/ansicolors-1.0.2.tar.gz"
            }
          ],
          "project_name": "ansicolors",
          "requirement": "ansicolors==1.0.2",
          "version": "1.0.2",
          "via": []
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "68eb94073fc486091447fcb0501efd6560a0e5a1839ba249e5ff3c4c93f05f90",
              "url": "https://files.pythonhosted.org/packages/eb/53/0dd4c7960579da8be13fa9b2c2591643d37f323e3d79f8bc8b1b6c8e6217/setuptools-60.5.0-py3-none-any.whl"
            }
          ],
          "project_name": "setuptools",
          "requirement": "setuptools>=40.8.0",
          "version": "60.5",
          "via": []
        },
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a",
              "url": "https://files.pythonhosted.org/packages/27/d6/003e593296a85fd6ed616ed962795b2f87709c3eee2bca4f6d0fe55c6d00/wheel-0.37.1-py2.py3-none-any.whl"
            }
          ],
          "project_name": "wheel",
          "requirement": "wheel",
          "version": "0.37.1",
          "via": []
        }
      ],
      "platform_tag": [
        "cp310",
        "cp310",
        "manylinux_2_33_x86_64"
      ]
    }
  ],
  "pex_version": "2.1.61",
  "prefer_older_binary": false,
  "requirements": [
    "ansicolors==1.0.2"
  ],
  "resolver_version": "pip-legacy-resolver",
  "style": "strict",
  "transitive": true,
  "use_pep517": true
}
```

Resolving patch:
diff --git a/pex/pip.py b/pex/pip.py
--- a/pex/pip.py
+++ b/pex/pip.py
@@ -63,6 +63,7 @@
         List,
         Mapping,
         Optional,
+        Pattern,
         Protocol,
         Sequence,
         Tuple,
@@ -405,6 +406,7 @@ def __init__(
         )
         self._analysis_completed = False
         self._locked_resolve = None  # type: Optional[LockedResolve]
+        self._done_building_re = None  # type: Optional[Pattern]
 
     def should_collect(self, returncode):
         # type: (int) -> bool
@@ -429,20 +431,48 @@ def _extract_resolve_data(url):
     def analyze(self, line):
         # type: (str) -> _LogAnalyzer.Continue[None]
 
+        # The log sequence for processing a resolved requirement is as follows (log lines irrelevant
+        # to our purposes omitted):
+        #
+        # 1.) "... Found link <url1> ..."
+        # ...
+        # 1.) "... Found link <urlN> ..."
+        # 2.) "... Added <requirement pin> from <url> ... to build tracker ..."
+        # 3.) Lines related to extracting metadata from <requirement pin>'s artifact
+        # 4.) "... Removed <requirement pin> from <url> ... from build tracker ..."
+        #
+        # The lines in section 3 can contain this same pattern of lines if the metadata extraction
+        # proceeds via PEP-517 which recursively uses Pip to resolve build dependencies. We want to
+        # ignore this recursion since a lock should only contain install requirements and not build
+        # requirements (If a build proceeds differently tomorrow than today then we don't care as
+        # long as the final built artifact hashes the same. In other words, we completely rely on a
+        # cryptographic fingerprint for reproducibility and security guarantees from a lock).
+
         if self._analysis_completed:
             raise self.StateError("Line analysis was requested after the log analysis completed.")
 
+        if self._done_building_re:
+            if self._done_building_re.search(line):
+                self._done_building_re = None
+            return self.Continue()
+
         match = re.search(
-            r"Added (?P<requirement>.*) from (?P<url>[^\s]+) (\(from (?P<from>.*)\) )?to build "
+            r"Added (?P<requirement>.*) from (?P<url>[^\s]+) (?:\(from (?P<from>.*)\) )?to build "
             r"tracker",
             line,
         )
         if match:
-            requirement = Requirement.parse(match.group("requirement"))
-            project_name_and_version, partial_artifact = self._extract_resolve_data(
-                match.group("url")
+            raw_requirement = match.group("requirement")
+            url = match.group("url")
+            self._done_building_re = re.compile(
+                r"Removed {requirement} from {url} (?:.* )?from build tracker".format(
+                    requirement=re.escape(raw_requirement), url=re.escape(url)
+                )
             )
 
+            requirement = Requirement.parse(raw_requirement)
+            project_name_and_version, partial_artifact = self._extract_resolve_data(url)
+
             from_ = match.group("from")
             if from_:
                 via = tuple(from_.split("->"))
@@ -451,6 +481,7 @@ def analyze(self, line):
 
             additional_artifacts = self._links[project_name_and_version]
             additional_artifacts.discard(partial_artifact)
+            self._links.clear()
 
             self._resolved_requirements.append(
                 ResolvedRequirement(


Test patch:
diff --git a/tests/integration/cli/commands/test_lock.py b/tests/integration/cli/commands/test_lock.py
--- a/tests/integration/cli/commands/test_lock.py
+++ b/tests/integration/cli/commands/test_lock.py
@@ -530,3 +530,86 @@ def test_update_partial(tmpdir):
     )
     result.assert_success()
     assert DUAL_UPDATE_LOCKFILE == lockfile.load(lock_file_path)
+
+
+def test_excludes_pep517_build_requirements_issue_1565(tmpdir):
+    # type: (Any) -> None
+
+    # Here we resolve ansicolors 1.0.2 and find 2020.12.3 which are both pure legacy sdist
+    # distributions that will need to download build requirements using Pip since we force PEP-517.
+    # The cowsay 4.0 requirement is satisfied by a universal wheel and has no build requirements as
+    # a result.
+
+    result = run_pex3(
+        "lock",
+        "create",
+        "ansicolors==1.0.2",
+        "find==2020.12.3",
+        "cowsay==4.0",
+        "--force-pep517",
+    )
+    result.assert_success()
+    lock = lockfile.loads(result.output)
+
+    assert 1 == len(lock.locked_resolves)
+    assert (
+        SortedTuple(
+            [
+                LockedRequirement.create(
+                    pin=Pin(
+                        project_name=ProjectName(project_name="ansicolors"),
+                        version=Version(version="1.0.2"),
+                    ),
+                    artifact=Artifact(
+                        url=(
+                            "https://files.pythonhosted.org/packages/ac/c1/"
+                            "e21f0a1258ff927d124a72179669dcc7efcb57b22df8cd0e49ed8f1a308c/"
+                            "ansicolors-1.0.2.tar.gz"
+                        ),
+                        fingerprint=Fingerprint(
+                            algorithm="sha256",
+                            hash="7664530bb992e3847b61e3aab1580b4df9ed00c5898e80194a9933bc9c80950a",
+                        ),
+                    ),
+                    requirement=Requirement.parse("ansicolors==1.0.2"),
+                ),
+                LockedRequirement.create(
+                    pin=Pin(
+                        project_name=ProjectName(project_name="find"),
+                        version=Version(version="2020.12.3"),
+                    ),
+                    artifact=Artifact(
+                        url=(
+                            "https://files.pythonhosted.org/packages/91/1c/"
+                            "90cac4602ec146ce6f055b2e9598f46da08e941dd860f0498af764407b7e/"
+                            "find-2020.12.3.tar.gz"
+                        ),
+                        fingerprint=Fingerprint(
+                            algorithm="sha256",
+                            hash="7dadadb63e13de019463f13d83e0e0567a963cad99a568d0f0001ac1104d8210",
+                        ),
+                    ),
+                    requirement=Requirement.parse("find==2020.12.3"),
+                ),
+                LockedRequirement.create(
+                    pin=Pin(
+                        project_name=ProjectName(project_name="cowsay"),
+                        version=Version(version="4"),
+                    ),
+                    artifact=Artifact(
+                        url=(
+                            "https://files.pythonhosted.org/packages/b7/65/"
+                            "38f31ef16efc312562f68732098d6f7ba3b2c108a4aaa8ac8ba673ee0871/"
+                            "cowsay-4.0-py2.py3-none-any.whl"
+                        ),
+                        fingerprint=Fingerprint(
+                            algorithm="sha256",
+                            hash="2594b11d6624fff4bf5147b6bdd510ada54a7b5b4e3f2b15ac2a6d3cf99e0bf8",
+                        ),
+                    ),
+                    requirement=Requirement.parse("cowsay==4.0"),
+                ),
+            ]
+        )
+        == lock.locked_resolves[0].locked_requirements
+    )