Skip to content

Commit cff5c81

Browse files
committed
refactor: implement robot.toml config file and runner
1 parent f37bab6 commit cff5c81

File tree

17 files changed

+415
-128
lines changed

17 files changed

+415
-128
lines changed

.vscode/launch.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@
4545
"justMyCode": false,
4646
"cwd": "${workspaceFolder}/tests/robotcode/language_server/robotframework/parts/data",
4747
"args": [
48-
48+
//"-v",
4949
"-p",
5050
"firefox",
5151
"--dry",
52-
"run"
52+
"run",
53+
"."
5354
]
5455
},
5556
{

etc/robot.toml.json

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
"type": "string"
5858
},
5959
"detached": {
60-
"default": false,
6160
"description": "If the profile should be detached.\"\nDetached means it is not inherited from the main profile.\n",
6261
"title": "Detached",
6362
"type": "boolean"
@@ -127,19 +126,12 @@
127126
"type": "object"
128127
},
129128
"paths": {
130-
"anyOf": [
131-
{
132-
"type": "string"
133-
},
134-
{
135-
"items": {
136-
"type": "string"
137-
},
138-
"type": "array"
139-
}
140-
],
141129
"description": "Paths to test data. If no paths are given at the command line this value is used.\n",
142-
"title": "Paths"
130+
"items": {
131+
"type": "string"
132+
},
133+
"title": "Paths",
134+
"type": "array"
143135
},
144136
"pre-rebot-modifiers": {
145137
"additionalProperties": {
@@ -288,19 +280,12 @@
288280
"type": "object"
289281
},
290282
"paths": {
291-
"anyOf": [
292-
{
293-
"type": "string"
294-
},
295-
{
296-
"items": {
297-
"type": "string"
298-
},
299-
"type": "array"
300-
}
301-
],
302283
"description": "Paths to test data. If no paths are given at the command line this value is used.\n",
303-
"title": "Paths"
284+
"items": {
285+
"type": "string"
286+
},
287+
"title": "Paths",
288+
"type": "array"
304289
},
305290
"pre-rebot-modifiers": {
306291
"additionalProperties": {

hatch.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dependencies = [
2222
"semantic-version",
2323
"robotremoteserver",
2424
"pydantic",
25+
"tomli_w"
2526
]
2627
features = ["yaml", "rest", "lint", "tidy"]
2728
pre-install-commands = ["python ./scripts/install_packages.py"]

packages/analyze/robotcode/analyze/cli/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@
2222
def analyze(
2323
ctx: click.Context,
2424
) -> Union[str, int, None]:
25-
"""Analyzes Robot Framework Tests."""
25+
"""Analyzes a Robot Framework project."""
2626
click.echo("Not implemented yet")
2727
return 0

packages/core/robotcode/core/dataclasses.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,12 @@ def from_dict(
221221
t is Any
222222
or t is Ellipsis # type: ignore
223223
or isinstance(value, origin or t)
224-
or (isinstance(value, Sequence) and args and issubclass(origin or t, Sequence))
224+
or (
225+
isinstance(value, Sequence)
226+
and args
227+
and issubclass(origin or t, Sequence)
228+
and not isinstance(value, str)
229+
)
225230
):
226231
if isinstance(value, Mapping):
227232
return cast(
@@ -378,7 +383,12 @@ def _validate_types(expected_types: Union[type, Tuple[type, ...], None], value:
378383
t is Any
379384
or t is Ellipsis # type: ignore
380385
or isinstance(value, origin or t)
381-
or (isinstance(value, Sequence) and args and issubclass(origin or t, Sequence))
386+
or (
387+
isinstance(value, Sequence)
388+
and args
389+
and issubclass(origin or t, Sequence)
390+
and not isinstance(value, str)
391+
)
382392
):
383393
if isinstance(value, Mapping):
384394
r = list(
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
1-
from typing import Any, Callable, TypeVar, cast
1+
from dataclasses import dataclass
2+
from pathlib import Path
3+
from typing import Any, Callable, List, Optional, TypeVar, cast
24

5+
import click
36
import pluggy
47

8+
__all__ = ["hookimpl", "CommonConfig", "pass_common_config"]
9+
510
F = TypeVar("F", bound=Callable[..., Any])
611
hookimpl = cast(Callable[[F], F], pluggy.HookimplMarker("robotcode"))
12+
13+
14+
@dataclass
15+
class CommonConfig:
16+
config_file: Optional[Path] = None
17+
profiles: Optional[List[str]] = None
18+
dry: bool = False
19+
verbose: bool = False
20+
21+
22+
pass_common_config = click.make_pass_decorator(CommonConfig, ensure=True)

packages/robot/robotcode/robot/config/loader.py

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import sys
2+
from enum import Enum
3+
from pathlib import Path
4+
from typing import Any, Dict, Optional, Sequence, Tuple, Union, cast
25

36
from robotcode.core.dataclasses import from_dict
47

@@ -10,7 +13,100 @@
1013

1114
from .model import MainProfile
1215

16+
PYPROJECT_TOML = "pyproject.toml"
17+
ROBOT_TOML = "robot.toml"
18+
LOCAL_ROBOT_TOML = ".robot.toml"
1319

14-
def create_from_toml(__s: str) -> MainProfile:
20+
21+
class DiscoverdBy(str, Enum):
22+
GIT = ".git directory"
23+
HG = "hg"
24+
PYPROJECT_TOML = PYPROJECT_TOML
25+
ROBOT_TOML = ROBOT_TOML
26+
LOCAL_ROBOT_TOML = LOCAL_ROBOT_TOML
27+
NOT_FOUND = "not found"
28+
29+
30+
class ConfigType(str, Enum):
31+
PYPROJECT_TOML = PYPROJECT_TOML
32+
ROBOT_TOML = ROBOT_TOML
33+
LOCAL_ROBOT_TOML = LOCAL_ROBOT_TOML
34+
35+
36+
def loads_config_from_robot_toml(__s: str) -> MainProfile:
1537
dict_data = tomllib.loads(__s)
1638
return from_dict(dict_data, MainProfile)
39+
40+
41+
def loads_config_from_pyproject_toml(__s: str) -> MainProfile:
42+
dict_data = tomllib.loads(__s)
43+
44+
return from_dict(dict_data.get("tool", {}).get("robot", {}), MainProfile)
45+
46+
47+
def _load_config_data_from_path(__path: Path) -> Dict[str, Any]:
48+
if __path.name == PYPROJECT_TOML:
49+
return cast(Dict[str, Any], tomllib.loads(__path.read_text("utf-8")).get("tool", {}).get("robot", {}))
50+
51+
if __path.name == ROBOT_TOML or __path.name == LOCAL_ROBOT_TOML:
52+
return tomllib.loads(__path.read_text("utf-8"))
53+
54+
raise ValueError(f"Unknown config file name: {__path.name}")
55+
56+
57+
def load_config_from_path(*__paths: Union[Path, Tuple[Path, ConfigType]]) -> MainProfile:
58+
config_data = {}
59+
for __path in __paths:
60+
config_data.update(_load_config_data_from_path(__path if isinstance(__path, Path) else __path[0]))
61+
62+
return from_dict(config_data, MainProfile)
63+
64+
65+
def find_project_root(*sources: Union[str, Path]) -> Tuple[Optional[Path], DiscoverdBy]:
66+
if not sources:
67+
sources = (str(Path.cwd().resolve()),)
68+
69+
path_srcs = [Path(Path.cwd(), src).resolve() for src in sources]
70+
71+
src_parents = [list(path.parents) + ([path] if path.is_dir() else []) for path in path_srcs]
72+
73+
common_base = max(
74+
set.intersection(*(set(parents) for parents in src_parents)),
75+
key=lambda path: path.parts,
76+
)
77+
78+
for directory in (common_base, *common_base.parents):
79+
if (directory / LOCAL_ROBOT_TOML).is_file():
80+
return directory, DiscoverdBy.LOCAL_ROBOT_TOML
81+
82+
if (directory / ROBOT_TOML).is_file():
83+
return directory, DiscoverdBy.ROBOT_TOML
84+
85+
if (directory / PYPROJECT_TOML).is_file():
86+
return directory, DiscoverdBy.PYPROJECT_TOML
87+
88+
if (directory / ".git").exists():
89+
return directory, DiscoverdBy.GIT
90+
91+
if (directory / ".hg").is_dir():
92+
return directory, DiscoverdBy.HG
93+
94+
return None, DiscoverdBy.NOT_FOUND
95+
96+
97+
def get_config_files_from_folder(folder: Path) -> Sequence[Tuple[Path, ConfigType]]:
98+
result = []
99+
100+
pyproject_toml = folder / PYPROJECT_TOML
101+
if pyproject_toml.is_file():
102+
result.append((pyproject_toml, ConfigType.PYPROJECT_TOML))
103+
104+
robot_toml = folder / ROBOT_TOML
105+
if robot_toml.is_file():
106+
result.append((robot_toml, ConfigType.ROBOT_TOML))
107+
108+
local_robot_toml = folder / LOCAL_ROBOT_TOML
109+
if local_robot_toml.is_file():
110+
result.append((local_robot_toml, ConfigType.LOCAL_ROBOT_TOML))
111+
112+
return result

0 commit comments

Comments
 (0)