robotcode-robot 0.91.0__py3-none-any.whl → 0.92.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,,