robotcode-robot 0.91.0__py3-none-any.whl → 0.93.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|