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

pantsbuild / pants / 25441711719

06 May 2026 02:31PM UTC coverage: 92.915%. Remained the same
25441711719

push

github

web-flow
use sha pin (with comment) format for generated actions (#23312)

Per the GitHub Action best practices we recently enabled at #23249, we
should pin each action to a SHA so that the reference is actually
immutable.

This will -- I hope -- knock out a large chunk of the 421 alerts we
currently get from zizmor. The next followup would then be upgrades and
harmonizing the generated and none-generated pins.

Notice: This idea was suggested by Claude while going over pinact output
and I was surprised to see that post processing the yaml wasn't too
gross.

92206 of 99237 relevant lines covered (92.91%)

4.04 hits per line

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

94.29
/src/python/pants/backend/python/macros/python_artifact.py
1
# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
import collections.abc
12✔
5
import copy
12✔
6
import json
12✔
7
from typing import Any
12✔
8

9
from pants.util.strutil import softwrap
12✔
10

11

12
def _normalize_entry_points(
12✔
13
    all_entry_points: dict[str, list[str] | dict[str, str]],
14
) -> dict[str, dict[str, str]]:
15
    """Ensure any entry points are in the form Dict[str, Dict[str, str]]."""
16
    if not isinstance(all_entry_points, collections.abc.Mapping):
2✔
17
        raise ValueError(
1✔
18
            softwrap(
19
                f"""
20
                The `entry_points` in `python_artifact()` must be a dictionary,
21
                but was {all_entry_points!r} with type {type(all_entry_points).__name__}.
22
                """
23
            )
24
        )
25

26
    def _values_to_entry_points(values):
2✔
27
        if isinstance(values, collections.abc.Mapping):
2✔
28
            return values
2✔
29
        if isinstance(values, collections.abc.Iterable) and not isinstance(values, str):
1✔
30
            for entry_point in values:
1✔
31
                if not isinstance(entry_point, str) or "=" not in entry_point:
1✔
32
                    raise ValueError(
1✔
33
                        softwrap(
34
                            f"""
35
                            Invalid `entry_point`, expected `<name> = <entry point>`,
36
                            but got {entry_point!r}.
37
                            """
38
                        )
39
                    )
40

41
            return dict(tuple(map(str.strip, entry_point.split("=", 1))) for entry_point in values)
1✔
42
        raise ValueError(
1✔
43
            softwrap(
44
                f"""
45
                The values of the `entry_points` dictionary in `python_artifact()` must be
46
                a list of strings or a dictionary of string to string,
47
                but got {values!r} of type {type(values).__name__}.
48
                """
49
            )
50
        )
51

52
    return {
2✔
53
        category: _values_to_entry_points(values) for category, values in all_entry_points.items()
54
    }
55

56

57
class PythonArtifact:
12✔
58
    """Represents a Python setup.py-based project."""
59

60
    def __init__(self, **kwargs) -> None:
12✔
61
        """
62
        :param kwargs: Passed to `setuptools.setup
63
          <https://setuptools.pypa.io/en/latest/setuptools.html>`_.
64
        """
65
        if "entry_points" in kwargs:
6✔
66
            # coerce entry points from Dict[str, List[str]] to Dict[str, Dict[str, str]]
67
            kwargs["entry_points"] = _normalize_entry_points(kwargs["entry_points"])
2✔
68

69
        self._kw: dict[str, Any] = copy.deepcopy(kwargs)
6✔
70
        # The kwargs come from a BUILD file, and can contain somewhat arbitrary nested structures,
71
        # so we don't have a principled way to make them into a hashable data structure.
72
        # E.g., we can't naively turn all lists into tuples because distutils checks that some
73
        # fields (such as ext_modules) are lists, and doesn't accept tuples.
74
        # Instead we stringify and precompute a hash to use in our own __hash__, since we know
75
        # that this object is immutable.
76
        self._hash: int = hash(json.dumps(kwargs, sort_keys=True))
6✔
77

78
    @property
12✔
79
    def kwargs(self) -> dict[str, Any]:
12✔
80
        return self._kw
5✔
81

82
    def asdict(self) -> dict[str, Any]:
12✔
83
        return self.kwargs
×
84

85
    def __eq__(self, other: Any) -> bool:
12✔
86
        if not isinstance(other, PythonArtifact):
4✔
87
            return False
×
88
        return self._kw == other._kw
4✔
89

90
    def __hash__(self) -> int:
12✔
91
        return self._hash
6✔
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