robotcode-robot 0.91.0__py3-none-any.whl → 0.92.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.
@@ -1 +1 @@
1
- __version__ = "0.91.0"
1
+ __version__ = "0.92.0"
@@ -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
 
@@ -58,9 +59,9 @@ def load_robot_config_from_robot_toml_str(__s: str) -> RobotConfig:
58
59
 
59
60
 
60
61
  def load_config_from_robot_toml_str(
61
- config_type: Type[_ConfigType], __s: str, tool_name: Optional[str] = None
62
+ config_type: Type[_ConfigType], data: Union[str, Dict[str, Any]], tool_name: Optional[str] = None
62
63
  ) -> _ConfigType:
63
- dict_data = tomllib.loads(__s)
64
+ dict_data = tomllib.loads(data) if isinstance(data, str) else data
64
65
 
65
66
  if tool_name:
66
67
  try:
@@ -73,34 +74,39 @@ def load_config_from_robot_toml_str(
73
74
  return from_dict(dict_data, config_type)
74
75
 
75
76
 
76
- def load_config_from_pyproject_toml_str(config_type: Type[_ConfigType], tool_name: str, __s: str) -> _ConfigType:
77
- dict_data = tomllib.loads(__s)
77
+ def load_config_from_pyproject_toml_str(
78
+ config_type: Type[_ConfigType], tool_name: str, data: Union[str, Dict[str, Any]]
79
+ ) -> _ConfigType:
80
+ dict_data = tomllib.loads(data) if isinstance(data, str) else data
78
81
 
79
82
  return from_dict(dict_data.get("tool", {}).get(tool_name, {}), config_type)
80
83
 
81
84
 
82
85
  def _load_config_data_from_path(
83
86
  config_type: Type[_ConfigType],
84
- tool_name: str,
87
+ pyproject_toml_tool_name: str,
85
88
  robot_toml_tool_name: Optional[str],
86
- __path: Path,
89
+ path: Path,
90
+ data: Optional[Dict[str, Any]] = None,
87
91
  ) -> _ConfigType:
88
92
  try:
89
- if __path.name == PYPROJECT_TOML:
90
- return load_config_from_pyproject_toml_str(config_type, tool_name, __path.read_text("utf-8"))
93
+ if path.name == PYPROJECT_TOML:
94
+ return load_config_from_pyproject_toml_str(
95
+ config_type, pyproject_toml_tool_name, path.read_text("utf-8") if data is None else data
96
+ )
91
97
 
92
- if __path.name == ROBOT_TOML or __path.name == LOCAL_ROBOT_TOML or __path.suffix == ".toml":
98
+ if path.name == ROBOT_TOML or path.name == LOCAL_ROBOT_TOML or path.suffix == ".toml":
93
99
  return load_config_from_robot_toml_str(
94
100
  config_type,
95
- __path.read_text("utf-8"),
101
+ path.read_text("utf-8") if data is None else data,
96
102
  tool_name=robot_toml_tool_name,
97
103
  )
98
104
  raise TypeError("Unknown config file type.")
99
105
 
100
106
  except ValueError as e:
101
- raise ConfigValueError(__path, f'Parsing "{__path}" failed: {e}') from e
107
+ raise ConfigValueError(path, f'Parsing "{path}" failed: {e}') from e
102
108
  except TypeError as e:
103
- raise ConfigTypeError(__path, f'Parsing "{__path}" failed: {e}') from e
109
+ raise ConfigTypeError(path, f'Parsing "{path}" failed: {e}') from e
104
110
 
105
111
 
106
112
  def get_default_config() -> RobotConfig:
@@ -113,34 +119,74 @@ def get_default_config() -> RobotConfig:
113
119
  def load_config_from_path(
114
120
  config_type: Type[_ConfigType],
115
121
  *__paths: Union[Path, Tuple[Path, ConfigType]],
116
- tool_name: str,
122
+ pyproject_toml_tool_name: str,
117
123
  robot_toml_tool_name: Optional[str] = None,
124
+ extra_tools: Optional[Dict[str, Type[Any]]] = None,
125
+ verbose_callback: Optional[Callable[[str], None]] = None,
118
126
  ) -> _ConfigType:
119
127
  result = config_type()
128
+ tools: Optional[Dict[str, Any]] = (
129
+ {} if extra_tools and is_dataclass(result) and any(f for f in fields(result) if f.name == "tool") else None
130
+ )
120
131
 
121
132
  for __path in __paths:
122
133
  if isinstance(__path, tuple):
123
134
  path, c_type = __path
124
135
  if path.name == "__no_user_config__.toml" and c_type == ConfigType.DEFAULT_CONFIG_TOML:
136
+ if verbose_callback:
137
+ verbose_callback("Load default configuration.")
125
138
  result.add_options(get_default_config())
126
139
  continue
127
140
 
141
+ if verbose_callback:
142
+ verbose_callback(f"Load configuration from {__path if isinstance(__path, Path) else __path[0]}")
143
+
144
+ p = __path if isinstance(__path, Path) else __path[0]
145
+ data = tomllib.loads(p.read_text("utf-8"))
146
+
128
147
  result.add_options(
129
148
  _load_config_data_from_path(
130
149
  config_type,
131
- tool_name,
150
+ pyproject_toml_tool_name,
132
151
  robot_toml_tool_name,
133
- __path if isinstance(__path, Path) else __path[0],
152
+ p,
153
+ data,
134
154
  )
135
155
  )
136
156
 
157
+ if tools is not None and extra_tools:
158
+ for tool_name, tool_config in extra_tools.items():
159
+ if tool_name not in tools:
160
+ tools[tool_name] = tool_config()
161
+
162
+ tool = tools[tool_name]
163
+ tool.add_options(
164
+ _load_config_data_from_path(
165
+ tool_config,
166
+ tool_name,
167
+ tool_name,
168
+ p,
169
+ data,
170
+ )
171
+ )
172
+ if tools is not None:
173
+ setattr(result, "tool", tools)
174
+
137
175
  return result
138
176
 
139
177
 
140
178
  def load_robot_config_from_path(
141
179
  *__paths: Union[Path, Tuple[Path, ConfigType]],
180
+ extra_tools: Optional[Dict[str, Type[Any]]] = None,
181
+ verbose_callback: Optional[Callable[[str], None]] = None,
142
182
  ) -> RobotConfig:
143
- return load_config_from_path(RobotConfig, *__paths, tool_name="robot")
183
+ return load_config_from_path(
184
+ RobotConfig,
185
+ *__paths,
186
+ pyproject_toml_tool_name="robot",
187
+ extra_tools=extra_tools,
188
+ verbose_callback=verbose_callback,
189
+ )
144
190
 
145
191
 
146
192
  def find_project_root(
@@ -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
- result = dataclasses.replace(self)
382
- for f in dataclasses.fields(result):
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
- if isinstance(getattr(result, f.name), Condition):
385
- setattr(result, f.name, getattr(result, f.name).evaluate())
386
- elif isinstance(getattr(result, f.name), Expression):
387
- setattr(result, f.name, getattr(result, f.name).evaluate())
388
- elif isinstance(getattr(result, f.name), list):
389
- setattr(
390
- result,
391
- f.name,
392
- [e.evaluate() if isinstance(e, Expression) else e for e in getattr(result, f.name)],
393
- )
394
- elif isinstance(getattr(result, f.name), dict):
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
- message = f"Evaluation of '{f.name}' failed: {type(e).__name__}: {e}"
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(BaseOptions):
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(BaseOptions):
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(BaseOptions):
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(BaseOptions):
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(BaseOptions):
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(BaseOptions):
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(BaseOptions):
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(BaseOptions):
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(BaseOptions):
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,
@@ -2,7 +2,8 @@ import functools
2
2
  import re as re
3
3
  from ast import AST
4
4
  from collections import defaultdict
5
- from dataclasses import dataclass
5
+ from dataclasses import dataclass, field
6
+ from enum import Enum
6
7
  from typing import Dict, List, Optional, Set, Union
7
8
 
8
9
  from robot.parsing.lexer.tokens import Token
@@ -12,15 +13,32 @@ from robotcode.core.lsp.types import Diagnostic, DiagnosticSeverity
12
13
 
13
14
  from ..utils.visitor import Visitor
14
15
 
15
- ACTIONS = ["ignore", "error", "warn", "information", "hint", "reset"]
16
+ ACTIONS = ["ignore", "error", "warn", "warning", "info", "information", "hint", "reset"]
16
17
 
17
18
  ROBOTCODE_ACTION_AND_CODES_PATTERN = re.compile(rf"(?P<action>{'|'.join(ACTIONS)})(\[(?P<codes>[^\]]*?)\])?")
18
19
 
19
20
 
21
+ class ModifierAction(Enum):
22
+ IGNORE = "ignore"
23
+ ERROR = "error"
24
+ WARNING = "warning"
25
+ INFORMATION = "information"
26
+ HINT = "hint"
27
+ RESET = "reset"
28
+
29
+ @classmethod
30
+ def from_str(cls, value: str) -> "ModifierAction":
31
+ if value == "warn":
32
+ value = "warning"
33
+ elif value == "info":
34
+ value = "information"
35
+ return cls(value)
36
+
37
+
20
38
  @dataclass
21
39
  class RulesAndCodes:
22
40
  codes: Dict[Union[str, int], Set[int]]
23
- actions: Dict[int, Dict[Union[str, int], str]]
41
+ actions: Dict[int, Dict[Union[str, int], ModifierAction]]
24
42
 
25
43
 
26
44
  _translation_table = str.maketrans("", "", "_- ")
@@ -28,7 +46,7 @@ _translation_table = str.maketrans("", "", "_- ")
28
46
  ROBOTCODE_MARKER = "robotcode:"
29
47
 
30
48
 
31
- class DisablersVisitor(Visitor):
49
+ class ModifiersVisitor(Visitor):
32
50
 
33
51
  def __init__(self) -> None:
34
52
  super().__init__()
@@ -65,8 +83,8 @@ class DisablersVisitor(Visitor):
65
83
  self._handle_statement_comments(node)
66
84
  self.generic_visit(node)
67
85
 
68
- def _parse_robotcode_disabler(self, comment: str) -> Dict[str, List[str]]:
69
- result: Dict[str, List[str]] = {}
86
+ def _parse_robotcode_disabler(self, comment: str) -> Dict[ModifierAction, List[str]]:
87
+ result: Dict[ModifierAction, List[str]] = {}
70
88
 
71
89
  comment = comment.strip()
72
90
  m = ROBOTCODE_ACTION_AND_CODES_PATTERN.match(comment)
@@ -74,7 +92,8 @@ class DisablersVisitor(Visitor):
74
92
  return result
75
93
 
76
94
  for m in ROBOTCODE_ACTION_AND_CODES_PATTERN.finditer(comment):
77
- action = m.group("action")
95
+ action_str = m.group("action")
96
+ action = ModifierAction.from_str(action_str)
78
97
  messages = m.group("codes")
79
98
  result[action] = (
80
99
  [m.strip().translate(_translation_table).lower() for m in messages.split(",")]
@@ -153,19 +172,36 @@ class DisablersVisitor(Visitor):
153
172
  self.rules_and_codes.actions[i][code] = action
154
173
 
155
174
 
175
+ @dataclass
176
+ class DiagnosticModifiersConfig:
177
+ ignore: List[str] = field(default_factory=list)
178
+ error: List[str] = field(default_factory=list)
179
+ warning: List[str] = field(default_factory=list)
180
+ information: List[str] = field(default_factory=list)
181
+ hint: List[str] = field(default_factory=list)
182
+
183
+
156
184
  class DiagnosticsModifier:
157
- def __init__(self, model: AST) -> None:
185
+ def __init__(self, model: AST, config: Optional[DiagnosticModifiersConfig] = None) -> None:
186
+ self.config = config or DiagnosticModifiersConfig()
187
+
188
+ self.config.ignore = [i.translate(_translation_table).lower() for i in self.config.ignore]
189
+ self.config.error = [i.translate(_translation_table).lower() for i in self.config.error]
190
+ self.config.warning = [i.translate(_translation_table).lower() for i in self.config.warning]
191
+ self.config.information = [i.translate(_translation_table).lower() for i in self.config.information]
192
+ self.config.hint = [i.translate(_translation_table).lower() for i in self.config.hint]
193
+
158
194
  self.model = model
159
195
 
160
196
  @functools.cached_property
161
197
  def rules_and_codes(self) -> RulesAndCodes:
162
- visitor = DisablersVisitor()
198
+ visitor = ModifiersVisitor()
163
199
  visitor.visit(self.model)
164
200
  return visitor.rules_and_codes
165
201
 
166
202
  def modify_diagnostic(self, diagnostic: Diagnostic) -> Optional[Diagnostic]:
167
203
  if diagnostic.code is not None:
168
- code = (
204
+ code = orig_code = (
169
205
  str(diagnostic.code).translate(_translation_table).lower()
170
206
  if diagnostic.code is not None
171
207
  else "unknowncode"
@@ -177,24 +213,51 @@ class DiagnosticsModifier:
177
213
  code = "*"
178
214
  lines = self.rules_and_codes.codes.get(code)
179
215
 
216
+ modified = False
180
217
  if lines is not None and diagnostic.range.start.line in lines:
181
218
  actions = self.rules_and_codes.actions.get(diagnostic.range.start.line)
182
219
  if actions is not None:
183
220
  action = actions.get(code)
184
221
  if action is not None:
185
- if action == "ignore":
222
+ if action == ModifierAction.IGNORE:
186
223
  return None
187
- if action == "reset":
188
- pass # do nothing
189
- elif action == "error":
224
+ if action == ModifierAction.RESET:
225
+ pass
226
+ elif action == ModifierAction.ERROR:
227
+ modified = True
190
228
  diagnostic.severity = DiagnosticSeverity.ERROR
191
- elif action == "warn":
229
+ elif action == ModifierAction.WARNING:
230
+ modified = True
192
231
  diagnostic.severity = DiagnosticSeverity.WARNING
193
- elif action == "information":
232
+ elif action == ModifierAction.INFORMATION:
233
+ modified = True
194
234
  diagnostic.severity = DiagnosticSeverity.INFORMATION
195
- elif action == "hint":
235
+ elif action == ModifierAction.HINT:
236
+ modified = True
196
237
  diagnostic.severity = DiagnosticSeverity.HINT
197
238
 
239
+ if not modified:
240
+ if orig_code in self.config.ignore:
241
+ return None
242
+ if orig_code in self.config.error:
243
+ diagnostic.severity = DiagnosticSeverity.ERROR
244
+ elif orig_code in self.config.warning:
245
+ diagnostic.severity = DiagnosticSeverity.WARNING
246
+ elif orig_code in self.config.information:
247
+ diagnostic.severity = DiagnosticSeverity.INFORMATION
248
+ elif orig_code in self.config.hint:
249
+ diagnostic.severity = DiagnosticSeverity.HINT
250
+ elif "*" in self.config.hint:
251
+ diagnostic.severity = DiagnosticSeverity.HINT
252
+ elif "*" in self.config.information:
253
+ diagnostic.severity = DiagnosticSeverity.INFORMATION
254
+ elif "*" in self.config.warning:
255
+ diagnostic.severity = DiagnosticSeverity.WARNING
256
+ elif "*" in self.config.error:
257
+ diagnostic.severity = DiagnosticSeverity.ERROR
258
+ elif "*" in self.config.ignore:
259
+ return None
260
+
198
261
  return diagnostic
199
262
 
200
263
  def modify_diagnostics(self, diagnostics: List[Diagnostic]) -> List[Diagnostic]:
@@ -26,7 +26,7 @@ from robotcode.core.text_document import TextDocument
26
26
  from robotcode.core.uri import Uri
27
27
  from robotcode.core.utils.logging import LoggingDescriptor
28
28
  from robotcode.core.workspace import Workspace, WorkspaceFolder
29
- from robotcode.robot.diagnostics.diagnostics_modifier import DiagnosticsModifier
29
+ from robotcode.robot.diagnostics.diagnostics_modifier import DiagnosticModifiersConfig, DiagnosticsModifier
30
30
 
31
31
  from ..config.model import RobotBaseProfile
32
32
  from ..utils import get_robot_version
@@ -34,7 +34,13 @@ from ..utils.stubs import Languages
34
34
  from .imports_manager import ImportsManager
35
35
  from .library_doc import LibraryDoc
36
36
  from .namespace import DocumentType, Namespace
37
- from .workspace_config import AnalysisRobotConfig, CacheConfig, RobotConfig
37
+ from .workspace_config import (
38
+ AnalysisDiagnosticModifiersConfig,
39
+ AnalysisRobotConfig,
40
+ CacheConfig,
41
+ RobotConfig,
42
+ WorkspaceAnalysisConfig,
43
+ )
38
44
 
39
45
 
40
46
  class UnknownFileTypeError(Exception):
@@ -54,6 +60,7 @@ class DocumentsCacheHelper:
54
60
  documents_manager: DocumentsManager,
55
61
  file_watcher_manager: FileWatcherManagerBase,
56
62
  robot_profile: Optional[RobotBaseProfile],
63
+ analysis_config: Optional[WorkspaceAnalysisConfig],
57
64
  ) -> None:
58
65
  self.INITIALIZED_NAMESPACE = _CacheEntry()
59
66
 
@@ -62,6 +69,7 @@ class DocumentsCacheHelper:
62
69
  self.file_watcher_manager = file_watcher_manager
63
70
 
64
71
  self.robot_profile = robot_profile or RobotBaseProfile()
72
+ self.analysis_config = analysis_config or WorkspaceAnalysisConfig()
65
73
 
66
74
  self._imports_managers_lock = threading.RLock()
67
75
  self._imports_managers: weakref.WeakKeyDictionary[WorkspaceFolder, ImportsManager] = weakref.WeakKeyDictionary()
@@ -514,10 +522,10 @@ class DocumentsCacheHelper:
514
522
  variables,
515
523
  variable_files,
516
524
  environment,
517
- cache_config.ignored_libraries,
518
- cache_config.ignored_variables,
519
- cache_config.ignore_arguments_for_library,
520
- analysis_config.global_library_search_order,
525
+ self.analysis_config.cache.ignored_libraries + cache_config.ignored_libraries,
526
+ self.analysis_config.cache.ignored_variables + cache_config.ignored_variables,
527
+ self.analysis_config.cache.ignore_arguments_for_library + cache_config.ignore_arguments_for_library,
528
+ self.analysis_config.robot.global_library_search_order + analysis_config.global_library_search_order,
521
529
  cache_base_path,
522
530
  )
523
531
 
@@ -583,4 +591,14 @@ class DocumentsCacheHelper:
583
591
  return document.get_cache(self.__get_diagnostic_modifier)
584
592
 
585
593
  def __get_diagnostic_modifier(self, document: TextDocument) -> DiagnosticsModifier:
586
- return DiagnosticsModifier(self.get_model(document, False))
594
+ modifiers_config = self.workspace.get_configuration(AnalysisDiagnosticModifiersConfig, document.uri)
595
+ return DiagnosticsModifier(
596
+ self.get_model(document, False),
597
+ DiagnosticModifiersConfig(
598
+ ignore=self.analysis_config.modifiers.ignore + modifiers_config.ignore,
599
+ error=self.analysis_config.modifiers.error + modifiers_config.error,
600
+ warning=self.analysis_config.modifiers.warning + modifiers_config.warning,
601
+ information=self.analysis_config.modifiers.information + modifiers_config.information,
602
+ hint=self.analysis_config.modifiers.hint + modifiers_config.hint,
603
+ ),
604
+ )
@@ -55,3 +55,20 @@ class CacheConfig(ConfigBase):
55
55
  @dataclass
56
56
  class AnalysisRobotConfig(ConfigBase):
57
57
  global_library_search_order: List[str] = field(default_factory=list)
58
+
59
+
60
+ @config_section("robotcode.analysis.diagnosticModifiers")
61
+ @dataclass
62
+ class AnalysisDiagnosticModifiersConfig(ConfigBase):
63
+ ignore: List[str] = field(default_factory=list)
64
+ error: List[str] = field(default_factory=list)
65
+ warning: List[str] = field(default_factory=list)
66
+ information: List[str] = field(default_factory=list)
67
+ hint: List[str] = field(default_factory=list)
68
+
69
+
70
+ @dataclass
71
+ class WorkspaceAnalysisConfig:
72
+ cache: CacheConfig = field(default_factory=CacheConfig)
73
+ robot: AnalysisRobotConfig = field(default_factory=AnalysisRobotConfig)
74
+ modifiers: AnalysisDiagnosticModifiersConfig = field(default_factory=AnalysisDiagnosticModifiersConfig)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: robotcode-robot
3
- Version: 0.91.0
3
+ Version: 0.92.0
4
4
  Summary: Support classes for RobotCode for handling Robot Framework projects.
5
5
  Project-URL: Homepage, https://robotcode.io
6
6
  Project-URL: Donate, https://opencollective.com/robotcode
@@ -26,7 +26,7 @@ Classifier: Topic :: Utilities
26
26
  Classifier: Typing :: Typed
27
27
  Requires-Python: >=3.8
28
28
  Requires-Dist: platformdirs<4.2.0,>=3.2.0
29
- Requires-Dist: robotcode-core==0.91.0
29
+ Requires-Dist: robotcode-core==0.92.0
30
30
  Requires-Dist: robotframework>=4.1.0
31
31
  Requires-Dist: tomli>=1.1.0; python_version < '3.11'
32
32
  Description-Content-Type: text/markdown
@@ -1,13 +1,13 @@
1
1
  robotcode/robot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- robotcode/robot/__version__.py,sha256=RBon26SFk4Bs08h0XAmTYW8lrYs3N9ihifF9r6VT4Vc,23
2
+ robotcode/robot/__version__.py,sha256=4oQqdZ-jW338RGQfLxI7hoQNb8tTR6y4hzWoJRPdI3o,23
3
3
  robotcode/robot/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27
4
4
  robotcode/robot/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- robotcode/robot/config/loader.py,sha256=LpGqJAdysvVSZpccW-Il52xn9RMBBb9X94emlBY7zCc,6077
6
- robotcode/robot/config/model.py,sha256=w2qqxPwuv-N3GEZsCaaxFxU5iPfQr3oS1mqMkZFkRPU,88891
5
+ robotcode/robot/config/loader.py,sha256=5PQTWDn1vYFN0VmLVhEYdu12Foq1AwRtDFvjU44QQvw,7878
6
+ robotcode/robot/config/model.py,sha256=sgr6-4_E06g-yIXW41Z-NtIXZ_7JMmR5WvUD7kTUqu4,89106
7
7
  robotcode/robot/config/utils.py,sha256=c_WZg39DJgM6kXcAH_h-v68qhf1eStJ0TslTawaJoZw,2827
8
8
  robotcode/robot/diagnostics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- robotcode/robot/diagnostics/diagnostics_modifier.py,sha256=9AFptQsdw_TvgBkKqutpvI8WKsOUXipXQDOExarJJy4,6903
10
- robotcode/robot/diagnostics/document_cache_helper.py,sha256=pa9AdSbEFZJ6VSdZluGyfzyZPjzBfNFAv5pXJEM-7fc,22664
9
+ robotcode/robot/diagnostics/diagnostics_modifier.py,sha256=3dDsu8-ET6weIvv7Sk3IQaPYFNxnXUs8Y7gpGTjfOBs,9796
10
+ robotcode/robot/diagnostics/document_cache_helper.py,sha256=MlOPXC3mUVjNB2rHn5UJK7NnUPa8S4mE3qojILOi_Mk,23756
11
11
  robotcode/robot/diagnostics/entities.py,sha256=AtrqPAS3XUC-8VpFmqMXfMKjQHmfxXZlyGWWFaEBpQA,10979
12
12
  robotcode/robot/diagnostics/errors.py,sha256=HCE0h0_ui1tx-6pJHs0r4s09ZHmArYwKUo-_lBl9K-4,1600
13
13
  robotcode/robot/diagnostics/imports_manager.py,sha256=qE__lm0Hsj3R0gUauXRmbWJPYihNjk3O-c5hcICGQjc,56251
@@ -15,7 +15,7 @@ robotcode/robot/diagnostics/library_doc.py,sha256=CHgyC9GMRQFP3fGTxWa139rIcGIk8Y
15
15
  robotcode/robot/diagnostics/model_helper.py,sha256=_5ixKKMrb-nY-uvV8_WjJ1rlNlz7gT7kHM5NYi_hjVg,30232
16
16
  robotcode/robot/diagnostics/namespace.py,sha256=jCUb5GmMDA6KTr2BnwHD_0Mp_gTg1XGskUvRYPL7XQ0,89543
17
17
  robotcode/robot/diagnostics/namespace_analyzer.py,sha256=3eC1xEBm_foCnqoHk8Cj6O11UXOHcHxWnfuKP8M5KIQ,51369
18
- robotcode/robot/diagnostics/workspace_config.py,sha256=lWNq1KmGGJ9cHFNHn0QTCBHHzgz4AewTw0l-W4TKrj0,1803
18
+ robotcode/robot/diagnostics/workspace_config.py,sha256=3SoewUj_LZB1Ki5hXM8oxQpJr6vyiog66SUw-ibODSA,2478
19
19
  robotcode/robot/utils/__init__.py,sha256=OjNPMn_XSnfaMCyKd8Kmq6vlRt6mIGlzW4qiiD3ykUg,447
20
20
  robotcode/robot/utils/ast.py,sha256=ynxvXv1SHkAcF07m76ey9_zaGCYXhlPOnny7km-XWSQ,10479
21
21
  robotcode/robot/utils/markdownformatter.py,sha256=0XZZ5wDU2yfzIuShjG7h79PgzMaQJ501WH4YRFcG1VM,11615
@@ -24,7 +24,7 @@ robotcode/robot/utils/robot_path.py,sha256=qKBh1cEnReBBLKkWu4gB9EzM-scAwE4xJc1m6
24
24
  robotcode/robot/utils/stubs.py,sha256=6-DMI_CQVJHDgG13t-zINKGCRb_Q7MQPm0_AkfhAEvE,748
25
25
  robotcode/robot/utils/variables.py,sha256=fEl8S37lb_mD4hn2MZRAlkiuLGBjAOeZVK0r2o2CfPw,742
26
26
  robotcode/robot/utils/visitor.py,sha256=uYLqEhGPmzWKWI3SSrmCaYMwtKvNShvbiPZ4b3FavX8,3241
27
- robotcode_robot-0.91.0.dist-info/METADATA,sha256=Cl7XlzEEn_Q5xmyv7C6R09JliQm1pL45DGuC-M4cSrY,2240
28
- robotcode_robot-0.91.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
29
- robotcode_robot-0.91.0.dist-info/licenses/LICENSE.txt,sha256=B05uMshqTA74s-0ltyHKI6yoPfJ3zYgQbvcXfDVGFf8,10280
30
- robotcode_robot-0.91.0.dist-info/RECORD,,
27
+ robotcode_robot-0.92.0.dist-info/METADATA,sha256=H_aQghUcVbx_6vUwIw0DTqVSUhN1ewvzGjinWv-MEX0,2240
28
+ robotcode_robot-0.92.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
29
+ robotcode_robot-0.92.0.dist-info/licenses/LICENSE.txt,sha256=B05uMshqTA74s-0ltyHKI6yoPfJ3zYgQbvcXfDVGFf8,10280
30
+ robotcode_robot-0.92.0.dist-info/RECORD,,