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

pantsbuild / pants / 19015773527

02 Nov 2025 05:33PM UTC coverage: 17.872% (-62.4%) from 80.3%
19015773527

Pull #22816

github

web-flow
Merge a12d75757 into 6c024e162
Pull Request #22816: Update Pants internal Python to 3.14

4 of 5 new or added lines in 3 files covered. (80.0%)

28452 existing lines in 683 files now uncovered.

9831 of 55007 relevant lines covered (17.87%)

0.18 hits per line

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

0.0
/src/python/pants/jvm/resolve/coordinate.py
1
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
UNCOV
3
from __future__ import annotations
×
4

UNCOV
5
import re
×
UNCOV
6
from dataclasses import dataclass
×
7

UNCOV
8
from pants.engine.collection import DeduplicatedCollection
×
9

10

UNCOV
11
class InvalidCoordinateString(Exception):
×
12
    """The coordinate string being passed is invalid or malformed."""
13

UNCOV
14
    def __init__(self, coords: str) -> None:
×
15
        super().__init__(f"Received invalid artifact coordinates: {coords}")
×
16

17

UNCOV
18
@dataclass(frozen=True, order=True)
×
UNCOV
19
class Coordinate:
×
20
    """A single Maven-style coordinate for a JVM dependency.
21

22
    Coursier uses at least two string serializations of coordinates:
23
    1. A format that is accepted by the Coursier CLI which uses trailing attributes to specify
24
       optional fields like `packaging`/`type`, `classifier`, `url`, etc. See `to_coord_arg_str`.
25
    2. A format in the JSON report, which uses token counts to specify optional fields. We
26
       additionally use this format in our own lockfile. See `to_coord_str` and `from_coord_str`.
27
    """
28

UNCOV
29
    REGEX = re.compile("([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)")
×
30

UNCOV
31
    group: str
×
UNCOV
32
    artifact: str
×
UNCOV
33
    version: str
×
UNCOV
34
    packaging: str = "jar"
×
UNCOV
35
    classifier: str | None = None
×
36

37
    # True to enforce that the exact declared version of a coordinate is fetched, rather than
38
    # allowing dependency resolution to adjust the version when conflicts occur.
UNCOV
39
    strict: bool = True
×
40

UNCOV
41
    @staticmethod
×
UNCOV
42
    def from_json_dict(data: dict) -> Coordinate:
×
UNCOV
43
        return Coordinate(
×
44
            group=data["group"],
45
            artifact=data["artifact"],
46
            version=data["version"],
47
            packaging=data.get("packaging", "jar"),
48
            classifier=data.get("classifier", None),
49
        )
50

UNCOV
51
    def to_json_dict(self) -> dict:
×
52
        ret = {
×
53
            "group": self.group,
54
            "artifact": self.artifact,
55
            "version": self.version,
56
            "packaging": self.packaging,
57
            "classifier": self.classifier,
58
        }
59
        return ret
×
60

UNCOV
61
    @classmethod
×
UNCOV
62
    def from_coord_str(cls, s: str) -> Coordinate:
×
63
        """Parses from a coordinate string with optional `packaging` and `classifier` coordinates.
64

65
        See the classdoc for more information on the format.
66

67
        Using Aether's implementation as reference
68
        http://www.javased.com/index.php?source_dir=aether-core/aether-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java
69

70
        ${organisation}:${artifact}[:${packaging}[:${classifier}]]:${version}
71

72
        See also: `to_coord_str`.
73
        """
74

UNCOV
75
        parts = Coordinate.REGEX.match(s)
×
UNCOV
76
        if parts is not None:
×
UNCOV
77
            packaging_part = parts.group(4)
×
UNCOV
78
            return cls(
×
79
                group=parts.group(1),
80
                artifact=parts.group(2),
81
                packaging=packaging_part if packaging_part is not None else "jar",
82
                classifier=parts.group(6),
83
                version=parts.group(7),
84
            )
85
        else:
86
            raise InvalidCoordinateString(s)
×
87

UNCOV
88
    def to_coord_str(self, versioned: bool = True) -> str:
×
89
        """Renders the coordinate in Coursier's JSON-report format, which does not use attributes.
90

91
        See also: `from_coord_str`.
92
        """
UNCOV
93
        unversioned = f"{self.group}:{self.artifact}"
×
UNCOV
94
        if self.classifier is not None:
×
95
            unversioned += f":{self.packaging}:{self.classifier}"
×
UNCOV
96
        elif self.packaging != "jar":
×
97
            unversioned += f":{self.packaging}"
×
98

UNCOV
99
        version_suffix = ""
×
UNCOV
100
        if versioned:
×
UNCOV
101
            version_suffix = f":{self.version}"
×
UNCOV
102
        return f"{unversioned}{version_suffix}"
×
103

UNCOV
104
    def to_coord_arg_str(self, extra_attrs: dict[str, str] | None = None) -> str:
×
105
        """Renders the coordinate in Coursier's CLI input format.
106

107
        The CLI input format uses trailing key-val attributes to specify `packaging`, `url`, etc.
108

109
        See https://github.com/coursier/coursier/blob/b5d5429a909426f4465a9599d25c678189a54549/modules/coursier/shared/src/test/scala/coursier/parse/DependencyParserTests.scala#L7
110
        """
UNCOV
111
        attrs = dict(extra_attrs or {})
×
UNCOV
112
        if self.packaging != "jar":
×
113
            # NB: Coursier refers to `packaging` as `type` internally.
114
            attrs["type"] = self.packaging
×
UNCOV
115
        if self.classifier:
×
116
            attrs["classifier"] = self.classifier
×
UNCOV
117
        attrs_sep_str = "," if attrs else ""
×
UNCOV
118
        attrs_str = ",".join((f"{k}={v}" for k, v in attrs.items()))
×
UNCOV
119
        return f"{self.group}:{self.artifact}:{self.version}{attrs_sep_str}{attrs_str}"
×
120

121

UNCOV
122
class Coordinates(DeduplicatedCollection[Coordinate]):
×
123
    """An ordered list of `Coordinate`s."""
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