prospector 1.10.3__py3-none-any.whl → 1.13.2__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.
Files changed (50) hide show
  1. prospector/autodetect.py +10 -9
  2. prospector/blender.py +18 -12
  3. prospector/config/__init__.py +66 -49
  4. prospector/config/configuration.py +17 -14
  5. prospector/config/datatype.py +1 -1
  6. prospector/encoding.py +2 -2
  7. prospector/exceptions.py +1 -6
  8. prospector/finder.py +9 -8
  9. prospector/formatters/__init__.py +1 -1
  10. prospector/formatters/base.py +17 -11
  11. prospector/formatters/base_summary.py +43 -0
  12. prospector/formatters/emacs.py +5 -5
  13. prospector/formatters/grouped.py +9 -7
  14. prospector/formatters/json.py +3 -2
  15. prospector/formatters/pylint.py +41 -9
  16. prospector/formatters/text.py +10 -58
  17. prospector/formatters/vscode.py +17 -7
  18. prospector/formatters/xunit.py +6 -7
  19. prospector/formatters/yaml.py +4 -2
  20. prospector/message.py +32 -14
  21. prospector/pathutils.py +3 -10
  22. prospector/postfilter.py +9 -8
  23. prospector/profiles/exceptions.py +14 -11
  24. prospector/profiles/profile.py +70 -59
  25. prospector/run.py +20 -18
  26. prospector/suppression.py +19 -13
  27. prospector/tools/__init__.py +19 -13
  28. prospector/tools/bandit/__init__.py +27 -15
  29. prospector/tools/base.py +11 -3
  30. prospector/tools/dodgy/__init__.py +7 -3
  31. prospector/tools/mccabe/__init__.py +13 -6
  32. prospector/tools/mypy/__init__.py +44 -76
  33. prospector/tools/profile_validator/__init__.py +24 -15
  34. prospector/tools/pycodestyle/__init__.py +22 -15
  35. prospector/tools/pydocstyle/__init__.py +12 -6
  36. prospector/tools/pyflakes/__init__.py +35 -19
  37. prospector/tools/pylint/__init__.py +57 -31
  38. prospector/tools/pylint/collector.py +3 -5
  39. prospector/tools/pylint/linter.py +19 -14
  40. prospector/tools/pyright/__init__.py +18 -7
  41. prospector/tools/pyroma/__init__.py +10 -6
  42. prospector/tools/ruff/__init__.py +84 -0
  43. prospector/tools/utils.py +25 -19
  44. prospector/tools/vulture/__init__.py +25 -15
  45. {prospector-1.10.3.dist-info → prospector-1.13.2.dist-info}/METADATA +10 -11
  46. prospector-1.13.2.dist-info/RECORD +71 -0
  47. prospector-1.10.3.dist-info/RECORD +0 -69
  48. {prospector-1.10.3.dist-info → prospector-1.13.2.dist-info}/LICENSE +0 -0
  49. {prospector-1.10.3.dist-info → prospector-1.13.2.dist-info}/WHEEL +0 -0
  50. {prospector-1.10.3.dist-info → prospector-1.13.2.dist-info}/entry_points.txt +0 -0
@@ -1,49 +1,61 @@
1
+ from typing import TYPE_CHECKING, Any, Optional
2
+
1
3
  from bandit.cli.main import _get_profile, _init_extensions
2
4
  from bandit.core.config import BanditConfig
3
5
  from bandit.core.constants import RANKING
4
6
  from bandit.core.manager import BanditManager
5
7
 
8
+ from prospector.finder import FileFinder
6
9
  from prospector.message import Location, Message
7
10
  from prospector.tools.base import ToolBase
8
11
 
12
+ if TYPE_CHECKING:
13
+ from prospector.config import ProspectorConfig
14
+
9
15
 
10
16
  class BanditTool(ToolBase):
