robotcode-plugin 2.6.2__tar.gz → 2.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/.gitignore +4 -1
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/PKG-INFO +3 -2
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/pyproject.toml +1 -0
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/src/robotcode/plugin/__init__.py +24 -2
- robotcode_plugin-2.8.0/src/robotcode/plugin/__version__.py +1 -0
- robotcode_plugin-2.8.0/src/robotcode/plugin/click_helper/exit_codes.py +21 -0
- robotcode_plugin-2.8.0/src/robotcode/plugin/click_helper/wrappable.py +110 -0
- robotcode_plugin-2.6.2/src/robotcode/plugin/__version__.py +0 -1
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/README.md +0 -0
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/src/robotcode/plugin/_agent_detection.py +0 -0
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/src/robotcode/plugin/click_helper/aliases.py +0 -0
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/src/robotcode/plugin/click_helper/options.py +0 -0
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/src/robotcode/plugin/click_helper/types.py +0 -0
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/src/robotcode/plugin/manager.py +0 -0
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/src/robotcode/plugin/py.typed +0 -0
- {robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/src/robotcode/plugin/specs.py +0 -0
|
@@ -279,6 +279,9 @@ dist
|
|
|
279
279
|
# vuepress build output
|
|
280
280
|
.vuepress/dist
|
|
281
281
|
|
|
282
|
+
# Astro (documentation site)
|
|
283
|
+
docs/.astro/
|
|
284
|
+
|
|
282
285
|
# Serverless directories
|
|
283
286
|
.serverless/
|
|
284
287
|
|
|
@@ -339,7 +342,7 @@ bundled/libs
|
|
|
339
342
|
# .agents
|
|
340
343
|
.agents/
|
|
341
344
|
skills-lock.json
|
|
342
|
-
|
|
345
|
+
#.claude
|
|
343
346
|
|
|
344
347
|
# sarif files
|
|
345
348
|
/**/*.sarif.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: robotcode-plugin
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.0
|
|
4
4
|
Summary: Some classes for RobotCode plugin management
|
|
5
5
|
Project-URL: Donate, https://opencollective.com/robotcode
|
|
6
6
|
Project-URL: Documentation, https://github.com/robotcodedev/robotcode#readme
|
|
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
22
23
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
24
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
24
25
|
Classifier: Topic :: Utilities
|
|
@@ -18,6 +18,7 @@ classifiers = [
|
|
|
18
18
|
"Programming Language :: Python :: 3.12",
|
|
19
19
|
"Programming Language :: Python :: 3.13",
|
|
20
20
|
"Programming Language :: Python :: 3.14",
|
|
21
|
+
"Programming Language :: Python :: 3.15",
|
|
21
22
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
22
23
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
23
24
|
"Operating System :: OS Independent",
|
|
@@ -83,6 +83,8 @@ class CommonConfig:
|
|
|
83
83
|
colored_output: ColoredOutput = ColoredOutput.AUTO
|
|
84
84
|
default_paths: Optional[Sequence[str]] = None
|
|
85
85
|
launcher_script: Optional[str] = None
|
|
86
|
+
wrapper: Optional[str] = None
|
|
87
|
+
no_wrapper: bool = False
|
|
86
88
|
output_format: Optional[OutputFormat] = None
|
|
87
89
|
pager: Optional[bool] = None
|
|
88
90
|
log_enabled: bool = False
|
|
@@ -136,6 +138,7 @@ def _get_deep_markdown_cls() -> "type[Markdown]":
|
|
|
136
138
|
TableHeaderElement,
|
|
137
139
|
TableRowElement,
|
|
138
140
|
)
|
|
141
|
+
from rich.segment import Segment
|
|
139
142
|
from rich.text import Text
|
|
140
143
|
|
|
141
144
|
class LeftHeading(Heading):
|
|
@@ -196,6 +199,25 @@ def _get_deep_markdown_cls() -> "type[Markdown]":
|
|
|
196
199
|
parser.options.maxNesting = 1000
|
|
197
200
|
self.parsed = parser.parse(markup)
|
|
198
201
|
|
|
202
|
+
def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
|
|
203
|
+
# rich pads every line with spaces to the full console width. A
|
|
204
|
+
# pager that reserves columns (bat's line numbers and grid) wraps
|
|
205
|
+
# those full-width lines, and every wrapped remainder shows up
|
|
206
|
+
# as an empty line. Drop the padding unless it carries a
|
|
207
|
+
# background colour (code blocks draw a box with it).
|
|
208
|
+
for line in Segment.split_lines(cast(Iterable[Segment], super().__rich_console__(console, options))):
|
|
209
|
+
while line and not line[-1].control:
|
|
210
|
+
text, style, _ = line[-1]
|
|
211
|
+
if style is not None and style.bgcolor is not None:
|
|
212
|
+
break
|
|
213
|
+
text = text.rstrip(" ")
|
|
214
|
+
if text:
|
|
215
|
+
line[-1] = Segment(text, style)
|
|
216
|
+
break
|
|
217
|
+
line.pop()
|
|
218
|
+
yield from line
|
|
219
|
+
yield Segment.line()
|
|
220
|
+
|
|
199
221
|
_deep_markdown_cls = DeepMarkdown
|
|
200
222
|
return DeepMarkdown
|
|
201
223
|
|
|
@@ -341,7 +363,7 @@ class Application:
|
|
|
341
363
|
if dataclasses.is_dataclass(data)
|
|
342
364
|
else data
|
|
343
365
|
if isinstance(data, dict)
|
|
344
|
-
else {data: data}
|
|
366
|
+
else {"data": data}
|
|
345
367
|
)
|
|
346
368
|
|
|
347
369
|
if text is None:
|
|
@@ -349,7 +371,7 @@ class Application:
|
|
|
349
371
|
text = as_json(
|
|
350
372
|
data,
|
|
351
373
|
indent=format == OutputFormat.JSON_INDENT,
|
|
352
|
-
compact=format == OutputFormat.
|
|
374
|
+
compact=format == OutputFormat.JSON,
|
|
353
375
|
)
|
|
354
376
|
else:
|
|
355
377
|
text = str(data)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.8.0"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
_ROBOT_EXIT_CODES_ATTR = "__robotcode_robot_exit_codes__"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def robot_exit_codes(cmd: click.Command) -> click.Command:
|
|
7
|
+
"""Mark a CLI command that passes on the exit code of a Robot Framework tool.
|
|
8
|
+
|
|
9
|
+
Such a command answers with Robot Framework's exit codes, like the `wrappable`
|
|
10
|
+
commands that run Robot Framework, so an error robotcode reports itself for it
|
|
11
|
+
exits with Robot Framework's 252 instead of Click's 1 or 2. Mark the commands
|
|
12
|
+
that pass on a tool's exit code without running tests - rebot, libdoc,
|
|
13
|
+
testdoc; `wrappable` commands are covered already.
|
|
14
|
+
"""
|
|
15
|
+
setattr(cmd, _ROBOT_EXIT_CODES_ATTR, True)
|
|
16
|
+
return cmd
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def has_robot_exit_codes(cmd: click.Command) -> bool:
|
|
20
|
+
"""Return whether `cmd` was marked with :func:`robot_exit_codes`."""
|
|
21
|
+
return bool(getattr(cmd, _ROBOT_EXIT_CODES_ATTR, False))
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shlex
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Optional, Sequence
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from robotcode.plugin import Application
|
|
11
|
+
|
|
12
|
+
# Set in the environment once a wrapper has been applied to the process tree.
|
|
13
|
+
# Prevents infinite re-exec recursion and lets an outer layer (e.g. the VS Code
|
|
14
|
+
# debug launcher applying its own wrapper) suppress the CLI/profile re-exec.
|
|
15
|
+
WRAPPER_APPLIED_ENV = "ROBOTCODE_WRAPPER_APPLIED"
|
|
16
|
+
|
|
17
|
+
_WRAPPABLE_ATTR = "__robotcode_wrappable__"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def wrappable(cmd: click.Command) -> click.Command:
|
|
21
|
+
"""Mark a CLI command as *wrappable*.
|
|
22
|
+
|
|
23
|
+
When a `wrapper` (session) command is configured, a wrappable command is
|
|
24
|
+
re-executed through it, because it executes Robot Framework (and may, for
|
|
25
|
+
example, need to run inside a specific X11/Wayland session). Commands that
|
|
26
|
+
do not execute Robot Framework - language server, discovery, libdoc, … -
|
|
27
|
+
must NOT be marked.
|
|
28
|
+
|
|
29
|
+
The re-execution happens when the command loads its configuration with
|
|
30
|
+
`load_robot_profile` or `handle_robot_options` of `robotcode.runner.cli.robot`,
|
|
31
|
+
so a marked command must do that first, before it starts a server, an
|
|
32
|
+
interpreter or a debugger. A marked command that loads its configuration in
|
|
33
|
+
another way runs without the wrapper.
|
|
34
|
+
"""
|
|
35
|
+
setattr(cmd, _WRAPPABLE_ATTR, True)
|
|
36
|
+
return cmd
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def is_wrappable(cmd: click.Command) -> bool:
|
|
40
|
+
"""Return whether `cmd` was marked with :func:`wrappable`."""
|
|
41
|
+
return bool(getattr(cmd, _WRAPPABLE_ATTR, False))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def invoked_command_is_wrappable(ctx: click.Context) -> bool:
|
|
45
|
+
"""Return whether the command that the root group invokes is marked with :func:`wrappable`.
|
|
46
|
+
|
|
47
|
+
A command that such a command runs itself, such as `robot` in `debug`, belongs to it.
|
|
48
|
+
"""
|
|
49
|
+
root = ctx.find_root()
|
|
50
|
+
sub_name = root.invoked_subcommand
|
|
51
|
+
if not sub_name or not isinstance(root.command, click.Group):
|
|
52
|
+
return False
|
|
53
|
+
# Aliases resolve to the same command object, so this is alias-safe and needs
|
|
54
|
+
# no central name list.
|
|
55
|
+
sub_cmd = root.command.get_command(root, sub_name)
|
|
56
|
+
return sub_cmd is not None and is_wrappable(sub_cmd)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def run_under_wrapper(app: "Application", profile_wrapper: Optional[Sequence[Any]]) -> None:
|
|
60
|
+
"""Re-execute this process through the configured ``wrapper`` command.
|
|
61
|
+
|
|
62
|
+
A wrappable command calls this once it has loaded its configuration, before it
|
|
63
|
+
starts anything. ``--wrapper`` overrides the ``wrapper`` of the profile
|
|
64
|
+
(``profile_wrapper``); ``--no-wrapper`` disables wrapping for this run. The
|
|
65
|
+
guard env var prevents infinite recursion and lets an outer layer suppress the
|
|
66
|
+
re-exec by setting it itself. Returns when no wrapper applies or for a dry run.
|
|
67
|
+
"""
|
|
68
|
+
if app.config.no_wrapper or os.environ.get(WRAPPER_APPLIED_ENV):
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
# `--wrapper` overrides the profile's `wrapper`. The evaluated profile turns
|
|
72
|
+
# StringExpression entries into plain strings.
|
|
73
|
+
if app.config.wrapper:
|
|
74
|
+
wrapper = shlex.split(app.config.wrapper)
|
|
75
|
+
elif profile_wrapper:
|
|
76
|
+
wrapper = [str(w) for w in profile_wrapper]
|
|
77
|
+
else:
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
# A dry run must not start the wrapper: it would set up and tear down the
|
|
81
|
+
# real environment. Show it instead; the command itself then prints its own
|
|
82
|
+
# dry-run output.
|
|
83
|
+
if app.config.dry:
|
|
84
|
+
app.echo(f"Dry run, not executing the wrapper. Would run robotcode under: {' '.join(wrapper)}")
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
# Rebuild the invocation the way this process was actually started, so the
|
|
88
|
+
# re-exec finds robotcode even when it runs from a bundled path (VS Code) and
|
|
89
|
+
# is not installed in the interpreter. `launcher_script` is set only when we
|
|
90
|
+
# were started through a bundled entry (its `__main__` sets it) or an explicit
|
|
91
|
+
# `--launcher-script`; otherwise robotcode is importable directly and
|
|
92
|
+
# `-m robotcode.cli` is correct. We deliberately do NOT consult the
|
|
93
|
+
# ROBOTCODE_BUNDLED_ROBOTCODE_MAIN env var: VS Code sets it in every terminal,
|
|
94
|
+
# so a directly started robotcode would be diverted to the bundled copy.
|
|
95
|
+
# Mirrors the debug launcher's `debugger_script` handling.
|
|
96
|
+
entry = [str(app.config.launcher_script)] if app.config.launcher_script else ["-m", "robotcode.cli"]
|
|
97
|
+
command = [*wrapper, sys.executable, *entry, *sys.argv[1:]]
|
|
98
|
+
app.verbose(lambda: "Running under wrapper: " + " ".join(command))
|
|
99
|
+
|
|
100
|
+
# Run robotcode again under the wrapper; the guard env stops the spawned
|
|
101
|
+
# robotcode from wrapping a second time. On POSIX we replace this process with
|
|
102
|
+
# execvp (no extra process); Windows has no such exec, so there we spawn the
|
|
103
|
+
# command, wait, and forward its exit code.
|
|
104
|
+
os.environ[WRAPPER_APPLIED_ENV] = "1"
|
|
105
|
+
try:
|
|
106
|
+
if sys.platform == "win32":
|
|
107
|
+
sys.exit(subprocess.run(command).returncode)
|
|
108
|
+
os.execvp(command[0], command)
|
|
109
|
+
except OSError as e:
|
|
110
|
+
raise click.ClickException(f"Failed to execute wrapper command {wrapper!r}: {e}") from e
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.6.2"
|
|
File without changes
|
|
File without changes
|
{robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/src/robotcode/plugin/click_helper/aliases.py
RENAMED
|
File without changes
|
{robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/src/robotcode/plugin/click_helper/options.py
RENAMED
|
File without changes
|
{robotcode_plugin-2.6.2 → robotcode_plugin-2.8.0}/src/robotcode/plugin/click_helper/types.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|