prospector 1.7.5__tar.gz → 1.14.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.
Files changed (88) hide show
  1. {prospector-1.7.5 → prospector-1.14.0}/PKG-INFO +54 -24
  2. {prospector-1.7.5 → prospector-1.14.0}/README.rst +33 -8
  3. prospector-1.14.0/prospector/__main__.py +4 -0
  4. {prospector-1.7.5 → prospector-1.14.0}/prospector/autodetect.py +26 -28
  5. {prospector-1.7.5 → prospector-1.14.0}/prospector/blender.py +19 -12
  6. prospector-1.14.0/prospector/blender_combinations.yaml +287 -0
  7. prospector-1.14.0/prospector/compat.py +14 -0
  8. {prospector-1.7.5 → prospector-1.14.0}/prospector/config/__init__.py +110 -62
  9. {prospector-1.7.5 → prospector-1.14.0}/prospector/config/configuration.py +20 -15
  10. {prospector-1.7.5 → prospector-1.14.0}/prospector/config/datatype.py +3 -3
  11. {prospector-1.7.5 → prospector-1.14.0}/prospector/encoding.py +13 -8
  12. {prospector-1.7.5 → prospector-1.14.0}/prospector/exceptions.py +7 -10
  13. prospector-1.14.0/prospector/finder.py +132 -0
  14. {prospector-1.7.5 → prospector-1.14.0}/prospector/formatters/__init__.py +5 -3
  15. prospector-1.14.0/prospector/formatters/base.py +56 -0
  16. prospector-1.14.0/prospector/formatters/base_summary.py +43 -0
  17. {prospector-1.7.5 → prospector-1.14.0}/prospector/formatters/emacs.py +5 -10
  18. {prospector-1.7.5 → prospector-1.14.0}/prospector/formatters/grouped.py +9 -8
  19. {prospector-1.7.5 → prospector-1.14.0}/prospector/formatters/json.py +4 -7
  20. prospector-1.14.0/prospector/formatters/pylint.py +80 -0
  21. prospector-1.14.0/prospector/formatters/pylint_parseable.py +74 -0
  22. prospector-1.14.0/prospector/formatters/text.py +52 -0
  23. {prospector-1.7.5 → prospector-1.14.0}/prospector/formatters/vscode.py +17 -8
  24. {prospector-1.7.5 → prospector-1.14.0}/prospector/formatters/xunit.py +6 -7
  25. {prospector-1.7.5 → prospector-1.14.0}/prospector/formatters/yaml.py +5 -4
  26. prospector-1.14.0/prospector/message.py +119 -0
  27. prospector-1.14.0/prospector/pathutils.py +37 -0
  28. {prospector-1.7.5 → prospector-1.14.0}/prospector/postfilter.py +16 -11
  29. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/__init__.py +2 -2
  30. prospector-1.14.0/prospector/profiles/exceptions.py +31 -0
  31. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profile.py +121 -69
  32. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/no_test_warnings.yaml +1 -0
  33. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/strictness_high.yaml +7 -0
  34. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/strictness_medium.yaml +4 -0
  35. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/strictness_veryhigh.yaml +4 -0
  36. {prospector-1.7.5 → prospector-1.14.0}/prospector/run.py +41 -43
  37. prospector-1.14.0/prospector/suppression.py +157 -0
  38. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/__init__.py +30 -11
  39. prospector-1.14.0/prospector/tools/bandit/__init__.py +81 -0
  40. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/base.py +13 -2
  41. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/dodgy/__init__.py +14 -20
  42. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/mccabe/__init__.py +15 -13
  43. prospector-1.14.0/prospector/tools/mypy/__init__.py +107 -0
  44. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/profile_validator/__init__.py +46 -36
  45. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/pycodestyle/__init__.py +35 -45
  46. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/pydocstyle/__init__.py +17 -18
  47. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/pyflakes/__init__.py +36 -21
  48. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/pylint/__init__.py +90 -61
  49. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/pylint/collector.py +16 -17
  50. prospector-1.14.0/prospector/tools/pylint/linter.py +45 -0
  51. prospector-1.14.0/prospector/tools/pyright/__init__.py +99 -0
  52. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/pyroma/__init__.py +32 -21
  53. prospector-1.14.0/prospector/tools/ruff/__init__.py +86 -0
  54. prospector-1.14.0/prospector/tools/utils.py +59 -0
  55. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/vulture/__init__.py +26 -16
  56. prospector-1.14.0/pyproject.toml +110 -0
  57. prospector-1.14.0/setup.py +81 -0
  58. prospector-1.7.5/prospector/blender_combinations.yaml +0 -257
  59. prospector-1.7.5/prospector/finder.py +0 -218
  60. prospector-1.7.5/prospector/formatters/base.py +0 -14
  61. prospector-1.7.5/prospector/formatters/pylint.py +0 -45
  62. prospector-1.7.5/prospector/formatters/text.py +0 -101
  63. prospector-1.7.5/prospector/message.py +0 -112
  64. prospector-1.7.5/prospector/pathutils.py +0 -33
  65. prospector-1.7.5/prospector/profiles/exceptions.py +0 -28
  66. prospector-1.7.5/prospector/suppression.py +0 -118
  67. prospector-1.7.5/prospector/tools/bandit/__init__.py +0 -62
  68. prospector-1.7.5/prospector/tools/frosted/__init__.py +0 -82
  69. prospector-1.7.5/prospector/tools/mypy/__init__.py +0 -116
  70. prospector-1.7.5/prospector/tools/pylint/linter.py +0 -26
  71. prospector-1.7.5/prospector/tools/utils.py +0 -45
  72. prospector-1.7.5/pyproject.toml +0 -99
  73. prospector-1.7.5/setup.py +0 -77
  74. {prospector-1.7.5 → prospector-1.14.0}/LICENSE +0 -0
  75. {prospector-1.7.5 → prospector-1.14.0}/prospector/__init__.py +0 -0
  76. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/default.yaml +0 -0
  77. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/doc_warnings.yaml +0 -0
  78. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/flake8.yaml +0 -0
  79. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/full_pep8.yaml +0 -0
  80. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/member_warnings.yaml +0 -0
  81. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/no_doc_warnings.yaml +0 -0
  82. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/no_member_warnings.yaml +0 -0
  83. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/no_pep8.yaml +0 -0
  84. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/strictness_low.yaml +0 -0
  85. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/strictness_none.yaml +0 -0
  86. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/strictness_verylow.yaml +0 -0
  87. {prospector-1.7.5 → prospector-1.14.0}/prospector/profiles/profiles/test_warnings.yaml +0 -0
  88. {prospector-1.7.5 → prospector-1.14.0}/prospector/tools/exceptions.py +0 -0
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prospector
3
- Version: 1.7.5
4
- Summary:
3
+ Version: 1.14.0
4
+ Summary: Prospector is a tool to analyse Python code by aggregating the result of other tools.
5
5
  Home-page: http://prospector.readthedocs.io
