prospector 1.12.1__py3-none-any.whl → 1.13.1__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.
- prospector/autodetect.py +10 -9
- prospector/blender.py +18 -11
- prospector/config/__init__.py +66 -49
- prospector/config/configuration.py +14 -12
- prospector/config/datatype.py +1 -1
- prospector/encoding.py +2 -2
- prospector/exceptions.py +1 -5
- prospector/finder.py +9 -8
- prospector/formatters/__init__.py +1 -1
- prospector/formatters/base.py +16 -11
- prospector/formatters/base_summary.py +43 -0
- prospector/formatters/emacs.py +5 -5
- prospector/formatters/grouped.py +9 -7
- prospector/formatters/json.py +3 -2
- prospector/formatters/pylint.py +31 -8
- prospector/formatters/text.py +10 -58
- prospector/formatters/vscode.py +17 -6
- prospector/formatters/xunit.py +6 -6
- prospector/formatters/yaml.py +4 -2
- prospector/message.py +18 -13
- prospector/pathutils.py +3 -10
- prospector/postfilter.py +8 -7
- prospector/profiles/exceptions.py +14 -11
- prospector/profiles/profile.py +69 -58
- prospector/run.py +20 -18
- prospector/suppression.py +12 -10
- prospector/tools/__init__.py +19 -13
- prospector/tools/bandit/__init__.py +27 -15
- prospector/tools/base.py +11 -3
- prospector/tools/dodgy/__init__.py +7 -3
- prospector/tools/mccabe/__init__.py +13 -6
- prospector/tools/mypy/__init__.py +44 -81
- prospector/tools/profile_validator/__init__.py +24 -15
- prospector/tools/pycodestyle/__init__.py +22 -15
- prospector/tools/pydocstyle/__init__.py +12 -6
- prospector/tools/pyflakes/__init__.py +35 -19
- prospector/tools/pylint/__init__.py +47 -28
- prospector/tools/pylint/collector.py +3 -5
- prospector/tools/pylint/linter.py +11 -9
- prospector/tools/pyright/__init__.py +18 -7
- prospector/tools/pyroma/__init__.py +10 -6
- prospector/tools/ruff/__init__.py +74 -0
- prospector/tools/utils.py +25 -19
- prospector/tools/vulture/__init__.py +25 -15
- {prospector-1.12.1.dist-info → prospector-1.13.1.dist-info}/METADATA +4 -3
- prospector-1.13.1.dist-info/RECORD +71 -0
- prospector-1.12.1.dist-info/RECORD +0 -69
- {prospector-1.12.1.dist-info → prospector-1.13.1.dist-info}/LICENSE +0 -0
- {prospector-1.12.1.dist-info → prospector-1.13.1.dist-info}/WHEEL +0 -0
- {prospector-1.12.1.dist-info → prospector-1.13.1.dist-info}/entry_points.txt +0 -0
prospector/tools/base.py
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from
|
|
2
|
+
from collections.abc import Iterable
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TYPE_CHECKING, Optional, Union
|
|
3
5
|
|
|
6
|
+
from prospector.finder import FileFinder
|
|
4
7
|
from prospector.message import Message
|
|
5
8
|
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from prospector.config import ProspectorConfig
|
|
11
|
+
|
|
6
12
|
|
|
7
13
|
class ToolBase(ABC):
|
|
8
14
|
@abstractmethod
|
|
9
|
-
def configure(
|
|
15
|
+
def configure(
|
|
16
|
+
self, prospector_config: "ProspectorConfig", found_files: FileFinder
|
|
17
|
+
) -> Optional[tuple[Optional[Union[str, Path]], Optional[Iterable[Message]]]]:
|
|
10
18
|
"""
|
|
11
19
|
Tools have their own way of being configured from configuration files
|
|
12
20
|
on the current path - for example, a .pep8rc file. Prospector will use
|
|
@@ -25,7 +33,7 @@ class ToolBase(ABC):
|
|
|
25
33
|
raise NotImplementedError
|
|
26
34
|
|
|
27
35
|
@abstractmethod
|
|
28
|
-
def run(self, found_files) ->
|
|
36
|
+
def run(self, found_files: FileFinder) -> list[Message]:
|
|
29
37
|
"""
|
|
30
38
|
Actually run the tool and collect the various messages emitted by the tool.
|
|
31
39
|
It is expected that this will convert whatever output of the tool into the
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import mimetypes
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
3
4
|
|
|
4
5
|
from dodgy.checks import check_file_contents
|
|
5
6
|
|
|
@@ -8,18 +9,21 @@ from prospector.finder import FileFinder
|
|
|
8
9
|
from prospector.message import Location, Message
|
|
9
10
|
from prospector.tools.base import ToolBase
|
|
10
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from prospector.config import ProspectorConfig
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
|
|
16
|
+
def module_from_path(path: Path) -> str:
|
|
13
17
|
# TODO hacky...
|
|
14
18
|
return ".".join(path.parts[1:-1] + (path.stem,))
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
class DodgyTool(ToolBase):
|
|
18
|
-
def configure(self, prospector_config, found_files):
|
|
22
|
+
def configure(self, prospector_config: "ProspectorConfig", found_files: FileFinder) -> None:
|
|
19
23
|
# empty: just implementing to satisfy the ABC contract
|
|
20
24
|
pass
|
|
21
25
|
|
|
22
|
-
def run(self, found_files: FileFinder):
|
|
26
|
+
def run(self, found_files: FileFinder) -> list[Message]:
|
|
23
27
|
warnings = []
|
|
24
28
|
for filepath in found_files.files:
|
|
25
29
|
mimetype = mimetypes.guess_type(str(filepath.absolute()))
|
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
import ast
|
|
2
|
+
from typing import TYPE_CHECKING, Any
|
|
2
3
|
|
|
3
4
|
from mccabe import PathGraphingAstVisitor
|
|
4
5
|
|
|
5
6
|
from prospector.encoding import CouldNotHandleEncoding, read_py_file
|
|
7
|
+
from prospector.finder import FileFinder
|
|
6
8
|
from prospector.message import Location, Message, make_tool_error_message
|
|
7
9
|
from prospector.tools.base import ToolBase
|
|
8
10
|
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from prospector.config import ProspectorConfig
|
|
13
|
+
|
|
9
14
|
__all__ = ("McCabeTool",)
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
class McCabeTool(ToolBase):
|
|
13
|
-
def __init__(self, *args, **kwargs):
|
|
18
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
14
19
|
super().__init__(*args, **kwargs)
|
|
15
|
-
self.ignore_codes =
|
|
20
|
+
self.ignore_codes: list[str] = []
|
|
16
21
|
self.max_complexity = 10
|
|
17
22
|
|
|
18
|
-
def configure(self, prospector_config, _):
|
|
23
|
+
def configure(self, prospector_config: "ProspectorConfig", _: Any) -> None:
|
|
19
24
|
self.ignore_codes = prospector_config.get_disabled_messages("mccabe")
|
|
20
25
|
|
|
21
26
|
options = prospector_config.tool_options("mccabe")
|
|
22
27
|
if "max-complexity" in options:
|
|
23
28
|
self.max_complexity = options["max-complexity"]
|
|
24
29
|
|
|
25
|
-
def run(self, found_files):
|
|
30
|
+
def run(self, found_files: FileFinder) -> list[Message]:
|
|
26
31
|
messages = []
|
|
27
32
|
|
|
28
33
|
for code_file in found_files.python_modules:
|
|
@@ -38,7 +43,9 @@ class McCabeTool(ToolBase):
|
|
|
38
43
|
code_file,
|
|
39
44
|
"mccabe",
|
|
40
45
|
"MC0000",
|
|
41
|
-
message=
|
|
46
|
+
message=(
|
|
47
|
+
f"Could not handle the encoding of this file: {err.encoding}" # type: ignore[attr-defined]
|
|
48
|
+
),
|
|
42
49
|
)
|
|
43
50
|
)
|
|
44
51
|
continue
|
|
@@ -77,5 +84,5 @@ class McCabeTool(ToolBase):
|
|
|
77
84
|
|
|
78
85
|
return self.filter_messages(messages)
|
|
79
86
|
|
|
80
|
-
def filter_messages(self, messages):
|
|
87
|
+
def filter_messages(self, messages: list[Message]) -> list[Message]:
|
|
81
88
|
return [message for message in messages if message.code not in self.ignore_codes]
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from multiprocessing import Process, Queue
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional
|
|
2
3
|
|
|
3
4
|
from mypy import api
|
|
4
5
|
|
|
6
|
+
from prospector.finder import FileFinder
|
|
5
7
|
from prospector.message import Location, Message
|
|
6
8
|
from prospector.tools import ToolBase
|
|
7
9
|
|
|
@@ -9,31 +11,20 @@ __all__ = ("MypyTool",)
|
|
|
9
11
|
|
|
10
12
|
from prospector.tools.exceptions import BadToolConfig
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"implicit-optional",
|
|
19
|
-
"strict-optional",
|
|
20
|
-
"platform",
|
|
21
|
-
"python-2-mode",
|
|
22
|
-
"python-version",
|
|
23
|
-
"namespace-packages",
|
|
24
|
-
"check-untyped-defs",
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def format_message(message):
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from prospector.config import ProspectorConfig
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def format_message(message: str) -> Message:
|
|
19
|
+
character: Optional[int]
|
|
29
20
|
try:
|
|
30
|
-
(path,
|
|
31
|
-
line = int(
|
|
32
|
-
character = int(
|
|
21
|
+
(path, line_str, char_str, err_type, err_msg) = message.split(":", 4)
|
|
22
|
+
line = int(line_str)
|
|
23
|
+
character = int(char_str)
|
|
33
24
|
except ValueError:
|
|
34
25
|
try:
|
|
35
|
-
(path,
|
|
36
|
-
line = int(
|
|
26
|
+
(path, line_str, err_type, err_msg) = message.split(":", 3)
|
|
27
|
+
line = int(line_str)
|
|
37
28
|
character = None
|
|
38
29
|
except ValueError:
|
|
39
30
|
(path, err_type, err_msg) = message.split(":", 2)
|
|
@@ -54,7 +45,9 @@ def format_message(message):
|
|
|
54
45
|
)
|
|
55
46
|
|
|
56
47
|
|
|
57
|
-
def _run_in_subprocess(
|
|
48
|
+
def _run_in_subprocess(
|
|
49
|
+
q: "Queue[tuple[str, str]]", cmd: Callable[[list[str]], tuple[str, str]], paths: list[str]
|
|
50
|
+
) -> None:
|
|
58
51
|
"""
|
|
59
52
|
This function exists only to be called by multiprocessing.Process as using
|
|
60
53
|
lambda is forbidden
|
|
@@ -63,76 +56,46 @@ def _run_in_subprocess(q, cmd, paths):
|
|
|
63
56
|
|
|
64
57
|
|
|
65
58
|
class MypyTool(ToolBase):
|
|
66
|
-
def __init__(self, *args, **kwargs):
|
|
59
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
67
60
|
super().__init__(*args, **kwargs)
|
|
68
61
|
self.checker = api
|
|
69
62
|
self.options = ["--show-column-numbers", "--no-error-summary"]
|
|
70
63
|
self.use_dmypy = False
|
|
71
64
|
|
|
72
|
-
def configure(self, prospector_config, _):
|
|
65
|
+
def configure(self, prospector_config: "ProspectorConfig", _: Any) -> None:
|
|
73
66
|
options = prospector_config.tool_options("mypy")
|
|
74
67
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if ignore_missing_imports:
|
|
102
|
-
self.options.append("--ignore-missing-imports")
|
|
103
|
-
|
|
104
|
-
if implict_optional:
|
|
105
|
-
self.options.append("--implict-optional")
|
|
106
|
-
|
|
107
|
-
if platform:
|
|
108
|
-
self.options.append(f"--platform {platform}")
|
|
109
|
-
|
|
110
|
-
if python_2_mode:
|
|
111
|
-
self.options.append("--py2")
|
|
112
|
-
|
|
113
|
-
if python_version:
|
|
114
|
-
self.options.append(f"--python-version {python_version}")
|
|
115
|
-
|
|
116
|
-
if strict_optional:
|
|
117
|
-
self.options.append("--strict-optional")
|
|
118
|
-
|
|
119
|
-
if namespace_packages:
|
|
120
|
-
self.options.append("--namespace-packages")
|
|
121
|
-
|
|
122
|
-
if check_untyped_defs:
|
|
123
|
-
self.options.append("--check-untyped-defs")
|
|
124
|
-
|
|
125
|
-
for list_option in LIST_OPTIONS:
|
|
126
|
-
for entry in options.get(list_option, []):
|
|
127
|
-
self.options.append(f"--{list_option}-{entry}")
|
|
128
|
-
|
|
129
|
-
def run(self, found_files):
|
|
68
|
+
self.use_dmypy = options.pop("use-dmypy", False)
|
|
69
|
+
|
|
70
|
+
# For backward compatibility
|
|
71
|
+
if "follow-imports" not in options:
|
|
72
|
+
options["follow-imports"] = "normal"
|
|
73
|
+
if "python-2-mode" in options and "py2" not in options:
|
|
74
|
+
options["py2"] = options.pop("python-2-mode")
|
|
75
|
+
|
|
76
|
+
for name, value in options.items():
|
|
77
|
+
if value is False:
|
|
78
|
+
continue
|
|
79
|
+
if value is True:
|
|
80
|
+
self.options.append(f"--{name}")
|
|
81
|
+
continue
|
|
82
|
+
if isinstance(value, (int, float, str)):
|
|
83
|
+
self.options.append(f"--{name}={value}")
|
|
84
|
+
continue
|
|
85
|
+
if isinstance(value, list):
|
|
86
|
+
for v in value:
|
|
87
|
+
self.options.append(f"--{name}-{v}")
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
raise BadToolConfig("mypy", f"The option {name} has an unsupported balue type: {type(value)}")
|
|
91
|
+
|
|
92
|
+
def run(self, found_files: FileFinder) -> list[Message]:
|
|
130
93
|
paths = [str(path) for path in found_files.python_modules]
|
|
131
94
|
paths.extend(self.options)
|
|
132
95
|
if self.use_dmypy:
|
|
133
96
|
# Due to dmypy messing with stdout/stderr we call it in a separate
|
|
134
97
|
# process
|
|
135
|
-
q = Queue(1)
|
|
98
|
+
q: Queue[str] = Queue(1)
|
|
136
99
|
p = Process(target=_run_in_subprocess, args=(q, self.checker.run_dmypy, ["run", "--"] + paths))
|
|
137
100
|
p.start()
|
|
138
101
|
result = q.get()
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from prospector.tools.base import ToolBase
|
|
3
6
|
|
|
4
7
|
try: # Python >= 3.11
|
|
5
8
|
import re._constants as sre_constants
|
|
6
9
|
except ImportError:
|
|
7
|
-
import sre_constants
|
|
10
|
+
import sre_constants # pylint: disable=deprecated-module
|
|
8
11
|
|
|
9
12
|
import yaml
|
|
10
13
|
|
|
11
14
|
from prospector.finder import FileFinder
|
|
12
15
|
from prospector.message import Location, Message
|
|
13
16
|
from prospector.profiles import AUTO_LOADED_PROFILES
|
|
14
|
-
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from prospector.config import ProspectorConfig
|
|
20
|
+
|
|
15
21
|
|
|
16
22
|
PROFILE_IS_EMPTY = "profile-is-empty"
|
|
17
23
|
CONFIG_SETTING_SHOULD_BE_LIST = "should-be-list"
|
|
@@ -26,7 +32,9 @@ CONFIG_DEPRECATED_CODE = "deprecated-tool-code"
|
|
|
26
32
|
__all__ = ("ProfileValidationTool",)
|
|
27
33
|
|
|
28
34
|
|
|
29
|
-
def _tool_names(with_deprecated: bool = True):
|
|
35
|
+
def _tool_names(with_deprecated: bool = True) -> list[str]:
|
|
36
|
+
from prospector.tools import DEPRECATED_TOOL_NAMES, TOOLS # pylint: disable=import-outside-toplevel
|
|
37
|
+
|
|
30
38
|
tools = list(TOOLS)
|
|
31
39
|
if with_deprecated:
|
|
32
40
|
tools += DEPRECATED_TOOL_NAMES.keys()
|
|
@@ -49,27 +57,27 @@ class ProfileValidationTool(ToolBase):
|
|
|
49
57
|
)
|
|
50
58
|
ALL_SETTINGS = LIST_SETTINGS + BOOL_SETTINGS + OTHER_SETTINGS
|
|
51
59
|
|
|
52
|
-
def __init__(self):
|
|
60
|
+
def __init__(self) -> None:
|
|
53
61
|
self.to_check = set(AUTO_LOADED_PROFILES)
|
|
54
|
-
self.ignore_codes =
|
|
62
|
+
self.ignore_codes: list[str] = []
|
|
55
63
|
|
|
56
|
-
def configure(self, prospector_config, found_files):
|
|
64
|
+
def configure(self, prospector_config: "ProspectorConfig", found_files: FileFinder) -> None:
|
|
57
65
|
for profile in prospector_config.config.profiles:
|
|
58
66
|
self.to_check.add(profile)
|
|
59
67
|
|
|
60
68
|
self.ignore_codes = prospector_config.get_disabled_messages("profile-validator")
|
|
61
69
|
|
|
62
|
-
def validate(self, filepath: Path):
|
|
70
|
+
def validate(self, filepath: Path) -> list[Message]:
|
|
63
71
|
# pylint: disable=too-many-locals
|
|
64
72
|
# TODO: this should be broken down into smaller pieces
|
|
65
|
-
messages = []
|
|
73
|
+
messages: list[Message] = []
|
|
66
74
|
|
|
67
75
|
with filepath.open() as profile_file:
|
|
68
76
|
_file_contents = profile_file.read()
|
|
69
77
|
parsed = yaml.safe_load(_file_contents)
|
|
70
78
|
raw_contents = _file_contents.split("\n")
|
|
71
79
|
|
|
72
|
-
def add_message(code, message, setting):
|
|
80
|
+
def add_message(code: str, message: str, setting: str) -> None:
|
|
73
81
|
if code in self.ignore_codes:
|
|
74
82
|
return
|
|
75
83
|
line = -1
|
|
@@ -77,9 +85,8 @@ class ProfileValidationTool(ToolBase):
|
|
|
77
85
|
if setting in fileline:
|
|
78
86
|
line = number + 1
|
|
79
87
|
break
|
|
80
|
-
location = Location(filepath, None, None, line, 0
|
|
81
|
-
|
|
82
|
-
messages.append(message)
|
|
88
|
+
location = Location(filepath, None, None, line, 0)
|
|
89
|
+
messages.append(Message("profile-validator", code, location, message))
|
|
83
90
|
|
|
84
91
|
if parsed is None:
|
|
85
92
|
# this happens if a completely empty profile is found
|
|
@@ -159,7 +166,7 @@ class ProfileValidationTool(ToolBase):
|
|
|
159
166
|
if not isinstance(parsed[key], (tuple, list)):
|
|
160
167
|
add_message(CONFIG_SETTING_SHOULD_BE_LIST, f'"{key}" should be a list', key)
|
|
161
168
|
|
|
162
|
-
for key in parsed
|
|
169
|
+
for key in parsed:
|
|
163
170
|
if key not in ProfileValidationTool.ALL_SETTINGS and key not in _tool_names():
|
|
164
171
|
add_message(
|
|
165
172
|
CONFIG_UNKNOWN_SETTING,
|
|
@@ -170,7 +177,7 @@ class ProfileValidationTool(ToolBase):
|
|
|
170
177
|
if "pep257" in parsed:
|
|
171
178
|
add_message(
|
|
172
179
|
CONFIG_DEPRECATED_CODE,
|
|
173
|
-
"pep257 tool has been renamed to 'pydocstyle'.
|
|
180
|
+
"pep257 tool has been renamed to 'pydocstyle'. The name pep257 will be removed in prospector 2.0+.",
|
|
174
181
|
"pep257",
|
|
175
182
|
)
|
|
176
183
|
|
|
@@ -191,6 +198,8 @@ class ProfileValidationTool(ToolBase):
|
|
|
191
198
|
)
|
|
192
199
|
|
|
193
200
|
if "pyflakes" in parsed:
|
|
201
|
+
from prospector.tools import pyflakes # pylint: disable=import-outside-toplevel
|
|
202
|
+
|
|
194
203
|
for code in parsed["pyflakes"].get("enable", []) + parsed["pyflakes"].get("disable", []):
|
|
195
204
|
if code in pyflakes.LEGACY_CODE_MAP:
|
|
196
205
|
_legacy = pyflakes.LEGACY_CODE_MAP[code]
|
|
@@ -202,7 +211,7 @@ class ProfileValidationTool(ToolBase):
|
|
|
202
211
|
|
|
203
212
|
return messages
|
|
204
213
|
|
|
205
|
-
def run(self, found_files: FileFinder):
|
|
214
|
+
def run(self, found_files: FileFinder) -> list[Message]:
|
|
206
215
|
messages = []
|
|
207
216
|
for filepath in found_files.files:
|
|
208
217
|
for possible in self.to_check:
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import codecs
|
|
2
2
|
import os
|
|
3
3
|
import re
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
4
7
|
|
|
5
8
|
from pep8ext_naming import NamingChecker
|
|
6
9
|
from pycodestyle import PROJECT_CONFIG, USER_CONFIG, BaseReport, StyleGuide, register_check
|
|
@@ -9,15 +12,18 @@ from prospector.finder import FileFinder
|
|
|
9
12
|
from prospector.message import Location, Message
|
|
10
13
|
from prospector.tools.base import ToolBase
|
|
11
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from prospector.config import ProspectorConfig
|
|
17
|
+
|
|
12
18
|
__all__ = ("PycodestyleTool",)
|
|
13
19
|
|
|
14
20
|
|
|
15
21
|
class ProspectorReport(BaseReport):
|
|
16
|
-
def __init__(self, *args, **kwargs):
|
|
22
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
17
23
|
super().__init__(*args, **kwargs)
|
|
18
|
-
self._prospector_messages = []
|
|
24
|
+
self._prospector_messages: list[Message] = []
|
|
19
25
|
|
|
20
|
-
def error(self, line_number, offset, text, check):
|
|
26
|
+
def error(self, line_number: Optional[int], offset: int, text: str, check: str) -> None:
|
|
21
27
|
code = super().error(
|
|
22
28
|
line_number,
|
|
23
29
|
offset,
|
|
@@ -53,12 +59,12 @@ class ProspectorReport(BaseReport):
|
|
|
53
59
|
|
|
54
60
|
self._prospector_messages.append(message)
|
|
55
61
|
|
|
56
|
-
def get_messages(self):
|
|
62
|
+
def get_messages(self) -> list[Message]:
|
|
57
63
|
return self._prospector_messages
|
|
58
64
|
|
|
59
65
|
|
|
60
66
|
class ProspectorStyleGuide(StyleGuide):
|
|
61
|
-
def __init__(self, config, found_files, *args, **kwargs):
|
|
67
|
+
def __init__(self, config: "ProspectorConfig", found_files: FileFinder, *args: Any, **kwargs: Any) -> None:
|
|
62
68
|
self._config = config
|
|
63
69
|
self._files = found_files
|
|
64
70
|
self._module_paths = found_files.python_modules
|
|
@@ -68,7 +74,7 @@ class ProspectorStyleGuide(StyleGuide):
|
|
|
68
74
|
|
|
69
75
|
super().__init__(*args, **kwargs)
|
|
70
76
|
|
|
71
|
-
def excluded(self, filename, parent=None):
|
|
77
|
+
def excluded(self, filename: str, parent: Optional[str] = None) -> bool:
|
|
72
78
|
if super().excluded(filename, parent):
|
|
73
79
|
return True
|
|
74
80
|
|
|
@@ -82,11 +88,11 @@ class ProspectorStyleGuide(StyleGuide):
|
|
|
82
88
|
|
|
83
89
|
|
|
84
90
|
class PycodestyleTool(ToolBase):
|
|
85
|
-
|
|
86
|
-
super().__init__(*args, **kwargs)
|
|
87
|
-
self.checker = None
|
|
91
|
+
checker: Optional[ProspectorStyleGuide] = None
|
|
88
92
|
|
|
89
|
-
def configure(
|
|
93
|
+
def configure(
|
|
94
|
+
self, prospector_config: "ProspectorConfig", found_files: FileFinder
|
|
95
|
+
) -> Optional[tuple[Optional[str], Optional[Iterable[Message]]]]:
|
|
90
96
|
# figure out if we should use a pre-existing config file
|
|
91
97
|
# such as setup.cfg or tox.ini
|
|
92
98
|
external_config = None
|
|
@@ -97,18 +103,18 @@ class PycodestyleTool(ToolBase):
|
|
|
97
103
|
if prospector_config.use_external_config("pycodestyle"):
|
|
98
104
|
use_config = True
|
|
99
105
|
|
|
100
|
-
paths = [os.path.join(prospector_config.workdir, name) for name in PROJECT_CONFIG]
|
|
106
|
+
paths: list[Union[str, Path]] = [os.path.join(prospector_config.workdir, name) for name in PROJECT_CONFIG]
|
|
101
107
|
paths.append(USER_CONFIG)
|
|
102
108
|
ext_loc = prospector_config.external_config_location("pycodestyle")
|
|
103
109
|
if ext_loc is not None:
|
|
104
|
-
paths = [ext_loc] + paths
|
|
110
|
+
paths = [ext_loc] + paths # type: ignore[assignment,operator]
|
|
105
111
|
|
|
106
112
|
for conf_path in paths:
|
|
107
113
|
if os.path.exists(conf_path) and os.path.isfile(conf_path):
|
|
108
114
|
# this file exists - but does it have pep8 or pycodestyle config in it?
|
|
109
115
|
# TODO: Remove this
|
|
110
116
|
header = re.compile(r"\[(pep8|pycodestyle)\]")
|
|
111
|
-
with codecs.open(conf_path) as conf_file:
|
|
117
|
+
with codecs.open(str(conf_path)) as conf_file:
|
|
112
118
|
if any(header.search(line) for line in conf_file.readlines()):
|
|
113
119
|
external_config = conf_path
|
|
114
120
|
break
|
|
@@ -133,7 +139,7 @@ class PycodestyleTool(ToolBase):
|
|
|
133
139
|
if "max-line-length" in prospector_config.tool_options("pycodestyle"):
|
|
134
140
|
self.checker.options.max_line_length = prospector_config.tool_options("pycodestyle")["max-line-length"]
|
|
135
141
|
else:
|
|
136
|
-
configured_by = "Configuration found at
|
|
142
|
+
configured_by = f"Configuration found at {external_config}"
|
|
137
143
|
|
|
138
144
|
# if we have a command line --max-line-length argument, that
|
|
139
145
|
# overrules everything
|
|
@@ -143,7 +149,8 @@ class PycodestyleTool(ToolBase):
|
|
|
143
149
|
|
|
144
150
|
return configured_by, []
|
|
145
151
|
|
|
146
|
-
def run(self, _):
|
|
152
|
+
def run(self, _: Any) -> list[Message]:
|
|
153
|
+
assert self.checker is not None
|
|
147
154
|
report = self.checker.check_files()
|
|
148
155
|
return report.get_messages()
|
|
149
156
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any
|
|
2
|
+
|
|
1
3
|
from pydocstyle.checker import AllError, ConventionChecker
|
|
2
4
|
|
|
3
5
|
from prospector.encoding import CouldNotHandleEncoding, read_py_file
|
|
@@ -5,19 +7,23 @@ from prospector.finder import FileFinder
|
|
|
5
7
|
from prospector.message import Location, Message, make_tool_error_message
|
|
6
8
|
from prospector.tools.base import ToolBase
|
|
7
9
|
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from prospector.config import ProspectorConfig
|
|
12
|
+
|
|
13
|
+
|
|
8
14
|
__all__ = ("PydocstyleTool",)
|
|
9
15
|
|
|
10
16
|
|
|
11
17
|
class PydocstyleTool(ToolBase):
|
|
12
|
-
def __init__(self, *args, **kwargs):
|
|
18
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
13
19
|
super().__init__(*args, **kwargs)
|
|
14
|
-
self._code_files = []
|
|
15
|
-
self.ignore_codes =
|
|
20
|
+
self._code_files: list[str] = []
|
|
21
|
+
self.ignore_codes: list[str] = []
|
|
16
22
|
|
|
17
|
-
def configure(self, prospector_config, found_files):
|
|
23
|
+
def configure(self, prospector_config: "ProspectorConfig", found_files: FileFinder) -> None:
|
|
18
24
|
self.ignore_codes = prospector_config.get_disabled_messages("pydocstyle")
|
|
19
25
|
|
|
20
|
-
def run(self, found_files: FileFinder):
|
|
26
|
+
def run(self, found_files: FileFinder) -> list[Message]:
|
|
21
27
|
messages = []
|
|
22
28
|
|
|
23
29
|
checker = ConventionChecker()
|
|
@@ -61,5 +67,5 @@ class PydocstyleTool(ToolBase):
|
|
|
61
67
|
|
|
62
68
|
return self.filter_messages(messages)
|
|
63
69
|
|
|
64
|
-
def filter_messages(self, messages):
|
|
70
|
+
def filter_messages(self, messages: list[Message]) -> list[Message]:
|
|
65
71
|
return [message for message in messages if message.code not in self.ignore_codes]
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
2
|
+
|
|
1
3
|
from pyflakes.api import checkPath
|
|
4
|
+
from pyflakes.messages import Message as FlakeMessage
|
|
2
5
|
from pyflakes.reporter import Reporter
|
|
3
6
|
|
|
7
|
+
from prospector.finder import FileFinder
|
|
4
8
|
from prospector.message import Location, Message
|
|
5
9
|
from prospector.tools.base import ToolBase
|
|
6
10
|
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from prospector.config import ProspectorConfig
|
|
7
13
|
__all__ = ("PyFlakesTool",)
|
|
8
14
|
|
|
9
15
|
|
|
10
16
|
# Prospector uses the same pyflakes codes as flake8 defines,
|
|
11
17
|
# see https://flake8.pycqa.org/en/latest/user/error-codes.html
|
|
12
|
-
# and https://
|
|
18
|
+
# and https://github.com/PyCQA/flake8/blob/e817c63a/src/flake8/plugins/pyflakes.py
|
|
13
19
|
_MESSAGE_CODES = {
|
|
14
20
|
"UnusedImport": "F401",
|
|
15
21
|
"ImportShadowedByLoopVar": "F402",
|
|
@@ -81,13 +87,22 @@ LEGACY_CODE_MAP = {
|
|
|
81
87
|
|
|
82
88
|
|
|
83
89
|
class ProspectorReporter(Reporter):
|
|
84
|
-
def __init__(self, ignore=None):
|
|
90
|
+
def __init__(self, ignore: Optional[list[str]] = None) -> None:
|
|
85
91
|
super().__init__(None, None)
|
|
86
|
-
self._messages = []
|
|
92
|
+
self._messages: list[Message] = []
|
|
87
93
|
self.ignore = ignore or ()
|
|
88
94
|
|
|
89
|
-
# pylint: disable=too-many-arguments
|
|
90
|
-
|
|
95
|
+
def record_message( # pylint: disable=too-many-arguments
|
|
96
|
+
self,
|
|
97
|
+
filename: str,
|
|
98
|
+
line: Optional[int] = None,
|
|
99
|
+
character: Optional[int] = None,
|
|
100
|
+
code: Optional[str] = None,
|
|
101
|
+
message: Optional[str] = None,
|
|
102
|
+
) -> None:
|
|
103
|
+
assert message is not None
|
|
104
|
+
assert code is not None
|
|
105
|
+
|
|
91
106
|
code = code or "F999"
|
|
92
107
|
if code in self.ignore:
|
|
93
108
|
return
|
|
@@ -99,15 +114,16 @@ class ProspectorReporter(Reporter):
|
|
|
99
114
|
line=line,
|
|
100
115
|
character=character,
|
|
101
116
|
)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
117
|
+
self._messages.append(
|
|
118
|
+
Message(
|
|
119
|
+
source="pyflakes",
|
|
120
|
+
code=code,
|
|
121
|
+
location=location,
|
|
122
|
+
message=message,
|
|
123
|
+
)
|
|
107
124
|
)
|
|
108
|
-
self._messages.append(message)
|
|
109
125
|
|
|
110
|
-
def unexpectedError(self, filename, msg):
|
|
126
|
+
def unexpectedError(self, filename: str, msg: str) -> None:
|
|
111
127
|
self.record_message(
|
|
112
128
|
filename=filename,
|
|
113
129
|
code="F999",
|
|
@@ -115,7 +131,7 @@ class ProspectorReporter(Reporter):
|
|
|
115
131
|
)
|
|
116
132
|
|
|
117
133
|
# pylint: disable=too-many-arguments
|
|
118
|
-
def syntaxError(self, filename, msg, lineno, offset, text):
|
|
134
|
+
def syntaxError(self, filename: str, msg: str, lineno: int, offset: int, text: str) -> None:
|
|
119
135
|
self.record_message(
|
|
120
136
|
filename=filename,
|
|
121
137
|
line=lineno,
|
|
@@ -124,7 +140,7 @@ class ProspectorReporter(Reporter):
|
|
|
124
140
|
message=msg,
|
|
125
141
|
)
|
|
126
142
|
|
|
127
|
-
def flake(self, message):
|
|
143
|
+
def flake(self, message: FlakeMessage) -> None:
|
|
128
144
|
code = _MESSAGE_CODES.get(message.__class__.__name__, "F999")
|
|
129
145
|
|
|
130
146
|
self.record_message(
|
|
@@ -135,21 +151,21 @@ class ProspectorReporter(Reporter):
|
|
|
135
151
|
message=message.message % message.message_args,
|
|
136
152
|
)
|
|
137
153
|
|
|
138
|
-
def get_messages(self):
|
|
154
|
+
def get_messages(self) -> list[Message]:
|
|
139
155
|
return self._messages
|
|
140
156
|
|
|
141
157
|
|
|
142
158
|
class PyFlakesTool(ToolBase):
|
|
143
|
-
def __init__(self, *args, **kwargs):
|
|
159
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
144
160
|
super().__init__(*args, **kwargs)
|
|
145
|
-
self.ignore_codes =
|
|
161
|
+
self.ignore_codes: list[str] = []
|
|
146
162
|
|
|
147
|
-
def configure(self, prospector_config, _):
|
|
163
|
+
def configure(self, prospector_config: "ProspectorConfig", _: Any) -> None:
|
|
148
164
|
ignores = prospector_config.get_disabled_messages("pyflakes")
|
|
149
165
|
# convert old style to new
|
|
150
166
|
self.ignore_codes = [LEGACY_CODE_MAP.get(code, code) for code in ignores]
|
|
151
167
|
|
|
152
|
-
def run(self, found_files):
|
|
168
|
+
def run(self, found_files: FileFinder) -> list[Message]:
|
|
153
169
|
reporter = ProspectorReporter(ignore=self.ignore_codes)
|
|
154
170
|
for filepath in found_files.python_modules:
|
|
155
171
|
checkPath(str(filepath.absolute()), reporter)
|