robotcode-analyze 1.0.2__tar.gz → 1.1.0__tar.gz
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_analyze-1.0.2 → robotcode_analyze-1.1.0}/PKG-INFO +4 -4
- {robotcode_analyze-1.0.2 → robotcode_analyze-1.1.0}/pyproject.toml +3 -3
- robotcode_analyze-1.1.0/src/robotcode/analyze/__version__.py +1 -0
- robotcode_analyze-1.1.0/src/robotcode/analyze/cli.py +26 -0
- {robotcode_analyze-1.0.2/src/robotcode/analyze → robotcode_analyze-1.1.0/src/robotcode/analyze/code}/cli.py +53 -27
- {robotcode_analyze-1.0.2/src/robotcode/analyze → robotcode_analyze-1.1.0/src/robotcode/analyze/code}/code_analyzer.py +1 -1
- {robotcode_analyze-1.0.2/src/robotcode/analyze → robotcode_analyze-1.1.0/src/robotcode/analyze/code}/diagnostics_context.py +3 -3
- {robotcode_analyze-1.0.2/src/robotcode/analyze → robotcode_analyze-1.1.0/src/robotcode/analyze/code}/language_provider.py +1 -1
- {robotcode_analyze-1.0.2/src/robotcode/analyze → robotcode_analyze-1.1.0/src/robotcode/analyze/code}/robot_framework_language_provider.py +17 -12
- {robotcode_analyze-1.0.2 → robotcode_analyze-1.1.0}/src/robotcode/analyze/config.py +66 -1
- robotcode_analyze-1.0.2/src/robotcode/analyze/__version__.py +0 -1
- {robotcode_analyze-1.0.2 → robotcode_analyze-1.1.0}/.gitignore +0 -0
- {robotcode_analyze-1.0.2 → robotcode_analyze-1.1.0}/LICENSE.txt +0 -0
- {robotcode_analyze-1.0.2 → robotcode_analyze-1.1.0}/README.md +0 -0
- {robotcode_analyze-1.0.2 → robotcode_analyze-1.1.0}/src/robotcode/analyze/__init__.py +0 -0
- {robotcode_analyze-1.0.2 → robotcode_analyze-1.1.0}/src/robotcode/analyze/hooks.py +0 -0
- {robotcode_analyze-1.0.2 → robotcode_analyze-1.1.0}/src/robotcode/analyze/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotcode-analyze
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: RobotCode analyze plugin for Robot Framework
|
|
5
5
|
Project-URL: Homepage, https://robotcode.io
|
|
6
6
|
Project-URL: Donate, https://opencollective.com/robotcode
|
|
@@ -25,9 +25,9 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
25
25
|
Classifier: Topic :: Utilities
|
|
26
26
|
Classifier: Typing :: Typed
|
|
27
27
|
Requires-Python: >=3.8
|
|
28
|
-
Requires-Dist: robotcode-plugin==1.0
|
|
29
|
-
Requires-Dist: robotcode-robot==1.0
|
|
30
|
-
Requires-Dist: robotcode==1.0
|
|
28
|
+
Requires-Dist: robotcode-plugin==1.1.0
|
|
29
|
+
Requires-Dist: robotcode-robot==1.1.0
|
|
30
|
+
Requires-Dist: robotcode==1.1.0
|
|
31
31
|
Requires-Dist: robotframework>=4.1.0
|
|
32
32
|
Description-Content-Type: text/markdown
|
|
33
33
|
|
|
@@ -27,9 +27,9 @@ classifiers = [
|
|
|
27
27
|
]
|
|
28
28
|
dependencies = [
|
|
29
29
|
"robotframework>=4.1.0",
|
|
30
|
-
"robotcode-plugin==1.0
|
|
31
|
-
"robotcode-robot==1.0
|
|
32
|
-
"robotcode==1.0
|
|
30
|
+
"robotcode-plugin==1.1.0",
|
|
31
|
+
"robotcode-robot==1.1.0",
|
|
32
|
+
"robotcode==1.1.0",
|
|
33
33
|
]
|
|
34
34
|
dynamic = ["version"]
|
|
35
35
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.1.0"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from robotcode.plugin import Application, pass_application
|
|
4
|
+
|
|
5
|
+
from .__version__ import __version__
|
|
6
|
+
from .code.cli import code
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.group(
|
|
10
|
+
add_help_option=True,
|
|
11
|
+
invoke_without_command=False,
|
|
12
|
+
)
|
|
13
|
+
@click.version_option(
|
|
14
|
+
version=__version__,
|
|
15
|
+
package_name="robotcode.analyze",
|
|
16
|
+
prog_name="RobotCode Analyze",
|
|
17
|
+
)
|
|
18
|
+
@pass_application
|
|
19
|
+
def analyze(app: Application) -> None:
|
|
20
|
+
"""\
|
|
21
|
+
The analyze command provides various subcommands for analyzing Robot Framework code.
|
|
22
|
+
These subcommands support specialized tasks, such as code analysis, style checking or dependency graphs.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
analyze.add_command(code)
|
|
@@ -16,27 +16,9 @@ from robotcode.robot.config.loader import (
|
|
|
16
16
|
)
|
|
17
17
|
from robotcode.robot.config.utils import get_config_files
|
|
18
18
|
|
|
19
|
-
from
|
|
19
|
+
from ..__version__ import __version__
|
|
20
|
+
from ..config import AnalyzeConfig, ExitCodeMask, ModifiersConfig
|
|
20
21
|
from .code_analyzer import CodeAnalyzer, DocumentDiagnosticReport, FolderDiagnosticReport
|
|
21
|
-
from .config import AnalyzeConfig, ModifiersConfig
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@click.group(
|
|
25
|
-
add_help_option=True,
|
|
26
|
-
invoke_without_command=False,
|
|
27
|
-
)
|
|
28
|
-
@click.version_option(
|
|
29
|
-
version=__version__,
|
|
30
|
-
package_name="robotcode.analyze",
|
|
31
|
-
prog_name="RobotCode Analyze",
|
|
32
|
-
)
|
|
33
|
-
@pass_application
|
|
34
|
-
def analyze(app: Application) -> None:
|
|
35
|
-
"""\
|
|
36
|
-
The analyze command provides various subcommands for analyzing Robot Framework code.
|
|
37
|
-
These subcommands support specialized tasks, such as code analysis, style checking or dependency graphs.
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
22
|
|
|
41
23
|
SEVERITY_COLORS = {
|
|
42
24
|
DiagnosticSeverity.ERROR: "red",
|
|
@@ -55,7 +37,8 @@ class ReturnCode(Flag):
|
|
|
55
37
|
|
|
56
38
|
|
|
57
39
|
class Statistic:
|
|
58
|
-
def __init__(self) -> None:
|
|
40
|
+
def __init__(self, exit_code_mask: ExitCodeMask) -> None:
|
|
41
|
+
self.exit_code_mask = exit_code_mask
|
|
59
42
|
self._folders: Set[WorkspaceFolder] = set()
|
|
60
43
|
self._files: Set[TextDocument] = set()
|
|
61
44
|
self._diagnostics: List[Union[DocumentDiagnosticReport, FolderDiagnosticReport]] = []
|
|
@@ -104,18 +87,34 @@ class Statistic:
|
|
|
104
87
|
|
|
105
88
|
def calculate_return_code(self) -> ReturnCode:
|
|
106
89
|
return_code = ReturnCode.SUCCESS
|
|
107
|
-
if self.errors > 0:
|
|
90
|
+
if self.errors > 0 and not self.exit_code_mask & ExitCodeMask.ERROR:
|
|
108
91
|
return_code |= ReturnCode.ERRORS
|
|
109
|
-
if self.warnings > 0:
|
|
92
|
+
if self.warnings > 0 and not self.exit_code_mask & ExitCodeMask.WARN:
|
|
110
93
|
return_code |= ReturnCode.WARNINGS
|
|
111
|
-
if self.infos > 0:
|
|
94
|
+
if self.infos > 0 and not self.exit_code_mask & ExitCodeMask.INFO:
|
|
112
95
|
return_code |= ReturnCode.INFOS
|
|
113
|
-
if self.hints > 0:
|
|
96
|
+
if self.hints > 0 and not self.exit_code_mask & ExitCodeMask.HINT:
|
|
114
97
|
return_code |= ReturnCode.HINTS
|
|
115
98
|
return return_code
|
|
116
99
|
|
|
117
100
|
|
|
118
|
-
|
|
101
|
+
def _parse_exit_code_mask(ctx: click.Context, param: click.Option, value: Tuple[str, ...]) -> ExitCodeMask:
|
|
102
|
+
try:
|
|
103
|
+
return ExitCodeMask.parse(value)
|
|
104
|
+
except KeyError as e:
|
|
105
|
+
raise click.BadParameter(str(e)) from e
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _split_comma(ctx: click.Context, param: click.Option, value: Optional[List[str]]) -> List[str]:
|
|
109
|
+
if value is None:
|
|
110
|
+
return []
|
|
111
|
+
result: List[str] = []
|
|
112
|
+
for item in value:
|
|
113
|
+
result.extend([x.strip() for x in item.split(",") if x.strip()])
|
|
114
|
+
return result
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@click.command(
|
|
119
118
|
add_help_option=True,
|
|
120
119
|
)
|
|
121
120
|
@click.version_option(
|
|
@@ -199,6 +198,24 @@ class Statistic:
|
|
|
199
198
|
multiple=True,
|
|
200
199
|
help="Specifies the diagnostics codes to treat as hint.",
|
|
201
200
|
)
|
|
201
|
+
@click.option(
|
|
202
|
+
"--exit-code-mask",
|
|
203
|
+
"-xm",
|
|
204
|
+
multiple=True,
|
|
205
|
+
callback=_parse_exit_code_mask,
|
|
206
|
+
metavar="[" + "|".join(member.name.lower() for member in ExitCodeMask if member.name is not None) + "|all]",
|
|
207
|
+
help="Specifies which diagnostic severities should not affect the exit code. "
|
|
208
|
+
"For example, with 'warn' in the mask, warnings won't cause a non-zero exit code.",
|
|
209
|
+
)
|
|
210
|
+
@click.option(
|
|
211
|
+
"--extend-exit-code-mask",
|
|
212
|
+
"-xe",
|
|
213
|
+
multiple=True,
|
|
214
|
+
callback=_parse_exit_code_mask,
|
|
215
|
+
metavar="[" + "|".join(member.name.lower() for member in ExitCodeMask if member.name is not None) + "|all]",
|
|
216
|
+
help="Extend the exit code mask with the specified values. This appends to the default mask, defined in the config"
|
|
217
|
+
" file.",
|
|
218
|
+
)
|
|
202
219
|
@click.argument(
|
|
203
220
|
"paths", nargs=-1, type=click.Path(exists=True, dir_okay=True, file_okay=True, readable=True, path_type=Path)
|
|
204
221
|
)
|
|
@@ -214,6 +231,8 @@ def code(
|
|
|
214
231
|
modifiers_warning: Tuple[str, ...],
|
|
215
232
|
modifiers_information: Tuple[str, ...],
|
|
216
233
|
modifiers_hint: Tuple[str, ...],
|
|
234
|
+
exit_code_mask: ExitCodeMask,
|
|
235
|
+
extend_exit_code_mask: ExitCodeMask,
|
|
217
236
|
paths: Tuple[Path],
|
|
218
237
|
) -> None:
|
|
219
238
|
"""\
|
|
@@ -309,7 +328,14 @@ def code(
|
|
|
309
328
|
analyzer_config.modifiers.hint = []
|
|
310
329
|
analyzer_config.modifiers.hint.extend(modifiers_hint)
|
|
311
330
|
|
|
312
|
-
|
|
331
|
+
default_mask = (
|
|
332
|
+
exit_code_mask
|
|
333
|
+
if exit_code_mask != ExitCodeMask.NONE
|
|
334
|
+
else ExitCodeMask.parse(analyzer_config.code.exit_code_mask if analyzer_config.code is not None else None)
|
|
335
|
+
)
|
|
336
|
+
mask = default_mask | extend_exit_code_mask
|
|
337
|
+
|
|
338
|
+
statistics = Statistic(mask)
|
|
313
339
|
for e in CodeAnalyzer(
|
|
314
340
|
app=app,
|
|
315
341
|
analysis_config=analyzer_config.to_workspace_analysis_config(),
|
|
@@ -83,7 +83,7 @@ class CodeAnalyzer(DiagnosticsContext):
|
|
|
83
83
|
) -> Iterable[Union[DocumentDiagnosticReport, FolderDiagnosticReport]]:
|
|
84
84
|
for folder in self.workspace.workspace_folders:
|
|
85
85
|
self.app.verbose(f"Initialize folder {folder.uri.to_path()}")
|
|
86
|
-
initialize_result = self.diagnostics.
|
|
86
|
+
initialize_result = self.diagnostics.analyze_folder(folder)
|
|
87
87
|
if initialize_result is not None:
|
|
88
88
|
diagnostics: List[Diagnostic] = []
|
|
89
89
|
for item in initialize_result:
|
|
@@ -14,13 +14,13 @@ class DiagnosticHandlers:
|
|
|
14
14
|
@event
|
|
15
15
|
def document_analyzers(sender, document: TextDocument) -> Optional[List[Diagnostic]]: ...
|
|
16
16
|
@event
|
|
17
|
-
def
|
|
17
|
+
def folder_analyzers(sender, folder: WorkspaceFolder) -> Optional[List[Diagnostic]]: ...
|
|
18
18
|
|
|
19
19
|
@event
|
|
20
20
|
def collectors(sender, document: TextDocument) -> Optional[List[Diagnostic]]: ...
|
|
21
21
|
|
|
22
|
-
def
|
|
23
|
-
return self.
|
|
22
|
+
def analyze_folder(self, folder: WorkspaceFolder) -> List[Union[List[Diagnostic], BaseException, None]]:
|
|
23
|
+
return self.folder_analyzers(
|
|
24
24
|
self,
|
|
25
25
|
folder,
|
|
26
26
|
return_exceptions=True,
|
|
@@ -2,7 +2,7 @@ from abc import ABC, abstractmethod
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import Callable, Iterable, List, Optional
|
|
4
4
|
|
|
5
|
-
from robotcode.analyze.diagnostics_context import DiagnosticsContext
|
|
5
|
+
from robotcode.analyze.code.diagnostics_context import DiagnosticsContext
|
|
6
6
|
from robotcode.core.language import LanguageDefinition
|
|
7
7
|
from robotcode.core.workspace import WorkspaceFolder
|
|
8
8
|
|
|
@@ -5,7 +5,7 @@ from typing import Any, Iterable, List, Optional
|
|
|
5
5
|
|
|
6
6
|
from robot.utils import FileReader
|
|
7
7
|
|
|
8
|
-
from robotcode.analyze.diagnostics_context import DiagnosticsContext
|
|
8
|
+
from robotcode.analyze.code.diagnostics_context import DiagnosticsContext
|
|
9
9
|
from robotcode.core.filewatcher import FileWatcherManagerDummy
|
|
10
10
|
from robotcode.core.ignore_spec import DEFAULT_SPEC_RULES, GIT_IGNORE_FILE, ROBOT_IGNORE_FILE, IgnoreSpec, iter_files
|
|
11
11
|
from robotcode.core.language import LanguageDefinition, language_id
|
|
@@ -47,20 +47,25 @@ class RobotFrameworkLanguageProvider(LanguageProvider):
|
|
|
47
47
|
)
|
|
48
48
|
|
|
49
49
|
self.diagnostics_context.workspace.documents.on_read_document_text.add(self.on_read_document_text)
|
|
50
|
-
self.diagnostics_context.diagnostics.
|
|
50
|
+
self.diagnostics_context.diagnostics.folder_analyzers.add(self.analyze_folder)
|
|
51
51
|
self.diagnostics_context.diagnostics.document_analyzers.add(self.analyze_document)
|
|
52
52
|
|
|
53
53
|
def _update_python_path(self) -> None:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
root_path = (
|
|
55
|
+
self.diagnostics_context.workspace.root_uri.to_path()
|
|
56
|
+
if self.diagnostics_context.workspace.root_uri is not None
|
|
57
|
+
else None
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
for p in self.diagnostics_context.profile.python_path or []:
|
|
61
|
+
pa = Path(str(p))
|
|
62
|
+
if root_path is not None and not pa.is_absolute():
|
|
63
|
+
pa = Path(root_path, pa)
|
|
64
|
+
|
|
65
|
+
absolute_path = str(pa.absolute())
|
|
66
|
+
for f in glob.glob(absolute_path):
|
|
67
|
+
if Path(f).is_dir() and f not in sys.path:
|
|
68
|
+
sys.path.insert(0, f)
|
|
64
69
|
|
|
65
70
|
@language_id("robotframework")
|
|
66
71
|
def on_read_document_text(self, sender: Any, uri: Uri) -> str:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# ruff: noqa: RUF009
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from
|
|
3
|
+
from enum import IntFlag
|
|
4
|
+
from typing import Iterable, List, Literal, Optional, Union
|
|
4
5
|
|
|
5
6
|
from robotcode.robot.config.model import BaseOptions, field
|
|
6
7
|
from robotcode.robot.diagnostics.workspace_config import (
|
|
@@ -197,10 +198,74 @@ class CacheConfig(BaseOptions):
|
|
|
197
198
|
)
|
|
198
199
|
|
|
199
200
|
|
|
201
|
+
class ExitCodeMask(IntFlag):
|
|
202
|
+
NONE = 0
|
|
203
|
+
ERROR = 1
|
|
204
|
+
WARN = 2
|
|
205
|
+
WARNING = WARN
|
|
206
|
+
INFO = 4
|
|
207
|
+
INFORMATION = INFO
|
|
208
|
+
HINT = 8
|
|
209
|
+
ALL = ERROR | WARN | INFO | HINT
|
|
210
|
+
|
|
211
|
+
@staticmethod
|
|
212
|
+
def parse(value: Union[Iterable[str], str, None]) -> "ExitCodeMask":
|
|
213
|
+
if value is None:
|
|
214
|
+
return ExitCodeMask.NONE
|
|
215
|
+
|
|
216
|
+
flags = ExitCodeMask(0)
|
|
217
|
+
for entry in value:
|
|
218
|
+
for part_orig in entry.split(","):
|
|
219
|
+
part = part_orig.strip().upper()
|
|
220
|
+
if part:
|
|
221
|
+
try:
|
|
222
|
+
flags |= ExitCodeMask[part]
|
|
223
|
+
except KeyError as e:
|
|
224
|
+
raise KeyError(f"Invalid exit code mask value: {part_orig}") from e
|
|
225
|
+
return flags
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
ExitCodeMaskLiteral = Literal["error", "warn", "warning", "info", "information", "hint", "all"]
|
|
229
|
+
ExitCodeMaskList = List[ExitCodeMaskLiteral]
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@dataclass
|
|
233
|
+
class CodeConfig(BaseOptions):
|
|
234
|
+
"""robotcode-analyze code configuration."""
|
|
235
|
+
|
|
236
|
+
exit_code_mask: Optional[ExitCodeMaskList] = field(
|
|
237
|
+
description="""\
|
|
238
|
+
Specifies the exit code mask for the code analysis.
|
|
239
|
+
This is useful if you want to ignore certain types of diagnostics in the result code.
|
|
240
|
+
|
|
241
|
+
Examples:
|
|
242
|
+
```toml
|
|
243
|
+
[tool.robotcode-analyze.code]
|
|
244
|
+
exit_code_mask = ["error", "warn"]
|
|
245
|
+
```
|
|
246
|
+
""",
|
|
247
|
+
)
|
|
248
|
+
extend_exit_code_mask: Optional[ExitCodeMaskList] = field(description="Extend the exit code mask setting.")
|
|
249
|
+
|
|
250
|
+
|
|
200
251
|
@dataclass
|
|
201
252
|
class AnalyzeConfig(BaseOptions):
|
|
202
253
|
"""robotcode-analyze configuration."""
|
|
203
254
|
|
|
255
|
+
code: Optional[CodeConfig] = field(
|
|
256
|
+
description="""\
|
|
257
|
+
Defines the code analysis configuration.
|
|
258
|
+
|
|
259
|
+
Examples:
|
|
260
|
+
|
|
261
|
+
```toml
|
|
262
|
+
[tool.robotcode-analyze.code]
|
|
263
|
+
exit_code_mask = "error|warn"
|
|
264
|
+
```
|
|
265
|
+
"""
|
|
266
|
+
)
|
|
267
|
+
extend_code: Optional[CodeConfig] = field(description="Extend the code analysis configuration.")
|
|
268
|
+
|
|
204
269
|
modifiers: Optional[ModifiersConfig] = field(
|
|
205
270
|
description="""\
|
|
206
271
|
Defines the modifiers for the analysis.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.0.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|