6
- License: GPLv2
6
+ License: GPLv2+
7
7
  Keywords: pylint,prospector,static code analysis
8
8
  Author: Carl Crowder
9
9
  Author-email: git@carlcrowder.com
10
10
  Maintainer: Carl Crowder
11
11
  Maintainer-email: git@carlcrowder.com
12
- Requires-Python: >=3.6.2,<4.0
12
+ Requires-Python: >=3.9,<4.0
13
13
  Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Environment :: Console
15
15
  Classifier: Intended Audience :: Developers
@@ -17,35 +17,40 @@ Classifier: License :: OSI Approved :: GNU General Public License v2 or later (G
17
17
  Classifier: License :: Other/Proprietary License
18
18
  Classifier: Operating System :: Unix
19
19
  Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
20
22
  Classifier: Programming Language :: Python :: 3.10
21
- Classifier: Programming Language :: Python :: 3.6
22
- Classifier: Programming Language :: Python :: 3.7
23
- Classifier: Programming Language :: Python :: 3.8
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Programming Language :: Python :: 3.13
24
26
  Classifier: Programming Language :: Python :: 3.9
25
27
  Classifier: Topic :: Software Development :: Quality Assurance
26
28
  Provides-Extra: with_bandit
27
29
  Provides-Extra: with_everything
28
- Provides-Extra: with_frosted
29
30
  Provides-Extra: with_mypy
31
+ Provides-Extra: with_pyright
30
32
  Provides-Extra: with_pyroma
33
+ Provides-Extra: with_ruff
31
34
  Provides-Extra: with_vulture
35
+ Requires-Dist: GitPython (>=3.1.27,<4.0.0)
32
36
  Requires-Dist: PyYAML
33
37
  Requires-Dist: bandit (>=1.5.1); extra == "with_bandit" or extra == "with_everything"
34
38
  Requires-Dist: dodgy (>=0.2.1,<0.3.0)
35
- Requires-Dist: frosted (>=1.4.1); extra == "with_frosted" or extra == "with_everything"
36
- Requires-Dist: mccabe (>=0.6.0,<0.7.0)
39
+ Requires-Dist: mccabe (>=0.7.0,<0.8.0)
37
40
  Requires-Dist: mypy (>=0.600); extra == "with_mypy" or extra == "with_everything"
41
+ Requires-Dist: packaging
38
42
  Requires-Dist: pep8-naming (>=0.3.3,<=0.10.0)
39
- Requires-Dist: pycodestyle (>=2.6.0,<2.9.0)
43
+ Requires-Dist: pycodestyle (>=2.9.0)
40
44
  Requires-Dist: pydocstyle (>=2.0.0)
41
- Requires-Dist: pyflakes (>=2.2.0,<3)
42
- Requires-Dist: pylint (>=2.8.3)
45
+ Requires-Dist: pyflakes (>=2.2.0)
46
+ Requires-Dist: pylint (>=3.0)
43
47
  Requires-Dist: pylint-celery (==0.3)
44
- Requires-Dist: pylint-django (>=2.5,<2.6)
48
+ Requires-Dist: pylint-django (>=2.6.1)
45
49
  Requires-Dist: pylint-flask (==0.6)
46
- Requires-Dist: pylint-plugin-utils (>=0.7,<0.8)
50
+ Requires-Dist: pyright (>=1.1.3); extra == "with_pyright" or extra == "with_everything"
47
51
  Requires-Dist: pyroma (>=2.4); extra == "with_pyroma" or extra == "with_everything"
48
- Requires-Dist: requirements-detector (>=0.7,<0.8)
52
+ Requires-Dist: requirements-detector (>=1.3.2)
53
+ Requires-Dist: ruff; extra == "with_ruff" or extra == "with_everything"
49
54
  Requires-Dist: setoptconf-tmp (>=0.3.1,<0.4.0)
50
55
  Requires-Dist: toml (>=0.10.2,<0.11.0)
51
56
  Requires-Dist: vulture (>=1.5); extra == "with_vulture" or extra == "with_everything"
@@ -58,8 +63,8 @@ prospector
58
63
  .. image:: https://img.shields.io/pypi/v/prospector.svg
59
64
  :target: https://pypi.python.org/pypi/prospector
60
65
  :alt: Latest Version of Prospector
61
- .. image:: https://travis-ci.org/PyCQA/prospector.svg?branch=master
62
- :target: https://travis-ci.org/PyCQA/prospector
66
+ .. image:: https://github.com/PyCQA/prospector/actions/workflows/tests.yml/badge.svg
67
+ :target: https://github.com/PyCQA/prospector/actions/workflows/tests.yml
63
68
  :alt: Build Status
64
69
  .. image:: https://img.shields.io/coveralls/PyCQA/prospector.svg?style=flat
65
70
  :target: https://coveralls.io/r/PyCQA/prospector
@@ -202,35 +207,60 @@ text to your repositories' ``.pre-commit-config.yaml``::
202
207
 
203
208
  repos:
204
209
  - repo: https://github.com/PyCQA/prospector
205
- rev: 1.7.5 # The version of Prospector to use, if not 'master' for latest
210
+ rev: 1.10.0 # The version of Prospector to use, if not 'master' for latest
206
211
  hooks:
207
212
  - id: prospector
208
213
 
209
- This only installs base prospector - if you also use optional tools, for example bandit or mypy, then you can add
214
+ This only installs base prospector - if you also use optional tools, for example bandit and/or mypy, then you can add
210
215
  them to the hook configuration like so::
211
216
 
212
217
  repos:
213
218
  - repo: https://github.com/PyCQA/prospector
214
- rev: 1.7.5
219
+ rev: 1.10.0
215
220
  hooks:
216
221
  - id: prospector
217
- additional_requirements:
222
+ additional_dependencies:
218
223
  - ".[with_mypy,with_bandit]"
224
+ - args: [
225
+ '--with-tool=mypy',
226
+ '--with-tool=bandit',
227
+ ]
228
+
229
+ Additional dependencies can be `individually configured <https://prospector.landscape.io/en/master/profiles.html#individual-configuration-options>`_ in your `prospector.yml` file ::
230
+
231
+ # https://bandit.readthedocs.io/en/latest/config.html
232
+ bandit:
233
+ options:
234
+ skips:
235
+ - B201
236
+ - B601
237
+ - B610
238
+ - B611
239
+ - B703
240
+
241
+ # https://mypy.readthedocs.io/en/stable/command_line.html
242
+ mypy:
243
+ options:
244
+ ignore-missing-imports: true
219
245
 
220
246
  For prospector options which affect display only - those which are not configurable using a profile - these can be
221
247
  added as command line arguments to the hook. For example::
222
248
 
223
249
  repos:
224
250
  - repo: https://github.com/PyCQA/prospector
225
- rev: 1.7.5
251
+ rev: 1.10.0
226
252
  hooks:
227
253
  - id: prospector
228
- additional_requirements:
254
+ additional_dependencies:
229
255
  - ".[with_mypy,with_bandit]"
230
256
  args:
257
+ - --with-tool=mypy
258
+ - --with-tool=bandit
231
259
  - --summary-only
232
260
  - --zero-exit
233
261
 
262
+
263
+
234
264
  License
235
265
  -------
236
266
 
@@ -4,8 +4,8 @@ prospector
4
4
  .. image:: https://img.shields.io/pypi/v/prospector.svg
5
5
  :target: https://pypi.python.org/pypi/prospector
6
6
  :alt: Latest Version of Prospector
7
- .. image:: https://travis-ci.org/PyCQA/prospector.svg?branch=master
8
- :target: https://travis-ci.org/PyCQA/prospector
7
+ .. image:: https://github.com/PyCQA/prospector/actions/workflows/tests.yml/badge.svg
8
+ :target: https://github.com/PyCQA/prospector/actions/workflows/tests.yml
9
9
  :alt: Build Status
10
10
  .. image:: https://img.shields.io/coveralls/PyCQA/prospector.svg?style=flat
11
11
  :target: https://coveralls.io/r/PyCQA/prospector
@@ -148,35 +148,60 @@ text to your repositories' ``.pre-commit-config.yaml``::
148
148
 
149
149
  repos:
150
150
  - repo: https://github.com/PyCQA/prospector
151
- rev: 1.7.5 # The version of Prospector to use, if not 'master' for latest
151
+ rev: 1.10.0 # The version of Prospector to use, if not 'master' for latest
152
152
  hooks:
153
153
  - id: prospector
154
154
 
155
- This only installs base prospector - if you also use optional tools, for example bandit or mypy, then you can add
155
+ This only installs base prospector - if you also use optional tools, for example bandit and/or mypy, then you can add
156
156
  them to the hook configuration like so::
157
157
 
158
158
  repos:
159
159
  - repo: https://github.com/PyCQA/prospector
160
- rev: 1.7.5
160
+ rev: 1.10.0
161
161
  hooks:
162
162
  - id: prospector
163
- additional_requirements:
163
+ additional_dependencies:
164
164
  - ".[with_mypy,with_bandit]"
165
+ - args: [
166
+ '--with-tool=mypy',
167
+ '--with-tool=bandit',
168
+ ]
169
+
170
+ Additional dependencies can be `individually configured <https://prospector.landscape.io/en/master/profiles.html#individual-configuration-options>`_ in your `prospector.yml` file ::
171
+
172
+ # https://bandit.readthedocs.io/en/latest/config.html
173
+ bandit:
174
+ options:
175
+ skips:
176
+ - B201
177
+ - B601
178
+ - B610
179
+ - B611
180
+ - B703
181
+
182
+ # https://mypy.readthedocs.io/en/stable/command_line.html
183
+ mypy:
184
+ options:
185
+ ignore-missing-imports: true
165
186
 
166
187
  For prospector options which affect display only - those which are not configurable using a profile - these can be
167
188
  added as command line arguments to the hook. For example::
168
189
 
169
190
  repos:
170
191
  - repo: https://github.com/PyCQA/prospector
171
- rev: 1.7.5
192
+ rev: 1.10.0
172
193
  hooks:
173
194
  - id: prospector
174
- additional_requirements:
195
+ additional_dependencies:
175
196
  - ".[with_mypy,with_bandit]"
176
197
  args:
198
+ - --with-tool=mypy
199
+ - --with-tool=bandit
177
200
  - --summary-only
178
201
  - --zero-exit
179
202
 
203
+
204
+
180
205
  License
181
206
  -------
182
207
 
@@ -0,0 +1,4 @@
1
+ from prospector.run import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -1,6 +1,8 @@
1
1
  import os
2
2
  import re
3
3
  import warnings
4
+ from pathlib import Path
5
+ from typing import Union
4
6
 
5
7
  from requirements_detector import find_requirements
6
8
  from requirements_detector.detect import RequirementsNotFound
@@ -18,7 +20,7 @@ _IMPORT_REGEX = re.compile(r"^\s*import ([\._a-zA-Z0-9]+)$")
18
20
  _IMPORT_MULTIPLE_REGEX = re.compile(r"^\s*import ([\._a-zA-Z0-9]+(, ){1})+")
19
21
 
20
22
 
21
- def find_from_imports(file_contents):
23
+ def find_from_imports(file_contents: str) -> set[str]:
22
24
  names = set()
23
25
  for line in file_contents.split("\n"):
24
26
  match = _IMPORT_MULTIPLE_REGEX.match(line)
@@ -41,52 +43,48 @@ def find_from_imports(file_contents):
41
43
  return names
42
44
 
43
45
 
44
- def find_from_path(path):
46
+ def find_from_path(path: Path) -> set[str]:
45
47
  names = set()
46
48
 
47
49
  try:
48
- dirlist = os.listdir(path)
50
+ for item in path.iterdir():
51
+ if item.is_dir():
52
+ if is_virtualenv(item):
53
+ continue
54
+ names |= find_from_path(item)
55
+ elif not item.is_symlink() and item.suffix == ".py":
56
+ try:
57
+ contents = encoding.read_py_file(item)
58
+ names |= find_from_imports(contents)
59
+ except encoding.CouldNotHandleEncoding as err:
60
+ # TODO: this output will break output formats such as JSON
61
+ warnings.warn(f"{err.path}: {err.__cause__}", ImportWarning, stacklevel=0)
62
+
63
+ if len(names) == len(POSSIBLE_LIBRARIES):
64
+ # don't continue on recursing, there's no point!
65
+ break
49
66
  except PermissionError as err:
50
67
  raise PermissionMissing(path) from err
51
68
 
52
- for item in dirlist:
53
- item_path = os.path.abspath(os.path.join(path, item))
54
- if os.path.isdir(item_path):
55
- if is_virtualenv(item_path):
56
- continue
57
- names |= find_from_path(item_path)
58
- elif not os.path.islink(item_path) and item_path.endswith(".py"):
59
- try:
60
- contents = encoding.read_py_file(item_path)
61
- names |= find_from_imports(contents)
62
- except encoding.CouldNotHandleEncoding as err:
63
- # TODO: this output will break output formats such as JSON
64
- warnings.warn("{0}: {1}".format(err.path, err.cause), ImportWarning)
65
-
66
- if len(names) == len(POSSIBLE_LIBRARIES):
67
- # don't continue on recursing, there's no point!
68
- break
69
-
70
69
  return names
71
70
 
72
71
 
73
- def find_from_requirements(path):
72
+ def find_from_requirements(path: Union[str, Path]) -> set[str]:
74
73
  reqs = find_requirements(path)
75
- names = []
74
+ names: set[str] = set()
76
75
  for requirement in reqs:
77
76
  if requirement.name is not None and requirement.name.lower() in POSSIBLE_LIBRARIES:
78
- names.append(requirement.name.lower())
77
+ names.add(requirement.name.lower())
79
78
  return names
80
79
 
81
80
 
82
- def autodetect_libraries(path):
83
-
81
+ def autodetect_libraries(path: Union[str, Path]) -> set[str]:
84
82
  if os.path.isfile(path):
85
83
  path = os.path.dirname(path)
86
84
  if path == "":
87
85
  path = "."
88
86
 
89
- libraries = []
87
+ libraries: set[str] = set()
90
88
 
91
89
  try:
92
90
  libraries = find_from_requirements(path)
@@ -94,6 +92,6 @@ def autodetect_libraries(path):
94
92
  pass
95
93
 
96
94
  if len(libraries) < len(POSSIBLE_LIBRARIES):
97
- libraries = find_from_path(path)
95
+ libraries = find_from_path(Path(path))
98
96
 
99
97
  return libraries
@@ -4,18 +4,22 @@
4
4
  # the same line. For example, both pyflakes and pylint will generate an
5
5
  # "Unused Import" warning on the same line. This is obviously redundant, so we
6
6
  # remove duplicates.
7
+ import pkgutil
7
8
  from collections import defaultdict
9
+ from pathlib import Path
10
+ from typing import Optional
8
11
 
9
- import pkg_resources
10
12
  import yaml
11
13
 
14
+ from prospector.message import Message
15
+
12
16
  __all__ = (
13
17
  "blend",
14
18
  "BLEND_COMBOS",
15
19
  )
16
20
 
17
21
 
18
- def blend_line(messages, blend_combos=None):
22
+ def blend_line(messages: list[Message], blend_combos: Optional[list[list[tuple[str, str]]]] = None) -> list[Message]:
19
23
  """
20
24
  Given a list of messages on the same line, blend them together so that we
21
25
  end up with one message per actual problem. Note that we can still return
@@ -23,8 +27,8 @@ def blend_line(messages, blend_combos=None):
23
27
  the line.
24
28
  """
25
29
  blend_combos = blend_combos or BLEND_COMBOS
26
- blend_lists = [[] for _ in range(len(blend_combos))]
27
- blended = []
30
+ blend_lists: list[list[Message]] = [[] for _ in range(len(blend_combos))]
31
+ blended: list[Message] = []
28
32
 
29
33
  # first we split messages into each of the possible blendable categories
30
34
  # so that we have a list of lists of messages which can be blended together
@@ -52,6 +56,7 @@ def blend_line(messages, blend_combos=None):
52
56
  for blend_combo_idx, blend_list in enumerate(blend_lists):
53
57
  if len(blend_list) == 0:
54
58
  continue
59
+ # pylint:disable=cell-var-from-loop
55
60
  blend_list.sort(
56
61
  key=lambda msg: blend_combos[blend_combo_idx].index(
57
62
  (msg.source, msg.code),
@@ -71,16 +76,16 @@ def blend_line(messages, blend_combos=None):
71
76
  # it will appear in two blend_lists. Therefore we mark anything not taken from the blend list
72
77
  # as "consumed" and then filter later, to avoid such cases.
73
78
  for now_used in blend_list[1:]:
74
- now_used.used = True
79
+ now_used.used = True # type: ignore[attr-defined]
75
80
 
76
81
  return [m for m in blended if not getattr(m, "used", False)]
77
82
 
78
83
 
79
- def blend(messages, blend_combos=None):
84
+ def blend(messages: list[Message], blend_combos: Optional[list[list[tuple[str, str]]]] = None) -> list[Message]:
80
85
  blend_combos = blend_combos or BLEND_COMBOS
81
86
 
82
87
  # group messages by file and then line number
83
- msgs_grouped = defaultdict(lambda: defaultdict(list))
88
+ msgs_grouped: dict[Optional[Path], dict[Optional[int], list[Message]]] = defaultdict(lambda: defaultdict(list))
84
89
 
85
90
  for message in messages:
86
91
  msgs_grouped[message.location.path][message.location.line].append(
@@ -96,18 +101,20 @@ def blend(messages, blend_combos=None):
96
101
  return out
97
102
 
98
103
 
99
- def get_default_blend_combinations():
100
- combos = yaml.safe_load(pkg_resources.resource_string(__name__, "blender_combinations.yaml"))
104
+ def get_default_blend_combinations() -> list[list[tuple[str, str]]]:
105
+ blender_combinations = pkgutil.get_data(__name__, "blender_combinations.yaml")
106
+ assert blender_combinations is not None
107
+ combos = yaml.safe_load(blender_combinations)
101
108
  combos = combos.get("combinations", [])
102
109
 
103
- defaults = []
110
+ defaults: list[list[tuple[str, str]]] = []
104
111
  for combo in combos:
105
112
  toblend = []
106
113
  for msg in combo:
107
114
  toblend += msg.items()
108
- defaults.append(tuple(toblend))
115
+ defaults.append(toblend)
109
116
 
110
- return tuple(defaults)
117
+ return defaults
111
118
 
112
119
 
113
120
  BLEND_COMBOS = get_default_blend_combinations()