• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 23173035367

17 Mar 2026 12:47AM UTC coverage: 91.371% (-1.6%) from 92.933%
23173035367

push

github

web-flow
update helm (and friends) to a recent 3.x seies (#23143)

v4 is a major breaking change, so holding off on that. (There was also
just a 3.20, but 3.19 has this reassuring series of bug fixes and was
also quite recent.)

2 of 2 new or added lines in 2 files covered. (100.0%)

1263 existing lines in 73 files now uncovered.

86196 of 94336 relevant lines covered (91.37%)

3.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.43
/src/python/pants/util/pip_requirement.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
11✔
5

6
import logging
11✔
7
import urllib.parse
11✔
8

9
from packaging.requirements import InvalidRequirement, Requirement
11✔
10
from packaging.specifiers import SpecifierSet
11✔
11

12
logger = logging.getLogger(__name__)
11✔
13

14

15
class PipRequirement:
11✔
16
    """A Pip-style requirement."""
17

18
    @classmethod
11✔
19
    def parse(cls, line: str, description_of_origin: str = "") -> PipRequirement:
11✔
20
        try:
11✔
21
            return cls(Requirement(line))
11✔
22
        except InvalidRequirement as e:
2✔
23
            scheme, netloc, path, query, fragment = urllib.parse.urlsplit(line, scheme="file")
2✔
24
            if fragment:
2✔
25
                # Try converting a pip VCS-style requirement into a PEP-440 one that can be
26
                # parsed as a Requirement. E.g.,
27
                # git+https://github.com/django/django.git@stable/2.1.x#egg=Django
28
                # into
29
                # Django@ git+https://github.com/django/django.git@stable/2.1.x#egg=Django
30

31
                # Note: In pip VCS urls the fragment is a query-style string.
UNCOV
32
                fragment_params = urllib.parse.parse_qs(fragment)
×
UNCOV
33
                egg = fragment_params.get("egg")
×
UNCOV
34
                if egg:
×
35
                    # parse_qs() ignores params with empty values by default, so we're guaranteed
36
                    # that there is at least one value in this list.
UNCOV
37
                    project = egg[0]
×
38
                    # We recompose the URL to force the default file:// scheme to be explicit.
UNCOV
39
                    full_url = urllib.parse.urlunsplit((scheme, netloc, path, query, fragment))
×
UNCOV
40
                    pep_440_req_str = f"{project}@ {full_url}"
×
UNCOV
41
                    try:
×
UNCOV
42
                        return cls(Requirement(pep_440_req_str))
×
43
                    except InvalidRequirement:
×
44
                        # If parsing the converted URL fails for some reason, it's probably less
45
                        # confusing to the user if we raise the original error instead of one for
46
                        # a synthetic requirement string they don't directly know about.
47
                        pass
×
48
            origin_str = f" in {description_of_origin}" if description_of_origin else ""
2✔
49
            raise ValueError(f"Invalid requirement '{line}'{origin_str}: {e}")
2✔
50

51
    def __init__(self, req: Requirement):
11✔
52
        self._req = req
11✔
53

54
    def as_packaging_requirement(self) -> Requirement:
11✔
55
        return self._req
×
56

57
    @property
11✔
58
    def name(self) -> str:
11✔
59
        return self._req.name
11✔
60

61
    @property
11✔
62
    def specifier_set(self) -> SpecifierSet:
11✔
UNCOV
63
        return self._req.specifier
×
64

65
    @property
11✔
66
    def url(self):
11✔
UNCOV
67
        return self._req.url
×
68

69
    def __hash__(self):
11✔
70
        return hash(self._req)
11✔
71

72
    def __eq__(self, other):
11✔
73
        # Semantic equality requires parsing the specifier (since order in it doesn't matter).
74
        if not isinstance(other, self.__class__):
8✔
75
            return False
×
76
        return self._req == other._req
8✔
77

78
    def __repr__(self) -> str:
11✔
79
        return f"{self.__class__.__name__}({self._req})"
5✔
80

81
    def __str__(self):
11✔
82
        return str(self._req)
8✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc