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

pantsbuild / pants / 25443604553

06 May 2026 03:05PM UTC coverage: 92.879% (-0.04%) from 92.915%
25443604553

push

github

web-flow
[pants_ng] Scaffolding for a pants_ng mode. (#23319)

In this mode the command line is parsed as an
NG invocation, and dispatched appropriately.

Of course at the moment there are no
implementations to dispatch to. That will follow.

This does expose a new option, `pants_ng` to users. 
There is a big warning not to set it, but we're not trying
to hide that we're working on a new thing, so I am
comfortable with this.

25 of 76 new or added lines in 9 files covered. (32.89%)

1294 existing lines in 76 files now uncovered.

92234 of 99306 relevant lines covered (92.88%)

4.05 hits per line

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

91.84
/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
12✔
5

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

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

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

14

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

18
    @classmethod
12✔
19
    def parse(cls, line: str, description_of_origin: str = "") -> PipRequirement:
12✔
20
        try:
12✔
21
            return cls(Requirement(line))
12✔
22
        except InvalidRequirement as e:
3✔
23
            scheme, netloc, path, query, fragment = urllib.parse.urlsplit(line, scheme="file")
3✔
24
            if fragment:
3✔
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)
1✔
UNCOV
33
                egg = fragment_params.get("egg")
1✔
UNCOV
34
                if egg:
1✔
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]
1✔
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))
1✔
UNCOV
40
                    pep_440_req_str = f"{project}@ {full_url}"
1✔
UNCOV
41
                    try:
1✔
UNCOV
42
                        return cls(Requirement(pep_440_req_str))
1✔
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 ""
3✔
49
            raise ValueError(f"Invalid requirement '{line}'{origin_str}: {e}")
3✔
50

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

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

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

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

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

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

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

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

81
    def __str__(self):
12✔
82
        return str(self._req)
9✔
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