Skip to content

Commit dbb1cf0

Browse files
Cleanup requirement string to avoid packvers parsing issues
Extract clean name and version from requirements instead of using dumps() output that includes hash options and other pip-specific syntax that `packvers.requirements.Requirement` cannot parse. Resolves: #243. Signed-off-by: Marcel Bochtler <[email protected]>
1 parent ff07d0e commit dbb1cf0

File tree

6 files changed

+675
-2
lines changed

6 files changed

+675
-2
lines changed

src/_packagedcode/pypi.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ def parse(cls, location):
534534
for metapath in path.iterdir():
535535
if not metapath.name.endswith('METADATA'):
536536
continue
537-
537+
538538
yield parse_metadata(
539539
location=metapath,
540540
datasource_id=cls.datasource_id,
@@ -907,6 +907,52 @@ def parse(cls, location):
907907
# TODO: enable nested load
908908

909909

910+
def build_pep508_requirement_string(req):
911+
"""
912+
Build a PEP 508 compliant requirement string based on https://peps.python.org/pep-0508.
913+
914+
The format follows the PEP 508 grammar: name [extras] version_spec ; marker
915+
916+
This excludes pip-specific options that are not part of PEP 508:
917+
- --hash options (hash_options)
918+
- --editable/-e (is_editable)
919+
- --find-links/-f (from link URLs)
920+
- --constraint/-c (is_constraint)
921+
- VCS URLs (is_vcs_url)
922+
- Local paths (is_local_path)
923+
924+
But preserves standard PEP 508 requirement components:
925+
- Package name (required)
926+
- Version specifiers (>=, ==, etc.)
927+
- Extras [extra1,extra2]
928+
- Environment markers ; python_version >= "3.8"
929+
930+
Args:
931+
req: A requirement object with attributes: name, extras, specifier, marker
932+
933+
Returns:
934+
str: A PEP 508 compliant requirement string, or empty string if no name
935+
"""
936+
if not req.name:
937+
return ""
938+
939+
parts = [req.name]
940+
941+
# Add extras: package[extra1,extra2]
942+
if req.extras:
943+
parts.append(f"[{','.join(req.extras)}]")
944+
945+
# Add version specifiers: >=1.0,<2.0
946+
if req.specifier:
947+
parts.append(str(req.specifier))
948+
949+
# Add environment markers: ; python_version >= "3.8"
950+
if req.marker:
951+
parts.append(f"; {req.marker}")
952+
953+
return "".join(parts)
954+
955+
910956
def get_requirements_txt_dependencies(location, include_nested=False):
911957
"""
912958
Return a two-tuple of (list of deps, mapping of extra data) list of
@@ -945,7 +991,7 @@ def get_requirements_txt_dependencies(location, include_nested=False):
945991

946992
purl = purl and purl.to_string() or None
947993

948-
requirement = req.dumps()
994+
requirement = build_pep508_requirement_string(req)
949995

950996
if location.endswith(
951997
(
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
addict==2.4.0 \
2+
--hash=sha256:249bb56bbfd3cdc2a004ea0ff4c2b6ddc84d53bc2194761636eb314d5cfa5dfc \
3+
--hash=sha256:b3b2210e0e067a281f5646c8c5db92e99b7231ea8b0eb5f74dbdf9e259d4e494
4+
5+
# Package with environment marker and no hashes
6+
license-expression ; platform_system == "Windows"
7+
8+
# Package with both hash and environment marker
9+
requests==2.25.1 ; python_version >= "3.6" \
10+
--hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e \
11+
--hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804
12+
13+
# Package with complex specifiers and extras
14+
click[unicode]>=6.0,<7.0 ; python_version < "3.8"
15+
16+
# Package with multiple extras and environment marker
17+
flask[async,dotenv]>=1.0 ; python_version >= "3.7" and platform_machine == "x86_64"

tests/data/hash-requirements.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
addict==2.4.0 \
2+
--hash=sha256:249bb56bbfd3cdc2a004ea0ff4c2b6ddc84d53bc2194761636eb314d5cfa5dfc \
3+
--hash=sha256:b3b2210e0e067a281f5646c8c5db92e99b7231ea8b0eb5f74dbdf9e259d4e494
4+
5+
requests==2.25.1 \
6+
--hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e \
7+
--hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804

0 commit comments

Comments
 (0)