11
- def __init__(self, *args, **kwargs):
12
- super().__init__(*args, **kwargs)
13
- self.manager = None
14
- self.profile = None
15
- self.config_file = None
16
- self.agg_type = "file"
17
- self.severity = 0
18
- self.confidence = 0
19
-
20
- def configure(self, prospector_config, _):
17
+ manager: Optional[BanditManager] = None
18
+ profile: Optional[str] = None
19
+ config_file: Optional[str] = None
20
+ agg_type = "file"
21
+ severity = 0
22
+ confidence = 0
23
+
24
+ def configure(self, prospector_config: "ProspectorConfig", _: Any) -> None:
21
25
  options = prospector_config.tool_options("bandit")
22
26
 
23
27
  if "profile" in options:
24
- self.profile = options["profile"]
28
+ self.profile = options.pop("profile")
25
29
 
26
30
  if "config" in options:
27
- self.config_file = options["config"]
31
+ self.config_file = options.pop("config")
28
32
 
29
33
  if "severity" in options:
30
- self.severity = options["severity"]
34
+ self.severity = options.pop("severity")
31
35
  if not 0 <= self.severity <= 2:
32
36
  raise ValueError(f"severity {self.severity!r} must be between 0 and 2")
33
37
 
34
38
  if "confidence" in options:
35
- self.confidence = options["confidence"]
39
+ self.confidence = options.pop("confidence")
36
40
  if not 0 <= self.confidence <= 2:
37
41
  raise ValueError(f"confidence {self.confidence!r} must be between 0 and 2")
38
42
 
39
43
  b_conf = BanditConfig(config_file=self.config_file)
44
+ disabled_messages = prospector_config.get_disabled_messages("bandit")
45
+ if disabled_messages:
46
+ b_conf.config.setdefault("skips", []).extend(disabled_messages)
47
+ if options:
48
+ b_conf.config.update(options)
49
+ b_conf.validate(path="<prospector config>")
40
50
  profile = _get_profile(b_conf, self.profile, self.config_file)
41
51
  extension_mgr = _init_extensions()
42
52
  extension_mgr.validate_profile(profile)
43
53
 
44
54
  self.manager = BanditManager(b_conf, None, profile=profile)
45
55
 
46
- def run(self, found_files):
56
+ def run(self, found_files: FileFinder) -> list[Message]:
57
+ assert self.manager is not None
58
+
47
59
  self.manager.files_list = sorted(found_files.files)
48
60
  self.manager.exclude_files = []
49
61
 
prospector/tools/base.py CHANGED
@@ -1,12 +1,20 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Iterable, List, Optional, Tuple
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(self, prospector_config, found_files) -> Tuple[str, Optional[Iterable[Message]]]:
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) -> List[Message]:
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
- def module_from_path(path: Path):
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=f"Could not handle the encoding of this file: {err.encoding}",
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,30 +11,20 @@ __all__ = ("MypyTool",)
9
11
 
10
12
  from prospector.tools.exceptions import BadToolConfig
11
13
 
12
- LIST_OPTIONS = ["allow", "check", "disallow", "no-check", "no-warn", "warn"]
13
- VALID_OPTIONS = LIST_OPTIONS + [
14
- "use-dmypy",
15
- "strict",
16
- "follow-imports",
17
- "ignore-missing-imports",
18
- "implicit-optional",
19
- "strict-optional",
20
- "platform",
21
- "python-2-mode",
22
- "python-version",
23
- "namespace-packages",
24
- ]
25
-
26
-
27
- 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]
28
20
  try:
29
- (path, line, char, err_type, err_msg) = message.split(":", 4)
30
- line = int(line)
31
- character = int(char)
21
+ (path, line_str, char_str, err_type, err_msg) = message.split(":", 4)
22
+ line = int(line_str)
23
+ character = int(char_str)
32
24
  except ValueError:
33
25
  try:
34
- (path, line, err_type, err_msg) = message.split(":", 3)
35
- line = int(line)
26
+ (path, line_str, err_type, err_msg) = message.split(":", 3)
27
+ line = int(line_str)
36
28
  character = None
37
29
  except ValueError:
38
30
  (path, err_type, err_msg) = message.split(":", 2)
@@ -53,7 +45,9 @@ def format_message(message):
53
45
  )
