robotcode-robot 0.91.0__py3-none-any.whl → 0.93.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.
- robotcode/robot/__version__.py +1 -1
- robotcode/robot/config/loader.py +75 -21
- robotcode/robot/config/model.py +105 -100
- robotcode/robot/config/utils.py +4 -2
- robotcode/robot/diagnostics/diagnostics_modifier.py +80 -17
- robotcode/robot/diagnostics/document_cache_helper.py +25 -7
- robotcode/robot/diagnostics/entities.py +2 -2
- robotcode/robot/diagnostics/imports_manager.py +79 -64
- robotcode/robot/diagnostics/library_doc.py +25 -8
- robotcode/robot/diagnostics/namespace.py +105 -102
- robotcode/robot/diagnostics/workspace_config.py +17 -0
- robotcode/robot/utils/ast.py +34 -7
- {robotcode_robot-0.91.0.dist-info → robotcode_robot-0.93.0.dist-info}/METADATA +2 -2
- robotcode_robot-0.93.0.dist-info/RECORD +30 -0
- robotcode_robot-0.91.0.dist-info/RECORD +0 -30
- {robotcode_robot-0.91.0.dist-info → robotcode_robot-0.93.0.dist-info}/WHEEL +0 -0
- {robotcode_robot-0.91.0.dist-info → robotcode_robot-0.93.0.dist-info}/licenses/LICENSE.txt +0 -0
robotcode/robot/__version__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.
|
1
|
+
__version__ = "0.93.0"
|
robotcode/robot/config/loader.py
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
import sys
|
2
|
+
from dataclasses import fields, is_dataclass
|
2
3
|
from enum import Enum
|
3
4
|
from pathlib import Path
|
4
|
-
from typing import Any, Optional, Sequence, Tuple, Type, TypeVar, Union
|
5
|
+
from typing import Any, Callable, Dict, Optional, Sequence, Tuple, Type, TypeVar, Union
|
5
6
|
|
6
7
|
from robotcode.core.utils.dataclasses import from_dict
|
7
8
|
|
@@ -27,6 +28,7 @@ class DiscoverdBy(str, Enum):
|
|
27
28
|
LOCAL_ROBOT_TOML = ".robot.toml (local file)"
|
28
29
|
USER_DEFAULT_CONFIG_TOML = "robot.toml (user default config)"
|
29
30
|
NOT_FOUND = "not found"
|
31
|
+
COMMAND_LINE = "command line"
|
30
32
|
|
31
33
|
|
32
34
|
class ConfigType(str, Enum):
|
@@ -58,9 +60,9 @@ def load_robot_config_from_robot_toml_str(__s: str) -> RobotConfig:
|
|
58
60
|
|
59
61
|
|
60
62
|
def load_config_from_robot_toml_str(
|
61
|
-
config_type: Type[_ConfigType],
|
63
|
+
config_type: Type[_ConfigType], data: Union[str, Dict[str, Any]], tool_name: Optional[str] = None
|
62
64
|
) -> _ConfigType:
|
63
|
-
dict_data = tomllib.loads(
|
65
|
+
dict_data = tomllib.loads(data) if isinstance(data, str) else data
|
64
66
|
|
65
67
|
if tool_name:
|
66
68
|
try:
|
@@ -73,34 +75,39 @@ def load_config_from_robot_toml_str(
|
|
73
75
|
return from_dict(dict_data, config_type)
|
74
76
|
|
75
77
|
|
76
|
-
def load_config_from_pyproject_toml_str(
|
77
|
-
|
78
|
+
def load_config_from_pyproject_toml_str(
|
79
|
+
config_type: Type[_ConfigType], tool_name: str, data: Union[str, Dict[str, Any]]
|
80
|
+
) -> _ConfigType:
|
81
|
+
dict_data = tomllib.loads(data) if isinstance(data, str) else data
|
78
82
|
|
79
83
|
return from_dict(dict_data.get("tool", {}).get(tool_name, {}), config_type)
|
80
84
|
|
81
85
|
|
82
86
|
def _load_config_data_from_path(
|
83
87
|
config_type: Type[_ConfigType],
|
84
|
-
|
88
|
+
pyproject_toml_tool_name: str,
|
85
89
|
robot_toml_tool_name: Optional[str],
|
86
|
-
|
90
|
+
path: Path,
|
91
|
+
data: Optional[Dict[str, Any]] = None,
|
87
92
|
) -> _ConfigType:
|
88
93
|
try:
|
89
|
-
if
|
90
|
-
return load_config_from_pyproject_toml_str(
|
94
|
+
if path.name == PYPROJECT_TOML:
|
95
|
+
return load_config_from_pyproject_toml_str(
|
96
|
+
config_type, pyproject_toml_tool_name, path.read_text("utf-8") if data is None else data
|
97
|
+
)
|
91
98
|
|
92
|
-
if
|
99
|
+
if path.name == ROBOT_TOML or path.name == LOCAL_ROBOT_TOML or path.suffix == ".toml":
|
93
100
|
return load_config_from_robot_toml_str(
|
94
101
|
config_type,
|
95
|
-
|
102
|
+
path.read_text("utf-8") if data is None else data,
|
96
103
|
tool_name=robot_toml_tool_name,
|
97
104
|
)
|
98
105
|
raise TypeError("Unknown config file type.")
|
99
106
|
|
100
107
|
except ValueError as e:
|
101
|
-
raise ConfigValueError(
|
108
|
+
raise ConfigValueError(path, f'Parsing "{path}" failed: {e}') from e
|
102
109
|
except TypeError as e:
|
103
|
-
raise ConfigTypeError(
|
110
|
+
raise ConfigTypeError(path, f'Parsing "{path}" failed: {e}') from e
|
104
111
|
|
105
112
|
|
106
113
|
def get_default_config() -> RobotConfig:
|
@@ -113,39 +120,85 @@ def get_default_config() -> RobotConfig:
|
|
113
120
|
def load_config_from_path(
|
114
121
|
config_type: Type[_ConfigType],
|
115
122
|
*__paths: Union[Path, Tuple[Path, ConfigType]],
|
116
|
-
|
123
|
+
pyproject_toml_tool_name: str,
|
117
124
|
robot_toml_tool_name: Optional[str] = None,
|
125
|
+
extra_tools: Optional[Dict[str, Type[Any]]] = None,
|
126
|
+
verbose_callback: Optional[Callable[[str], None]] = None,
|
118
127
|
) -> _ConfigType:
|
119
128
|
result = config_type()
|
129
|
+
tools: Optional[Dict[str, Any]] = (
|
130
|
+
{} if extra_tools and is_dataclass(result) and any(f for f in fields(result) if f.name == "tool") else None
|
131
|
+
)
|
120
132
|
|
121
133
|
for __path in __paths:
|
122
134
|
if isinstance(__path, tuple):
|
123
135
|
path, c_type = __path
|
124
136
|
if path.name == "__no_user_config__.toml" and c_type == ConfigType.DEFAULT_CONFIG_TOML:
|
137
|
+
if verbose_callback:
|
138
|
+
verbose_callback("Load default configuration.")
|
125
139
|
result.add_options(get_default_config())
|
126
140
|
continue
|
127
141
|
|
142
|
+
if verbose_callback:
|
143
|
+
verbose_callback(f"Load configuration from {__path if isinstance(__path, Path) else __path[0]}")
|
144
|
+
|
145
|
+
p = __path if isinstance(__path, Path) else __path[0]
|
146
|
+
data = tomllib.loads(p.read_text("utf-8"))
|
147
|
+
|
128
148
|
result.add_options(
|
129
149
|
_load_config_data_from_path(
|
130
150
|
config_type,
|
131
|
-
|
151
|
+
pyproject_toml_tool_name,
|
132
152
|
robot_toml_tool_name,
|
133
|
-
|
153
|
+
p,
|
154
|
+
data,
|
134
155
|
)
|
135
156
|
)
|
136
157
|
|
158
|
+
if tools is not None and extra_tools:
|
159
|
+
for tool_name, tool_config in extra_tools.items():
|
160
|
+
if tool_name not in tools:
|
161
|
+
tools[tool_name] = tool_config()
|
162
|
+
|
163
|
+
tool = tools[tool_name]
|
164
|
+
tool.add_options(
|
165
|
+
_load_config_data_from_path(
|
166
|
+
tool_config,
|
167
|
+
tool_name,
|
168
|
+
tool_name,
|
169
|
+
p,
|
170
|
+
data,
|
171
|
+
)
|
172
|
+
)
|
173
|
+
if tools is not None:
|
174
|
+
setattr(result, "tool", tools)
|
175
|
+
|
137
176
|
return result
|
138
177
|
|
139
178
|
|
140
179
|
def load_robot_config_from_path(
|
141
180
|
*__paths: Union[Path, Tuple[Path, ConfigType]],
|
181
|
+
extra_tools: Optional[Dict[str, Type[Any]]] = None,
|
182
|
+
verbose_callback: Optional[Callable[[str], None]] = None,
|
142
183
|
) -> RobotConfig:
|
143
|
-
return load_config_from_path(
|
184
|
+
return load_config_from_path(
|
185
|
+
RobotConfig,
|
186
|
+
*__paths,
|
187
|
+
pyproject_toml_tool_name="robot",
|
188
|
+
extra_tools=extra_tools,
|
189
|
+
verbose_callback=verbose_callback,
|
190
|
+
)
|
144
191
|
|
145
192
|
|
146
193
|
def find_project_root(
|
147
194
|
*sources: Union[str, Path],
|
195
|
+
root_folder: Optional[Path] = None,
|
196
|
+
no_vcs: bool = False,
|
148
197
|
) -> Tuple[Optional[Path], DiscoverdBy]:
|
198
|
+
|
199
|
+
if root_folder:
|
200
|
+
return root_folder.absolute(), DiscoverdBy.COMMAND_LINE
|
201
|
+
|
149
202
|
if not sources:
|
150
203
|
sources = (str(Path.cwd().absolute()),)
|
151
204
|
|
@@ -168,11 +221,12 @@ def find_project_root(
|
|
168
221
|
if (directory / PYPROJECT_TOML).is_file():
|
169
222
|
return directory, DiscoverdBy.PYPROJECT_TOML
|
170
223
|
|
171
|
-
if
|
172
|
-
|
224
|
+
if not no_vcs:
|
225
|
+
if (directory / ".git").exists():
|
226
|
+
return directory, DiscoverdBy.GIT
|
173
227
|
|
174
|
-
|
175
|
-
|
228
|
+
if (directory / ".hg").is_dir():
|
229
|
+
return directory, DiscoverdBy.HG
|
176
230
|
|
177
231
|
return None, DiscoverdBy.NOT_FOUND
|
178
232
|
|
robotcode/robot/config/model.py
CHANGED
@@ -245,65 +245,6 @@ class BaseOptions(ValidateMixin):
|
|
245
245
|
def _decode_case(cls, s: str) -> str:
|
246
246
|
return s.replace("-", "_")
|
247
247
|
|
248
|
-
"""Base class for all options."""
|
249
|
-
|
250
|
-
def build_command_line(self) -> List[str]:
|
251
|
-
"""Build the arguments to pass to Robot Framework."""
|
252
|
-
result = []
|
253
|
-
|
254
|
-
sorted_fields = sorted(
|
255
|
-
(f for f in dataclasses.fields(self) if f.metadata.get("robot_priority", -1) > -1),
|
256
|
-
key=lambda f: f.metadata.get("robot_priority", 0),
|
257
|
-
)
|
258
|
-
|
259
|
-
def append_name(field: "dataclasses.Field[Any]", add_flag: Optional[str] = None) -> None:
|
260
|
-
if "robot_short_name" in field.metadata:
|
261
|
-
result.append(f"-{field.metadata['robot_short_name']}")
|
262
|
-
elif "robot_name" in field.metadata:
|
263
|
-
result.append(f"--{'no' if add_flag else ''}{field.metadata['robot_name']}")
|
264
|
-
|
265
|
-
for field in sorted_fields:
|
266
|
-
try:
|
267
|
-
value = getattr(self, field.name)
|
268
|
-
if value is None:
|
269
|
-
continue
|
270
|
-
|
271
|
-
if field.metadata.get("robot_is_flag", False):
|
272
|
-
if value is None or value == Flag.DEFAULT:
|
273
|
-
continue
|
274
|
-
|
275
|
-
append_name(
|
276
|
-
field,
|
277
|
-
bool(value) != field.metadata.get("robot_flag_default", True),
|
278
|
-
)
|
279
|
-
|
280
|
-
continue
|
281
|
-
|
282
|
-
if isinstance(value, list):
|
283
|
-
for item in value:
|
284
|
-
append_name(field)
|
285
|
-
if isinstance(item, dict):
|
286
|
-
for k, v in item.items():
|
287
|
-
result.append(f"{k}:{v}")
|
288
|
-
else:
|
289
|
-
result.append(str(item))
|
290
|
-
elif isinstance(value, dict):
|
291
|
-
for key, item in value.items():
|
292
|
-
append_name(field)
|
293
|
-
if isinstance(item, list):
|
294
|
-
str_item = [str(i) for i in item]
|
295
|
-
separator = ";" if any(True for s in str_item if ":" in s) else ":"
|
296
|
-
result.append(f"{key}{separator if item else ''}{separator.join(str_item)}")
|
297
|
-
else:
|
298
|
-
result.append(f"{key}:{item}")
|
299
|
-
else:
|
300
|
-
append_name(field)
|
301
|
-
result.append(str(value))
|
302
|
-
except EvaluationError as e:
|
303
|
-
raise ValueError(f"Evaluation of '{field.name}' failed: {e!s}") from e
|
304
|
-
|
305
|
-
return result
|
306
|
-
|
307
248
|
@staticmethod
|
308
249
|
def _verified_value(name: str, value: Any, types: Union[type, Tuple[type, ...]], target: Any) -> Any:
|
309
250
|
errors = validate_types(types, value)
|
@@ -315,6 +256,44 @@ class BaseOptions(ValidateMixin):
|
|
315
256
|
)
|
316
257
|
return value
|
317
258
|
|
259
|
+
def evaluated(
|
260
|
+
self,
|
261
|
+
verbose_callback: Optional[Callable[[Union[str, Callable[[], Any]]], None]] = None,
|
262
|
+
error_callback: Optional[Callable[[Union[str, Callable[[], Any]]], None]] = None,
|
263
|
+
) -> Self:
|
264
|
+
if verbose_callback is not None:
|
265
|
+
verbose_callback("Evaluating options")
|
266
|
+
|
267
|
+
result = dataclasses.replace(self)
|
268
|
+
for f in dataclasses.fields(result):
|
269
|
+
try:
|
270
|
+
if isinstance(getattr(result, f.name), Condition):
|
271
|
+
setattr(result, f.name, getattr(result, f.name).evaluate())
|
272
|
+
elif isinstance(getattr(result, f.name), Expression):
|
273
|
+
setattr(result, f.name, getattr(result, f.name).evaluate())
|
274
|
+
elif isinstance(getattr(result, f.name), list):
|
275
|
+
setattr(
|
276
|
+
result,
|
277
|
+
f.name,
|
278
|
+
[e.evaluate() if isinstance(e, Expression) else e for e in getattr(result, f.name)],
|
279
|
+
)
|
280
|
+
elif isinstance(getattr(result, f.name), dict):
|
281
|
+
setattr(
|
282
|
+
result,
|
283
|
+
f.name,
|
284
|
+
{
|
285
|
+
k: e.evaluate() if isinstance(e, Expression) else e
|
286
|
+
for k, e in getattr(result, f.name).items()
|
287
|
+
},
|
288
|
+
)
|
289
|
+
except EvaluationError as e:
|
290
|
+
message = f"Evaluation of '{f.name}' failed: {type(e).__name__}: {e}"
|
291
|
+
if error_callback is None:
|
292
|
+
raise ValueError(message) from e
|
293
|
+
error_callback(message)
|
294
|
+
|
295
|
+
return result
|
296
|
+
|
318
297
|
def add_options(self, config: "BaseOptions", combine_extends: bool = False) -> None:
|
319
298
|
type_hints = get_type_hints(type(self))
|
320
299
|
base_field_names = [f.name for f in dataclasses.fields(self)]
|
@@ -352,6 +331,8 @@ class BaseOptions(ValidateMixin):
|
|
352
331
|
setattr(self, old_field_name, [*old, *new])
|
353
332
|
elif isinstance(old, tuple):
|
354
333
|
setattr(self, old_field_name, (*old, *new))
|
334
|
+
elif isinstance(old, BaseOptions):
|
335
|
+
old.add_options(new)
|
355
336
|
else:
|
356
337
|
setattr(self, old_field_name, new)
|
357
338
|
continue
|
@@ -370,41 +351,65 @@ class BaseOptions(ValidateMixin):
|
|
370
351
|
if new is not None:
|
371
352
|
setattr(self, f.name, new)
|
372
353
|
|
373
|
-
def evaluated(
|
374
|
-
self,
|
375
|
-
verbose_callback: Optional[Callable[[Union[str, Callable[[], Any]]], None]] = None,
|
376
|
-
error_callback: Optional[Callable[[Union[str, Callable[[], Any]]], None]] = None,
|
377
|
-
) -> Self:
|
378
|
-
if verbose_callback is not None:
|
379
|
-
verbose_callback("Evaluating options")
|
380
354
|
|
381
|
-
|
382
|
-
|
355
|
+
@dataclass
|
356
|
+
class RobotBaseOptions(BaseOptions):
|
357
|
+
"""Base class for all options."""
|
358
|
+
|
359
|
+
def build_command_line(self) -> List[str]:
|
360
|
+
"""Build the arguments to pass to Robot Framework."""
|
361
|
+
result = []
|
362
|
+
|
363
|
+
sorted_fields = sorted(
|
364
|
+
(f for f in dataclasses.fields(self) if f.metadata.get("robot_priority", -1) > -1),
|
365
|
+
key=lambda f: f.metadata.get("robot_priority", 0),
|
366
|
+
)
|
367
|
+
|
368
|
+
def append_name(field: "dataclasses.Field[Any]", add_flag: Optional[str] = None) -> None:
|
369
|
+
if "robot_short_name" in field.metadata:
|
370
|
+
result.append(f"-{field.metadata['robot_short_name']}")
|
371
|
+
elif "robot_name" in field.metadata:
|
372
|
+
result.append(f"--{'no' if add_flag else ''}{field.metadata['robot_name']}")
|
373
|
+
|
374
|
+
for field in sorted_fields:
|
383
375
|
try:
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
setattr(
|
396
|
-
result,
|
397
|
-
f.name,
|
398
|
-
{
|
399
|
-
k: e.evaluate() if isinstance(e, Expression) else e
|
400
|
-
for k, e in getattr(result, f.name).items()
|
401
|
-
},
|
376
|
+
value = getattr(self, field.name)
|
377
|
+
if value is None:
|
378
|
+
continue
|
379
|
+
|
380
|
+
if field.metadata.get("robot_is_flag", False):
|
381
|
+
if value is None or value == Flag.DEFAULT:
|
382
|
+
continue
|
383
|
+
|
384
|
+
append_name(
|
385
|
+
field,
|
386
|
+
bool(value) != field.metadata.get("robot_flag_default", True),
|
402
387
|
)
|
388
|
+
|
389
|
+
continue
|
390
|
+
|
391
|
+
if isinstance(value, list):
|
392
|
+
for item in value:
|
393
|
+
append_name(field)
|
394
|
+
if isinstance(item, dict):
|
395
|
+
for k, v in item.items():
|
396
|
+
result.append(f"{k}:{v}")
|
397
|
+
else:
|
398
|
+
result.append(str(item))
|
399
|
+
elif isinstance(value, dict):
|
400
|
+
for key, item in value.items():
|
401
|
+
append_name(field)
|
402
|
+
if isinstance(item, list):
|
403
|
+
str_item = [str(i) for i in item]
|
404
|
+
separator = ";" if any(True for s in str_item if ":" in s) else ":"
|
405
|
+
result.append(f"{key}{separator if item else ''}{separator.join(str_item)}")
|
406
|
+
else:
|
407
|
+
result.append(f"{key}:{item}")
|
408
|
+
else:
|
409
|
+
append_name(field)
|
410
|
+
result.append(str(value))
|
403
411
|
except EvaluationError as e:
|
404
|
-
|
405
|
-
if error_callback is None:
|
406
|
-
raise ValueError(message) from e
|
407
|
-
error_callback(message)
|
412
|
+
raise ValueError(f"Evaluation of '{field.name}' failed: {e!s}") from e
|
408
413
|
|
409
414
|
return result
|
410
415
|
|
@@ -413,7 +418,7 @@ class BaseOptions(ValidateMixin):
|
|
413
418
|
|
414
419
|
|
415
420
|
@dataclass
|
416
|
-
class CommonOptions(
|
421
|
+
class CommonOptions(RobotBaseOptions):
|
417
422
|
"""Common options for all _robot_ commands."""
|
418
423
|
|
419
424
|
console_colors: Optional[Literal["auto", "on", "ansi", "off"]] = field(
|
@@ -972,7 +977,7 @@ class CommonOptions(BaseOptions):
|
|
972
977
|
|
973
978
|
|
974
979
|
@dataclass
|
975
|
-
class CommonExtendOptions(
|
980
|
+
class CommonExtendOptions(RobotBaseOptions):
|
976
981
|
"""Extra common options for all _robot_ commands."""
|
977
982
|
|
978
983
|
extend_excludes: Optional[List[Union[str, StringExpression]]] = field(
|
@@ -1297,7 +1302,7 @@ class CommonExtendOptions(BaseOptions):
|
|
1297
1302
|
|
1298
1303
|
|
1299
1304
|
@dataclass
|
1300
|
-
class RobotOptions(
|
1305
|
+
class RobotOptions(RobotBaseOptions):
|
1301
1306
|
"""Options for _robot_ command."""
|
1302
1307
|
|
1303
1308
|
console: Optional[Literal["verbose", "dotted", "skipped", "quiet", "none"]] = field(
|
@@ -1674,7 +1679,7 @@ class RobotOptions(BaseOptions):
|
|
1674
1679
|
|
1675
1680
|
|
1676
1681
|
@dataclass
|
1677
|
-
class RobotExtendOptions(
|
1682
|
+
class RobotExtendOptions(RobotBaseOptions):
|
1678
1683
|
"""Extra options for _robot_ command."""
|
1679
1684
|
|
1680
1685
|
extend_languages: Optional[List[Union[str, StringExpression]]] = field(
|
@@ -1789,7 +1794,7 @@ class RobotExtendOptions(BaseOptions):
|
|
1789
1794
|
|
1790
1795
|
|
1791
1796
|
@dataclass
|
1792
|
-
class RebotOptions(
|
1797
|
+
class RebotOptions(RobotBaseOptions):
|
1793
1798
|
"""Options for _rebot_ command."""
|
1794
1799
|
|
1795
1800
|
end_time: Optional[Union[str, StringExpression]] = field(
|
@@ -1884,7 +1889,7 @@ class RebotOptions(BaseOptions):
|
|
1884
1889
|
|
1885
1890
|
|
1886
1891
|
@dataclass
|
1887
|
-
class LibDocOptions(
|
1892
|
+
class LibDocOptions(RobotBaseOptions):
|
1888
1893
|
"""Options for _libdoc_ command."""
|
1889
1894
|
|
1890
1895
|
doc_format: Optional[Literal["ROBOT", "HTML", "TEXT", "REST"]] = field(
|
@@ -1976,7 +1981,7 @@ class LibDocOptions(BaseOptions):
|
|
1976
1981
|
|
1977
1982
|
|
1978
1983
|
@dataclass
|
1979
|
-
class LibDocExtendOptions(
|
1984
|
+
class LibDocExtendOptions(RobotBaseOptions):
|
1980
1985
|
"""Extra options for _libdoc_ command."""
|
1981
1986
|
|
1982
1987
|
extend_python_path: Optional[List[Union[str, StringExpression]]] = field(
|
@@ -1992,7 +1997,7 @@ class LibDocExtendOptions(BaseOptions):
|
|
1992
1997
|
|
1993
1998
|
|
1994
1999
|
@dataclass
|
1995
|
-
class TestDocOptions(
|
2000
|
+
class TestDocOptions(RobotBaseOptions):
|
1996
2001
|
"""Options for _testdoc_ command."""
|
1997
2002
|
|
1998
2003
|
doc: Optional[Union[str, StringExpression]] = field(
|
@@ -2090,7 +2095,7 @@ class TestDocOptions(BaseOptions):
|
|
2090
2095
|
|
2091
2096
|
|
2092
2097
|
@dataclass
|
2093
|
-
class TestDocExtendOptions(
|
2098
|
+
class TestDocExtendOptions(RobotBaseOptions):
|
2094
2099
|
"""Extra options for _testdoc_ command."""
|
2095
2100
|
|
2096
2101
|
extend_excludes: Optional[List[Union[str, StringExpression]]] = field(
|
@@ -2378,7 +2383,7 @@ class RobotConfig(RobotExtendBaseProfile):
|
|
2378
2383
|
|
2379
2384
|
extend_profiles: Optional[Dict[str, RobotProfile]] = field(description="Extra execution profiles.")
|
2380
2385
|
|
2381
|
-
tool: Any = field(description="Tool configurations.")
|
2386
|
+
tool: Optional[Dict[str, Any]] = field(description="Tool configurations.")
|
2382
2387
|
|
2383
2388
|
def _select_profiles(
|
2384
2389
|
self,
|
robotcode/robot/config/utils.py
CHANGED
@@ -43,10 +43,12 @@ def get_user_config_file(
|
|
43
43
|
def get_config_files(
|
44
44
|
paths: Optional[Sequence[Union[str, Path]]] = None,
|
45
45
|
config_files: Optional[Sequence[Path]] = None,
|
46
|
+
root_folder: Optional[Path] = None,
|
47
|
+
no_vcs: bool = False,
|
46
48
|
*,
|
47
49
|
verbose_callback: Optional[Callable[[str], None]] = None,
|
48
50
|
) -> Tuple[Sequence[Tuple[Path, ConfigType]], Optional[Path], DiscoverdBy]:
|
49
|
-
root_folder, discovered_by = find_project_root(*(paths or []))
|
51
|
+
root_folder, discovered_by = find_project_root(*(paths or []), root_folder=root_folder, no_vcs=no_vcs)
|
50
52
|
|
51
53
|
if root_folder is None:
|
52
54
|
root_folder = Path.cwd()
|
@@ -54,7 +56,7 @@ def get_config_files(
|
|
54
56
|
verbose_callback(f"Cannot detect root folder. Use current folder '{root_folder}' as root.")
|
55
57
|
|
56
58
|
if verbose_callback:
|
57
|
-
verbose_callback(f"
|
59
|
+
verbose_callback(f"Use root at:\n {root_folder} ({discovered_by.value})")
|
58
60
|
|
59
61
|
if config_files:
|
60
62
|
if verbose_callback:
|