• 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/core/goals/repl.py
1
# Copyright 2020 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 os
×
UNCOV
6
from abc import ABC
×
UNCOV
7
from collections.abc import Iterable, Mapping, Sequence
×
UNCOV
8
from dataclasses import dataclass
×
UNCOV
9
from typing import ClassVar
×
10

UNCOV
11
from pants.core.environments.rules import _warn_on_non_local_environments
×
UNCOV
12
from pants.engine.addresses import Addresses
×
UNCOV
13
from pants.engine.console import Console
×
UNCOV
14
from pants.engine.env_vars import CompleteEnvironmentVars
×
UNCOV
15
from pants.engine.environment import EnvironmentName
×
UNCOV
16
from pants.engine.fs import Digest
×
UNCOV
17
from pants.engine.goal import Goal, GoalSubsystem
×
UNCOV
18
from pants.engine.intrinsics import run_interactive_process
×
UNCOV
19
from pants.engine.process import InteractiveProcess
×
UNCOV
20
from pants.engine.rules import collect_rules, goal_rule, implicitly, rule
×
UNCOV
21
from pants.engine.target import FilteredTargets, Target
×
UNCOV
22
from pants.engine.unions import UnionMembership, union
×
UNCOV
23
from pants.option.option_types import ArgsListOption, BoolOption, StrOption
×
UNCOV
24
from pants.util.frozendict import FrozenDict
×
UNCOV
25
from pants.util.memo import memoized_property
×
UNCOV
26
from pants.util.strutil import softwrap
×
27

28

UNCOV
29
@union(in_scope_types=[EnvironmentName])
×
UNCOV
30
@dataclass(frozen=True)
×
UNCOV
31
class ReplImplementation(ABC):
×
32
    """A REPL implementation for a specific language or runtime.
33

34
    Proxies from the top-level `repl` goal to an actual implementation.
35
    """
36

UNCOV
37
    name: ClassVar[str]
×
UNCOV
38
    supports_args: ClassVar[bool]
×
39

UNCOV
40
    targets: Sequence[Target]
×
41

UNCOV
42
    def in_chroot(self, relpath: str) -> str:
×
43
        return os.path.join("{chroot}", relpath)
×
44

UNCOV
45
    @memoized_property
×
UNCOV
46
    def addresses(self) -> Addresses:
×
47
        return Addresses(t.address for t in self.targets)
×
48

49

UNCOV
50
class ReplSubsystem(GoalSubsystem):
×
UNCOV
51
    name = "repl"
×
UNCOV
52
    help = "Open a REPL with the specified code loadable."
×
53

UNCOV
54
    @classmethod
×
UNCOV
55
    def activated(cls, union_membership: UnionMembership) -> bool:
×
56
        return ReplImplementation in union_membership
×
57

UNCOV
58
    shell = StrOption(
×
59
        default=None,
60
        help="Override the automatically-detected REPL program for the target(s) specified.",
61
    )
UNCOV
62
    args = ArgsListOption(
×
63
        example="-i helloworld/main.py",
64
        tool_name="the repl program",
65
        passthrough=True,
66
        extra_help="Currently supported only for the ipython shell.",
67
    )
UNCOV
68
    restartable = BoolOption(
×
69
        default=False,
70
        help="True if the REPL should be restarted if its inputs have changed.",
71
    )
72

73

UNCOV
74
class Repl(Goal):
×
UNCOV
75
    subsystem_cls = ReplSubsystem
×
UNCOV
76
    environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY
×
77

78

UNCOV
79
@dataclass(frozen=True)
×
UNCOV
80
class ReplRequest:
×
UNCOV
81
    digest: Digest
×
UNCOV
82
    args: tuple[str, ...]
×
UNCOV
83
    extra_env: FrozenDict[str, str]
×
UNCOV
84
    immutable_input_digests: FrozenDict[str, Digest]
×
UNCOV
85
    append_only_caches: FrozenDict[str, str]
×
UNCOV
86
    run_in_workspace: bool
×
87

UNCOV
88
    def __init__(
×
89
        self,
90
        *,
91
        digest: Digest,
92
        args: Iterable[str],
93
        extra_env: Mapping[str, str] | None = None,
94
        immutable_input_digests: Mapping[str, Digest] | None = None,
95
        append_only_caches: Mapping[str, str] | None = None,
96
        run_in_workspace: bool = True,
97
    ) -> None:
98
        object.__setattr__(self, "digest", digest)
×
99
        object.__setattr__(self, "args", tuple(args))
×
100
        object.__setattr__(self, "extra_env", FrozenDict(extra_env or {}))
×
101
        object.__setattr__(
×
102
            self, "immutable_input_digests", FrozenDict(immutable_input_digests or {})
103
        )
104
        object.__setattr__(self, "append_only_caches", FrozenDict(append_only_caches or {}))
×
105
        object.__setattr__(self, "run_in_workspace", run_in_workspace)
×
106

107

UNCOV
108
@rule(polymorphic=True)
×
UNCOV
109
async def get_repl_request(
×
110
    impl: ReplImplementation,
111
    environment_name: EnvironmentName,
112
) -> ReplRequest:
113
    raise NotImplementedError()
×
114

115

UNCOV
116
@goal_rule
×
UNCOV
117
async def run_repl(
×
118
    console: Console,
119
    repl_subsystem: ReplSubsystem,
120
    specified_targets: FilteredTargets,
121
    union_membership: UnionMembership,
122
    complete_env: CompleteEnvironmentVars,
123
) -> Repl:
124
    await _warn_on_non_local_environments(specified_targets, "the `repl` goal")
×
125

126
    # TODO: When we support multiple languages, detect the default repl to use based
127
    #  on the targets.  For now we default to the python repl.
128
    repl_shell_name = repl_subsystem.shell or "python"
×
129
    implementations = {impl.name: impl for impl in union_membership[ReplImplementation]}
×
130
    repl_implementation_cls = implementations.get(repl_shell_name)
×
131
    if repl_implementation_cls is None:
×
132
        available = sorted(implementations.keys())
×
133
        console.print_stderr(
×
134
            softwrap(
135
                f"""
136
                {repr(repl_shell_name)} is not a registered REPL. Available REPLs (which may
137
                be specified through the option `--repl-shell`): {available}
138
                """
139
            )
140
        )
141
        return Repl(-1)
×
142

143
    if repl_subsystem.args and not repl_implementation_cls.supports_args:
×
144
        console.print_stderr(
×
145
            softwrap(
146
                f"""
147
                REPL goal does not support passing args to a {repr(repl_shell_name)} shell.
148
                """
149
            )
150
        )
151
        return Repl(-1)
×
152

153
    repl_impl = repl_implementation_cls(targets=specified_targets)
×
154
    request = await get_repl_request(**implicitly({repl_impl: ReplImplementation}))
×
155

156
    env = {**complete_env, **request.extra_env}
×
157

158
    result = await run_interactive_process(
×
159
        InteractiveProcess(
160
            argv=(*request.args, *repl_subsystem.args),
161
            env=env,
162
            input_digest=request.digest,
163
            run_in_workspace=request.run_in_workspace,
164
            restartable=repl_subsystem.restartable,
165
            immutable_input_digests=request.immutable_input_digests,
166
            append_only_caches=request.append_only_caches,
167
        ),
168
    )
169
    return Repl(result.exit_code)
×
170

171

UNCOV
172
def rules():
×
UNCOV
173
    return collect_rules()
×
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