54
46
 
55
47
 
56
- def _run_in_subprocess(q, cmd, paths):
48
+ def _run_in_subprocess(
49
+ q: "Queue[tuple[str, str]]", cmd: Callable[[list[str]], tuple[str, str]], paths: list[str]
50
+ ) -> None:
57
51
  """
58
52
  This function exists only to be called by multiprocessing.Process as using
59
53
  lambda is forbidden
@@ -62,72 +56,46 @@ def _run_in_subprocess(q, cmd, paths):
62
56
 
63
57
 
64
58
  class MypyTool(ToolBase):
65
- def __init__(self, *args, **kwargs):
59
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
66
60
  super().__init__(*args, **kwargs)
67
61
  self.checker = api
68
62
  self.options = ["--show-column-numbers", "--no-error-summary"]
69
63
  self.use_dmypy = False
70
64
 
71
- def configure(self, prospector_config, _):
65
+ def configure(self, prospector_config: "ProspectorConfig", _: Any) -> None:
72
66
  options = prospector_config.tool_options("mypy")
73
67
 
74
- for option_key in options.keys():
75
- if option_key not in VALID_OPTIONS:
76
- url = "https://github.com/PyCQA/prospector/blob/master/prospector/tools/mypy/__init__.py"
77
- raise BadToolConfig(
78
- "mypy", f"Option {option_key} is not valid. " f"See the list of possible options: {url}"
79
- )
80
-
81
- self.use_dmypy = options.get("use-dmypy", False)
82
-
83
- strict = options.get("strict", False)
84
-
85
- follow_imports = options.get("follow-imports", "normal")
86
- ignore_missing_imports = options.get("ignore-missing-imports", False)
87
- implict_optional = options.get("implict-optional", False)
88
- platform = options.get("platform", None)
89
- python_2_mode = options.get("python-2-mode", False)
90
- python_version = options.get("python-version", None)
91
- strict_optional = options.get("strict-optional", False)
92
- namespace_packages = options.get("namespace-packages", False)
93
-
94
- self.options.append(f"--follow-imports={follow_imports}")
95
-
96
- if strict:
97
- self.options.append("--strict")
98
-
99
- if ignore_missing_imports:
100
- self.options.append("--ignore-missing-imports")
101
-
102
- if implict_optional:
103
- self.options.append("--implict-optional")
104
-
105
- if platform:
106
- self.options.append(f"--platform {platform}")
107
-
108
- if python_2_mode:
109
- self.options.append("--py2")
110
-
111
- if python_version:
112
- self.options.append(f"--python-version {python_version}")
113
-
114
- if strict_optional:
115
- self.options.append("--strict-optional")
116
-
117
- if namespace_packages:
118
- self.options.append("--namespace-packages")
119
-
120
- for list_option in LIST_OPTIONS:
121
- for entry in options.get(list_option, []):
122
- self.options.append(f"--{list_option}-{entry}")
123
-
124
- 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]:
125
93
  paths = [str(path) for path in found_files.python_modules]
126
94
  paths.extend(self.options)
127
95
  if self.use_dmypy:
128
96
  # Due to dmypy messing with stdout/stderr we call it in a separate
129
97
  # process
130
- q = Queue(1)
98
+ q: Queue[str] = Queue(1)
131
99
  p = Process(target=_run_in_subprocess, args=(q, self.checker.run_dmypy, ["run", "--"] + paths))
132
100
  p.start()
133
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
- from prospector.tools import DEPRECATED_TOOL_NAMES, TOOLS, ToolBase, pyflakes
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): # noqa
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, False)
81
- message = Message("profile-validator", code, location, message)
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.keys():
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'. " "The name pep257 will be removed in prospector 2.0+.",
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
- def __init__(self, *args, **kwargs):
86
- super().__init__(*args, **kwargs)
87
- self.checker = None
91
+ checker: Optional[ProspectorStyleGuide] = None
88
92
 
89
- def configure(self, prospector_config, found_files: FileFinder):
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 %s" % external_config
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]