robotframework-harness 1.0.0__py3-none-any.whl
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.
- rf_harness/__init__.py +3 -0
- rf_harness/_assets/.claude-plugin/plugin.json +10 -0
- rf_harness/_assets/bin/rfh-launch +60 -0
- rf_harness/_assets/bin/rfh-launch.cmd +19 -0
- rf_harness/_assets/bin/rfh_launch.py +719 -0
- rf_harness/_assets/hooks/hooks.json +84 -0
- rf_harness/_assets/skills/rf-harness/SKILL.md +78 -0
- rf_harness/_assets/skills/rf-harness/references/browser.md +157 -0
- rf_harness/_assets/skills/rf-harness/references/debug.md +39 -0
- rf_harness/_assets/skills/rf-harness/references/explore.md +52 -0
- rf_harness/_assets/skills/rf-harness/references/requests.md +37 -0
- rf_harness/_assets/skills/rf-harness/references/results.md +34 -0
- rf_harness/_assets/skills/rf-harness/references/selenium.md +40 -0
- rf_harness/_assets/skills/rf-harness/references/waits.md +28 -0
- rf_harness/_assets/variants/codex/hooks.json +72 -0
- rf_harness/_assets/variants/copilot/hooks.json +53 -0
- rf_harness/_assets/variants/opencode/plugins/rf-harness.js +130 -0
- rf_harness/config.py +255 -0
- rf_harness/installer/__init__.py +1 -0
- rf_harness/installer/_assets.py +73 -0
- rf_harness/installer/adapters/__init__.py +37 -0
- rf_harness/installer/adapters/_base.py +189 -0
- rf_harness/installer/adapters/claude_code.py +88 -0
- rf_harness/installer/adapters/codex.py +108 -0
- rf_harness/installer/adapters/copilot_cli.py +89 -0
- rf_harness/installer/adapters/opencode.py +62 -0
- rf_harness/installer/adapters/vscode.py +56 -0
- rf_harness/installer/cli.py +490 -0
- rf_harness/installer/codex_trust.py +190 -0
- rf_harness/installer/coexist.py +211 -0
- rf_harness/installer/demo_site/index.html +28 -0
- rf_harness/installer/doctor.py +343 -0
- rf_harness/installer/envfind.py +354 -0
- rf_harness/installer/executor.py +288 -0
- rf_harness/installer/homes.py +102 -0
- rf_harness/installer/manifest.py +204 -0
- rf_harness/installer/prompt.py +58 -0
- rf_harness/installer/transforms.py +483 -0
- rf_harness/runtime/__init__.py +1 -0
- rf_harness/runtime/analyzer.py +132 -0
- rf_harness/runtime/data/failure_rules.json +77 -0
- rf_harness/runtime/data/kwnames_cache.json +1 -0
- rf_harness/runtime/data/repl_format_rules.json +455 -0
- rf_harness/runtime/data/robocop-agent.toml +58 -0
- rf_harness/runtime/desktop/__init__.py +87 -0
- rf_harness/runtime/desktop/_env.py +134 -0
- rf_harness/runtime/desktop/decision.py +616 -0
- rf_harness/runtime/desktop/focus.py +1094 -0
- rf_harness/runtime/desktop/guidance.py +91 -0
- rf_harness/runtime/desktop/launch_env.py +562 -0
- rf_harness/runtime/desktop/safety.py +509 -0
- rf_harness/runtime/desktop/signals.py +321 -0
- rf_harness/runtime/docs.py +190 -0
- rf_harness/runtime/docsgen.py +174 -0
- rf_harness/runtime/engine.py +1000 -0
- rf_harness/runtime/execd.py +1131 -0
- rf_harness/runtime/guards.py +294 -0
- rf_harness/runtime/hints.py +1523 -0
- rf_harness/runtime/hook.py +1897 -0
- rf_harness/runtime/hookcore.py +832 -0
- rf_harness/runtime/kwdoc.py +285 -0
- rf_harness/runtime/kwdocs.py +286 -0
- rf_harness/runtime/leanfmt.py +451 -0
- rf_harness/runtime/lintd.py +423 -0
- rf_harness/runtime/livetmo.py +385 -0
- rf_harness/runtime/pagestate.py +489 -0
- rf_harness/runtime/portable.py +191 -0
- rf_harness/runtime/procs.py +106 -0
- rf_harness/runtime/profiles.py +144 -0
- rf_harness/runtime/proto.py +120 -0
- rf_harness/runtime/pwlog.py +89 -0
- rf_harness/runtime/replfmt.py +556 -0
- rf_harness/runtime/rfh_repl_listener.py +244 -0
- rf_harness/runtime/richfmt.py +301 -0
- rf_harness/runtime/robotcode_adapter.py +157 -0
- rf_harness/runtime/rthooks.py +623 -0
- rf_harness/runtime/smoke.py +113 -0
- robotframework_harness-1.0.0.dist-info/METADATA +85 -0
- robotframework_harness-1.0.0.dist-info/RECORD +82 -0
- robotframework_harness-1.0.0.dist-info/WHEEL +4 -0
- robotframework_harness-1.0.0.dist-info/entry_points.txt +3 -0
- robotframework_harness-1.0.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""The only module that touches robotcode internals (pinned to robotcode >=2.7,<2.8; contract tests in
|
|
2
|
+
tests/runtime/test_robotcode_adapter.py).
|
|
3
|
+
|
|
4
|
+
robotcode has no stable Python API for what the harness needs, so every internal name the runtime uses is listed here
|
|
5
|
+
once and re-exported lazily (PEP 562): `from .robotcode_adapter import BaseInterpreter` imports only
|
|
6
|
+
`robotcode.repl.base_interpreter`, so the live-session daemon does not pay for the analyzer and vice versa.
|
|
7
|
+
|
|
8
|
+
Used for:
|
|
9
|
+
* the live session: `Application`, `BaseInterpreter` (subclassed by engine.Helper) and `run_repl` (one RF suite
|
|
10
|
+
whose body is fed from the helper's request queue);
|
|
11
|
+
* the static gate: `CodeAnalyzer` + `AnalyzeConfig`/`ModifiersConfig` + robot.toml loading
|
|
12
|
+
(`get_config_files`, `load_robot_config_from_path`) + LSP `Uri`/`FileEvent`/`FileChangeType` (analyzer.WarmAnalyzer,
|
|
13
|
+
lintd);
|
|
14
|
+
* keyword documentation: the `robotcode libdoc` command line (docsgen), run in a subprocess.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import importlib
|
|
20
|
+
import os
|
|
21
|
+
import subprocess
|
|
22
|
+
import sys
|
|
23
|
+
from collections.abc import Iterable, Sequence
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import TYPE_CHECKING, Any
|
|
26
|
+
|
|
27
|
+
SUPPORTED_RANGE = ((2, 7), (2, 8)) # >=2.7,<2.8, same as the `runtime` extra
|
|
28
|
+
|
|
29
|
+
_NAMES = {
|
|
30
|
+
# live session (REPL embedding)
|
|
31
|
+
"Application": "robotcode.plugin",
|
|
32
|
+
"BaseInterpreter": "robotcode.repl.base_interpreter",
|
|
33
|
+
"run_repl": "robotcode.repl.run",
|
|
34
|
+
# static analysis
|
|
35
|
+
"CodeAnalyzer": "robotcode.analyze.code.code_analyzer",
|
|
36
|
+
"AnalyzeConfig": "robotcode.analyze.config",
|
|
37
|
+
"ModifiersConfig": "robotcode.analyze.config",
|
|
38
|
+
"FileChangeType": "robotcode.core.lsp.types",
|
|
39
|
+
"FileEvent": "robotcode.core.lsp.types",
|
|
40
|
+
"Uri": "robotcode.core.uri",
|
|
41
|
+
"load_robot_config_from_path": "robotcode.robot.config.loader",
|
|
42
|
+
"get_config_files": "robotcode.robot.config.utils",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"AnalyzeConfig",
|
|
47
|
+
"Application",
|
|
48
|
+
"BaseInterpreter",
|
|
49
|
+
"CodeAnalyzer",
|
|
50
|
+
"FileChangeType",
|
|
51
|
+
"FileEvent",
|
|
52
|
+
"ModifiersConfig",
|
|
53
|
+
"Uri",
|
|
54
|
+
"get_config_files",
|
|
55
|
+
"load_robot_config_from_path",
|
|
56
|
+
"run_repl",
|
|
57
|
+
"SUPPORTED_RANGE",
|
|
58
|
+
"changed_file_events",
|
|
59
|
+
"document_uri",
|
|
60
|
+
"is_supported",
|
|
61
|
+
"libdoc",
|
|
62
|
+
"new_application",
|
|
63
|
+
"robotcode_command",
|
|
64
|
+
"robotcode_version",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
if TYPE_CHECKING: # pragma: no cover - static names for type checkers only
|
|
68
|
+
from robotcode.analyze.code.code_analyzer import CodeAnalyzer
|
|
69
|
+
from robotcode.analyze.config import AnalyzeConfig, ModifiersConfig
|
|
70
|
+
from robotcode.core.lsp.types import FileChangeType, FileEvent
|
|
71
|
+
from robotcode.core.uri import Uri
|
|
72
|
+
from robotcode.plugin import Application
|
|
73
|
+
from robotcode.repl.base_interpreter import BaseInterpreter
|
|
74
|
+
from robotcode.repl.run import run_repl
|
|
75
|
+
from robotcode.robot.config.loader import load_robot_config_from_path
|
|
76
|
+
from robotcode.robot.config.utils import get_config_files
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def __getattr__(name: str) -> Any:
|
|
80
|
+
module = _NAMES.get(name)
|
|
81
|
+
if module is None:
|
|
82
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
83
|
+
value = getattr(importlib.import_module(module), name)
|
|
84
|
+
globals()[name] = value # cache: next access skips __getattr__
|
|
85
|
+
return value
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def __dir__() -> list[str]:
|
|
89
|
+
return sorted(set(globals()) | set(__all__))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# ------------------------------------------------------------------------------------------------ version
|
|
93
|
+
def robotcode_version() -> str:
|
|
94
|
+
"""Installed robotcode version ("" when robotcode is missing)."""
|
|
95
|
+
try:
|
|
96
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
97
|
+
except ImportError: # pragma: no cover
|
|
98
|
+
return ""
|
|
99
|
+
try:
|
|
100
|
+
return version("robotcode")
|
|
101
|
+
except PackageNotFoundError:
|
|
102
|
+
return ""
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def is_supported(ver: str | None = None) -> bool:
|
|
106
|
+
"""True when `ver` (default: the installed robotcode) lies in SUPPORTED_RANGE."""
|
|
107
|
+
ver = robotcode_version() if ver is None else ver
|
|
108
|
+
try:
|
|
109
|
+
major_minor = tuple(int(x) for x in ver.split(".")[:2])
|
|
110
|
+
except ValueError:
|
|
111
|
+
return False
|
|
112
|
+
lo, hi = SUPPORTED_RANGE
|
|
113
|
+
return len(major_minor) == 2 and lo <= major_minor < hi
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# ------------------------------------------------------------------------------------------------ live session
|
|
117
|
+
def new_application(root: Path | None = None) -> Any:
|
|
118
|
+
"""A robotcode `Application` rooted at `root` (default: cwd), as `robotcode repl` builds it."""
|
|
119
|
+
app = __getattr__("Application")()
|
|
120
|
+
app.config.root = Path(root or os.getcwd())
|
|
121
|
+
return app
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# ------------------------------------------------------------------------------------------------ static analysis
|
|
125
|
+
def document_uri(path: Path) -> Any:
|
|
126
|
+
"""Normalized LSP document URI of a file (key of the analyzer's document store)."""
|
|
127
|
+
return __getattr__("Uri").from_path(path).normalized()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def changed_file_events(paths: Iterable[str]) -> list[Any]:
|
|
131
|
+
"""`did_change_watched_files` events for files edited on disk."""
|
|
132
|
+
uri, event, kind = __getattr__("Uri"), __getattr__("FileEvent"), __getattr__("FileChangeType")
|
|
133
|
+
return [event(str(uri.from_path(p)), kind.CHANGED) for p in paths]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ------------------------------------------------------------------------------------------------ libdoc
|
|
137
|
+
def robotcode_command(python: str | None = None) -> list[str]:
|
|
138
|
+
"""argv prefix of the `robotcode` CLI next to `python` (default: this interpreter)."""
|
|
139
|
+
py = Path(python or sys.executable)
|
|
140
|
+
exe = py.parent / ("robotcode.exe" if os.name == "nt" else "robotcode")
|
|
141
|
+
return [str(exe)] if exe.is_file() else [str(py), "-m", "robotcode"]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def libdoc(args: Sequence[str], timeout: float = 60, python: str | None = None,
|
|
145
|
+
root: str | os.PathLike | None = None) -> subprocess.CompletedProcess:
|
|
146
|
+
"""`robotcode libdoc <args>` in a subprocess without PYTHONPATH (docs come from the project environment as a plain
|
|
147
|
+
run sees it), e.g. `libdoc(["--specdocformat", "raw", "Browser", "spec.json"])` or
|
|
148
|
+
`libdoc(["BuiltIn", "show", "Log"])`.
|
|
149
|
+
|
|
150
|
+
The project root is always passed explicitly (`--root`, default: the current directory): robotcode otherwise
|
|
151
|
+
derives it from the common base of its path arguments, which fails when they sit on different Windows drives and
|
|
152
|
+
can pick the wrong directory elsewhere (research trap G3)."""
|
|
153
|
+
env = dict(os.environ)
|
|
154
|
+
env.pop("PYTHONPATH", None)
|
|
155
|
+
base = str(Path(root) if root is not None else Path.cwd())
|
|
156
|
+
return subprocess.run([*robotcode_command(python), "--root", base, "libdoc", *args], capture_output=True,
|
|
157
|
+
text=True, timeout=timeout, env=env)
|