prospector 1.15.3__tar.gz → 1.16.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.
- {prospector-1.15.3 → prospector-1.16.0}/PKG-INFO +1 -2
- {prospector-1.15.3 → prospector-1.16.0}/prospector/autodetect.py +1 -1
- {prospector-1.15.3 → prospector-1.16.0}/prospector/blender_combinations.yaml +88 -84
- {prospector-1.15.3 → prospector-1.16.0}/prospector/config/configuration.py +1 -1
- prospector-1.16.0/prospector/formatters/base_summary.py +90 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/pylint.py +17 -12
- {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/text.py +3 -0
- prospector-1.16.0/prospector/identify.py +90 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/pathutils.py +16 -2
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profile.py +5 -2
- {prospector-1.15.3 → prospector-1.16.0}/prospector/suppression.py +2 -2
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/base.py +2 -2
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/mypy/__init__.py +2 -2
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/profile_validator/__init__.py +1 -1
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pylint/__init__.py +6 -4
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/ruff/__init__.py +2 -2
- {prospector-1.15.3 → prospector-1.16.0}/pyproject.toml +1 -2
- {prospector-1.15.3 → prospector-1.16.0}/setup.py +1 -2
- prospector-1.15.3/prospector/formatters/base_summary.py +0 -43
- {prospector-1.15.3 → prospector-1.16.0}/LICENSE +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/README.rst +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/__main__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/blender.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/compat.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/config/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/config/datatype.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/encoding.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/exceptions.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/finder.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/base.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/emacs.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/grouped.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/json.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/pylint_parseable.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/vscode.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/xunit.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/yaml.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/message.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/postfilter.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/exceptions.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/default.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/doc_warnings.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/flake8.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/full_pep8.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/member_warnings.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/no_doc_warnings.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/no_member_warnings.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/no_pep8.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/no_test_warnings.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_high.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_low.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_medium.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_none.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_veryhigh.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_verylow.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/test_warnings.yaml +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/run.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/bandit/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/dodgy/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/exceptions.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/mccabe/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pycodestyle/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pydocstyle/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pyflakes/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pylint/collector.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pylint/linter.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pyright/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pyroma/__init__.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/utils.py +0 -0
- {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/vulture/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: prospector
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.16.0
|
|
4
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
6
|
License: GPLv2+
|
|
@@ -46,7 +46,6 @@ Requires-Dist: pyflakes (>=2.2.0)
|
|
|
46
46
|
Requires-Dist: pylint (>=3.0)
|
|
47
47
|
Requires-Dist: pylint-celery (==0.3)
|
|
48
48
|
Requires-Dist: pylint-django (>=2.6.1)
|
|
49
|
-
Requires-Dist: pylint-flask (==0.6)
|
|
50
49
|
Requires-Dist: pyright (>=1.1.3); extra == "with_pyright" or extra == "with_everything"
|
|
51
50
|
Requires-Dist: pyroma (>=2.4); extra == "with_pyroma" or extra == "with_everything"
|
|
52
51
|
Requires-Dist: requirements-detector (>=1.3.2)
|
|
@@ -11,7 +11,7 @@ from prospector import encoding
|
|
|
11
11
|
from prospector.exceptions import PermissionMissing
|
|
12
12
|
from prospector.pathutils import is_virtualenv
|
|
13
13
|
|
|
14
|
-
POSSIBLE_LIBRARIES = ("django", "celery"
|
|
14
|
+
POSSIBLE_LIBRARIES = ("django", "celery")
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
# see http://docs.python.org/2/reference/lexical_analysis.html#identifiers
|
|
@@ -4,19 +4,22 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Note that since not all tools will necessarily be run, the first message for
|
|
6
6
|
# a line as sorted by the code list will be the one remaining after blending.
|
|
7
|
+
#
|
|
8
|
+
# We put at first the PyLint, mypy and Ruff codes because we can
|
|
9
|
+
# ignore a specific code on a specific line.
|
|
7
10
|
|
|
8
11
|
combinations:
|
|
9
12
|
- # Unused Import
|
|
10
13
|
- pylint: unused-import
|
|
11
14
|
- pylint: W0611
|
|
15
|
+
- ruff: F401
|
|
12
16
|
- pyflakes: F401
|
|
13
17
|
- frosted: E101
|
|
14
|
-
- ruff: F401
|
|
15
18
|
|
|
16
19
|
- # Syntax Error
|
|
17
|
-
- dodgy: diff
|
|
18
20
|
- pylint: syntax-error
|
|
19
21
|
- pylint: E0001
|
|
22
|
+
- dodgy: diff
|
|
20
23
|
- pyflakes: F999
|
|
21
24
|
- pep8: E901
|
|
22
25
|
# expected an indented block
|
|
@@ -29,222 +32,222 @@ combinations:
|
|
|
29
32
|
- # Undefined local variable
|
|
30
33
|
- pylint: undefined-variable
|
|
31
34
|
- pylint: E0602
|
|
35
|
+
- mypy: name-defined
|
|
36
|
+
- ruff: F821
|
|
32
37
|
- pyflakes: F821
|
|
33
38
|
- frosted: E303
|
|
34
|
-
- ruff: F821
|
|
35
|
-
- mypy: name-defined
|
|
36
39
|
|
|
37
40
|
- # Unused variable
|
|
38
41
|
- pylint: unused-variable
|
|
39
42
|
- pylint: W0612
|
|
43
|
+
- ruff: F841
|
|
40
44
|
- vulture: unused-variable
|
|
41
45
|
- pyflakes: F841
|
|
42
46
|
- frosted: E307
|
|
43
|
-
- ruff: F841
|
|
44
47
|
|
|
45
48
|
- # Mixed tabs and spaces
|
|
49
|
+
- pylint: indentation-mixture
|
|
50
|
+
- ruff: D206
|
|
46
51
|
- pep257: D206
|
|
47
52
|
- pydocstyle: D206
|
|
48
53
|
- pep8: E101
|
|
49
54
|
- pycodestyle: E101
|
|
50
|
-
- pylint: indentation-mixture
|
|
51
|
-
- ruff: D206
|
|
52
55
|
|
|
53
56
|
- # Import from __future__ not first import
|
|
54
57
|
- pylint: misplaced-future
|
|
55
58
|
- pylint: W0410
|
|
59
|
+
- ruff: F404
|
|
56
60
|
- pyflakes: F404
|
|
57
61
|
- frosted: E207
|
|
58
|
-
- ruff: F404
|
|
59
62
|
|
|
60
63
|
- # Line too long
|
|
61
|
-
- pep8: E501
|
|
62
|
-
- pycodestyle: E501
|
|
63
64
|
- pylint: line-too-long
|
|
64
65
|
- pylint: C0301
|
|
66
|
+
- pep8: E501
|
|
67
|
+
- pycodestyle: E501
|
|
65
68
|
|
|
66
69
|
- # Trailing whitespace
|
|
67
|
-
- pep8: W291
|
|
68
|
-
- pycodestyle: W291
|
|
69
70
|
- pylint: trailing-whitespace
|
|
70
71
|
- pylint: C0303
|
|
72
|
+
- pep8: W291
|
|
73
|
+
- pycodestyle: W291
|
|
71
74
|
|
|
72
75
|
- # Blank line contains whitespace
|
|
73
|
-
- pep8: W293
|
|
74
|
-
- pycodestyle: W293
|
|
75
76
|
- pylint: trailing-whitespace
|
|
76
77
|
- pylint: C0303
|
|
78
|
+
- pep8: W293
|
|
79
|
+
- pycodestyle: W293
|
|
77
80
|
|
|
78
81
|
- # No newline at end of file
|
|
79
|
-
- pep8: W292
|
|
80
|
-
- pycodestyle: W292
|
|
81
82
|
- pylint: missing-final-newline
|
|
82
83
|
- pylint: C0304
|
|
84
|
+
- pep8: W292
|
|
85
|
+
- pycodestyle: W292
|
|
83
86
|
|
|
84
87
|
- # line ends with semi-colon
|
|
85
|
-
- pep8: E703
|
|
86
|
-
- pycodestyle: E703
|
|
87
88
|
- pylint: unnecessary-semicolon
|
|
88
89
|
- pylint: W0301
|
|
90
|
+
- pep8: E703
|
|
91
|
+
- pycodestyle: E703
|
|
89
92
|
|
|
90
93
|
- # multiple statements on one line (colon)
|
|
91
|
-
- pep8: E701
|
|
92
|
-
- pycodestyle: E701
|
|
93
94
|
- pylint: multiple-statements
|
|
94
95
|
- pylint: C0321
|
|
96
|
+
- pep8: E701
|
|
97
|
+
- pycodestyle: E701
|
|
95
98
|
|
|
96
99
|
- # multiple statements on one line (semicolon)
|
|
97
|
-
- pep8: E702
|
|
98
|
-
- pycodestyle: E702
|
|
99
100
|
- pylint: multiple-statements
|
|
100
101
|
- pylint: C0321
|
|
102
|
+
- pep8: E702
|
|
103
|
+
- pycodestyle: E702
|
|
101
104
|
|
|
102
105
|
- # incorrect indentation
|
|
106
|
+
- pylint: bad-indentation
|
|
107
|
+
- pylint: W0311
|
|
108
|
+
- ruff: D207
|
|
103
109
|
- pep257: D207
|
|
104
110
|
- pydocstyle: D207
|
|
105
111
|
- pep8: E111
|
|
106
112
|
- pycodestyle: E111
|
|
107
|
-
- pylint: bad-indentation
|
|
108
|
-
- pylint: W0311
|
|
109
|
-
- ruff: D207
|
|
110
113
|
|
|
111
114
|
- # incorrect indentation
|
|
115
|
+
- pylint: bad-indentation
|
|
116
|
+
- pylint: W0311
|
|
117
|
+
- ruff: D208
|
|
112
118
|
- pep257: D208
|
|
113
119
|
- pydocstyle: D208
|
|
114
120
|
- pep8: E111
|
|
115
121
|
- pycodestyle: E111
|
|
116
|
-
- pylint: bad-indentation
|
|
117
|
-
- pylint: W0311
|
|
118
|
-
- ruff: D208
|
|
119
122
|
|
|
120
123
|
- # comma not followed by a space
|
|
121
|
-
- pep8: E231
|
|
122
|
-
- pycodestyle: E231
|
|
123
124
|
- pylint: C0324
|
|
124
125
|
- pylint: bad-whitespace
|
|
126
|
+
- pep8: E231
|
|
127
|
+
- pycodestyle: E231
|
|
125
128
|
|
|
126
129
|
- # missing whitespace around operator
|
|
127
|
-
- pep8: E225
|
|
128
|
-
- pycodestyle: E225
|
|
129
130
|
- pylint: C0322
|
|
130
131
|
- pylint: bad-whitespace
|
|
131
|
-
|
|
132
|
-
- # missing whitespace around operator
|
|
133
132
|
- pep8: E225
|
|
134
133
|
- pycodestyle: E225
|
|
134
|
+
|
|
135
|
+
- # missing whitespace around operator
|
|
135
136
|
- pylint: C0323
|
|
136
137
|
- pylint: bad-whitespace
|
|
138
|
+
- pep8: E225
|
|
139
|
+
- pycodestyle: E225
|
|
137
140
|
|
|
138
141
|
- # undefined name in __all__
|
|
139
142
|
- pylint: undefined-all-variable
|
|
140
143
|
- pylint: E0603
|
|
144
|
+
- ruff: F822
|
|
141
145
|
- pyflakes: F822
|
|
142
146
|
- frosted: E304
|
|
143
|
-
- ruff: F822
|
|
144
147
|
|
|
145
148
|
- # duplicate argument in function definition
|
|
146
149
|
- pylint: duplicate-argument-name
|
|
147
150
|
- pylint: E0108
|
|
151
|
+
- ruff: F831
|
|
148
152
|
- pyflakes: F831
|
|
149
153
|
- frosted: E206
|
|
150
|
-
- ruff: F831
|
|
151
154
|
|
|
152
155
|
- # redefinition of unused function
|
|
153
|
-
- pyflakes: F811
|
|
154
156
|
- pylint: function-redefined
|
|
155
157
|
- pylint: E0102
|
|
156
158
|
- ruff: F811
|
|
159
|
+
- pyflakes: F811
|
|
157
160
|
|
|
158
161
|
- # f-string is missing placeholders
|
|
159
162
|
- pylint: f-string-without-interpolation
|
|
160
163
|
- pylint: W1309
|
|
161
|
-
- pyflakes: F541
|
|
162
164
|
- ruff: F541
|
|
165
|
+
- pyflakes: F541
|
|
163
166
|
|
|
164
167
|
- # Duplicate key in dictionary
|
|
165
168
|
- pylint: duplicate-key
|
|
166
169
|
- pylint: W0109
|
|
167
|
-
- pyflakes: F601
|
|
168
170
|
- ruff: F601
|
|
171
|
+
- pyflakes: F601
|
|
169
172
|
|
|
170
173
|
- # More than one starred expression in assignment
|
|
171
174
|
- pylint: too-many-star-expressions
|
|
172
175
|
- pylint: E0112
|
|
173
|
-
- pyflakes: F622
|
|
174
176
|
- ruff: F622
|
|
177
|
+
- pyflakes: F622
|
|
175
178
|
|
|
176
179
|
- # Assert called on a tuple
|
|
177
180
|
- pylint: assert-on-tuple
|
|
178
181
|
- pylint: W0199
|
|
179
|
-
- pyflakes: F631
|
|
180
182
|
- ruff: F631
|
|
183
|
+
- pyflakes: F631
|
|
181
184
|
|
|
182
185
|
- # 'break' outside loop
|
|
183
186
|
- pylint: not-in-loop
|
|
184
187
|
- pylint: E0103
|
|
185
|
-
- pyflakes: F701
|
|
186
188
|
- ruff: F701
|
|
189
|
+
- pyflakes: F701
|
|
187
190
|
|
|
188
191
|
- # 'continue' not properly in loop
|
|
189
192
|
- pylint: not-in-loop
|
|
190
193
|
- pylint: E0103
|
|
191
|
-
- pyflakes: F702
|
|
192
194
|
- ruff: F702
|
|
195
|
+
- pyflakes: F702
|
|
193
196
|
|
|
194
197
|
- # 'continue' not supported inside 'finally' clause
|
|
195
198
|
- pylint: continue-in-finally
|
|
196
199
|
- pylint: E0116
|
|
197
|
-
- pyflakes: F703
|
|
198
200
|
- ruff: F703
|
|
199
201
|
- ruff: PLE0116
|
|
202
|
+
- pyflakes: F703
|
|
200
203
|
|
|
201
204
|
- # Yield outside function
|
|
202
205
|
- pylint: yield-outside-function
|
|
203
206
|
- pylint: E0105
|
|
204
|
-
- pyflakes: F704
|
|
205
207
|
- ruff: F704
|
|
208
|
+
- pyflakes: F704
|
|
206
209
|
|
|
207
210
|
- # Return outside function
|
|
208
211
|
- pylint: return-outside-function
|
|
209
212
|
- pylint: E0104
|
|
210
|
-
- pyflakes: F706
|
|
211
213
|
- ruff: F706
|
|
214
|
+
- pyflakes: F706
|
|
212
215
|
|
|
213
216
|
- # default 'except:' must be last
|
|
214
217
|
- pylint: bad-except-order
|
|
215
218
|
- pylint: E0701
|
|
216
|
-
- pyflakes: F707
|
|
217
219
|
- ruff: F707
|
|
220
|
+
- pyflakes: F707
|
|
218
221
|
|
|
219
222
|
- # NotImplemented raised - should raise NotImplementedError
|
|
220
223
|
- pylint: notimplemented-raised
|
|
221
224
|
- pylint: E0711
|
|
222
|
-
- pyflakes: F901
|
|
223
225
|
- ruff: F901
|
|
226
|
+
- pyflakes: F901
|
|
224
227
|
|
|
225
228
|
- # first argument of a classmethod should be named 'cls'
|
|
226
|
-
- pep8: N804
|
|
227
|
-
- pycodestyle: N804
|
|
228
229
|
- pylint: bad-classmethod-argument
|
|
229
230
|
- pylint: C0202
|
|
230
231
|
- ruff: N804
|
|
232
|
+
- pep8: N804
|
|
233
|
+
- pycodestyle: N804
|
|
231
234
|
|
|
232
235
|
- # '<>' is deprecated, use '!='
|
|
236
|
+
- pylint: W0331
|
|
233
237
|
- pep8: W603
|
|
234
238
|
- pycodestyle: W603
|
|
235
|
-
- pylint: W0331
|
|
236
239
|
|
|
237
240
|
- # backticks are deprecated, use 'repr()'
|
|
241
|
+
- pylint: W0333
|
|
238
242
|
- pep8: W604
|
|
239
243
|
- pycodestyle: W604
|
|
240
|
-
- pylint: W0333
|
|
241
244
|
|
|
242
245
|
- # Redefining name from outer scope
|
|
243
246
|
- pylint: redefined-outer-name
|
|
244
247
|
- pylint: W0621
|
|
248
|
+
- ruff: F810
|
|
245
249
|
- pyflakes: F810
|
|
246
250
|
- frosted: E306
|
|
247
|
-
- ruff: F810
|
|
248
251
|
|
|
249
252
|
- # Redefinition of unused variable
|
|
250
253
|
- pylint: redefined-outer-name
|
|
@@ -254,9 +257,9 @@ combinations:
|
|
|
254
257
|
- # Wildcard import
|
|
255
258
|
- pylint: wildcard-import
|
|
256
259
|
- pylint: W0614
|
|
260
|
+
- ruff: F403
|
|
257
261
|
- pyflakes: F403
|
|
258
262
|
- frosted: E103
|
|
259
|
-
- ruff: F403
|
|
260
263
|
|
|
261
264
|
- # Return with argument inside generator
|
|
262
265
|
- pylint: return-arg-in-generator
|
|
@@ -281,23 +284,23 @@ combinations:
|
|
|
281
284
|
- # No exception type(s) specified
|
|
282
285
|
- pylint: bare-except
|
|
283
286
|
- pylint: W0702
|
|
284
|
-
- frosted: W101
|
|
285
|
-
- pep8: E722
|
|
286
|
-
- pycodestyle: E722
|
|
287
287
|
- ruff: E722
|
|
288
288
|
# try_except_pass
|
|
289
289
|
- ruff: S110
|
|
290
|
+
- frosted: W101
|
|
291
|
+
- pep8: E722
|
|
292
|
+
- pycodestyle: E722
|
|
290
293
|
- bandit: B110
|
|
291
294
|
|
|
292
295
|
- # No exception type(s) specified
|
|
293
296
|
- pylint: bare-except
|
|
294
297
|
- pylint: W0702
|
|
295
|
-
- frosted: W101
|
|
296
|
-
- pep8: E722
|
|
297
|
-
- pycodestyle: E722
|
|
298
298
|
- ruff: E722
|
|
299
299
|
# try_except_continue
|
|
300
300
|
- ruff: S112
|
|
301
|
+
- frosted: W101
|
|
302
|
+
- pep8: E722
|
|
303
|
+
- pycodestyle: E722
|
|
301
304
|
- bandit: B112
|
|
302
305
|
|
|
303
306
|
- # Do not catch blind exception: `Exception`
|
|
@@ -316,39 +319,39 @@ combinations:
|
|
|
316
319
|
- ruff: TRY002
|
|
317
320
|
|
|
318
321
|
- # Spaces around keyword/parameter equals
|
|
322
|
+
- pylint: bad-whitespace
|
|
319
323
|
- pep8: E251
|
|
320
324
|
- pycodestyle: E251
|
|
321
|
-
- pylint: bad-whitespace
|
|
322
325
|
|
|
323
326
|
- # Missing space after a comma
|
|
327
|
+
- pylint: bad-whitespace
|
|
324
328
|
- pep8: E231
|
|
325
329
|
- pycodestyle: E231
|
|
326
|
-
- pylint: bad-whitespace
|
|
327
330
|
|
|
328
331
|
- # redefinition of unused %r from line %r
|
|
332
|
+
- ruff: F811
|
|
329
333
|
- pyflakes: F811
|
|
330
334
|
- frosted: E301
|
|
331
|
-
- ruff: F811
|
|
332
335
|
|
|
333
336
|
- # list comprehension redefines %r from line %r
|
|
337
|
+
- ruff: F812
|
|
334
338
|
- pyflakes: F812
|
|
335
339
|
- frosted: E302
|
|
336
|
-
- ruff: F812
|
|
337
340
|
|
|
338
341
|
- # import %r from line %r shadowed by loop variable
|
|
342
|
+
- ruff: F402
|
|
339
343
|
- pyflakes: F402
|
|
340
344
|
- frosted: E102
|
|
341
|
-
- ruff: F402
|
|
342
345
|
|
|
343
346
|
- # syntax error in doctest
|
|
347
|
+
- ruff: FL0007
|
|
344
348
|
- pyflakes: FL0007
|
|
345
349
|
- frosted: E401
|
|
346
|
-
- ruff: FL0007
|
|
347
350
|
|
|
348
351
|
- # local variable %r referenced before assignment
|
|
352
|
+
- ruff: F823
|
|
349
353
|
- pyflakes: F823
|
|
350
354
|
- frosted: E305
|
|
351
|
-
- ruff: F823
|
|
352
355
|
|
|
353
356
|
- # pep8-naming incorrectly suggests that the first argument of a metaclass __new__ method should be 'cls'
|
|
354
357
|
- pylint: bad-mcs-classmethod-argument
|
|
@@ -357,71 +360,72 @@ combinations:
|
|
|
357
360
|
- pycodestyle: N804
|
|
358
361
|
|
|
359
362
|
- # class names should be camelcase
|
|
360
|
-
- pep8: N801
|
|
361
|
-
- pycodestyle: N801
|
|
362
363
|
- pylint: invalid-name
|
|
363
364
|
- pylint: C0103
|
|
365
|
+
- pep8: N801
|
|
366
|
+
- pycodestyle: N801
|
|
364
367
|
|
|
365
368
|
- # function name should be lowercase
|
|
366
369
|
- pylint: invalid-name
|
|
367
370
|
- pylint: C0103
|
|
371
|
+
- ruff: N802
|
|
368
372
|
- pep8: N802
|
|
369
373
|
- pycodestyle: N802
|
|
370
|
-
- ruff: N802
|
|
371
374
|
|
|
372
375
|
- # argument name should be lowercase
|
|
373
376
|
- pylint: invalid-name
|
|
374
377
|
- pylint: C0103
|
|
378
|
+
- ruff: N803
|
|
375
379
|
- pep8: N803
|
|
376
380
|
- pycodestyle: N803
|
|
377
|
-
- ruff: N803
|
|
378
381
|
|
|
379
382
|
- # Variable in function should be lowercase
|
|
380
383
|
- pylint: invalid-name
|
|
381
384
|
- pylint: C0103
|
|
385
|
+
- ruff: N806
|
|
382
386
|
- pep8: N806
|
|
383
387
|
- pycodestyle: N806
|
|
384
|
-
- ruff: N806
|
|
385
388
|
|
|
386
389
|
# too complex
|
|
387
390
|
- # Too many branches ({branches} > {max_branches})
|
|
388
391
|
- pylint: too-many-branches
|
|
389
392
|
- pylint: R0912
|
|
390
|
-
- mccabe: MC0001
|
|
391
393
|
- ruff: PLR0912
|
|
394
|
+
- mccabe: MC0001
|
|
395
|
+
|
|
392
396
|
- # Too many statements ({statements} > {max_statements})
|
|
393
397
|
- pylint: too-many-statements
|
|
394
398
|
- pylint: R0915
|
|
395
|
-
- mccabe: MC0001
|
|
396
399
|
- ruff: PLR0915
|
|
400
|
+
- mccabe: MC0001
|
|
397
401
|
|
|
398
402
|
- # pep257 takes preference over pylint documentation warnings
|
|
399
|
-
- pep257: D100
|
|
400
|
-
- pydocstyle: D100
|
|
401
403
|
- pylint: missing-docstring
|
|
402
404
|
- pylint: C0111
|
|
403
405
|
- ruff: D100
|
|
406
|
+
- pep257: D100
|
|
407
|
+
- pydocstyle: D100
|
|
404
408
|
|
|
405
409
|
- # Missing docstring in public class
|
|
406
|
-
- pep257: D101
|
|
407
|
-
- pydocstyle: D101
|
|
408
410
|
- pylint: missing-docstring
|
|
409
411
|
- pylint: C0111
|
|
410
412
|
- ruff: D101
|
|
413
|
+
- pep257: D101
|
|
414
|
+
- pydocstyle: D101
|
|
411
415
|
|
|
412
416
|
- # Missing docstring in public method
|
|
413
|
-
- pep257: D102
|
|
414
|
-
- pydocstyle: D102
|
|
415
417
|
- pylint: missing-docstring
|
|
416
418
|
- pylint: C0111
|
|
417
419
|
- ruff: D102
|
|
420
|
+
- pep257: D102
|
|
421
|
+
- pydocstyle: D102
|
|
418
422
|
|
|
419
423
|
- # Missing docstring in public function
|
|
420
|
-
- pep257: D103
|
|
421
|
-
- pydocstyle: D103
|
|
422
424
|
- pylint: missing-docstring
|
|
423
425
|
- pylint: C0111
|
|
424
426
|
- ruff: D103
|
|
427
|
+
- pep257: D103
|
|
428
|
+
- pydocstyle: D103
|
|
425
429
|
|
|
426
430
|
- - pylint: singleton-comparison
|
|
427
431
|
- pylint: C0221
|
|
@@ -430,9 +434,9 @@ combinations:
|
|
|
430
434
|
|
|
431
435
|
- - pylint: subprocess-run-check
|
|
432
436
|
- pylint: W1510
|
|
433
|
-
- bandit: B603
|
|
434
437
|
- ruff: S603
|
|
435
438
|
- ruff: PLW1510
|
|
439
|
+
- bandit: B603
|
|
436
440
|
|
|
437
441
|
- # Private member accessed:
|
|
438
442
|
- pylint: protected-access
|
|
@@ -591,8 +595,8 @@ combinations:
|
|
|
591
595
|
# See: https://docs.astral.sh/ruff/rules/suspicious-eval-usage
|
|
592
596
|
- - pylint: eval-used
|
|
593
597
|
- pylint: W0123
|
|
594
|
-
- bandit: B307
|
|
595
598
|
- ruff: S307
|
|
599
|
+
- bandit: B307
|
|
596
600
|
|
|
597
601
|
- # {kind} name "{param_name}" does not reflect its {variance}; consider renaming it to "{replacement_name}"
|
|
598
602
|
- pylint: type-name-incorrect-variance
|
|
@@ -127,7 +127,7 @@ def build_command_line_source(
|
|
|
127
127
|
"uses": {
|
|
128
128
|
"flags": ["-u", "--uses"],
|
|
129
129
|
"help": "A list of one or more libraries or frameworks that the"
|
|
130
|
-
" project uses. Possible values are: django, celery
|
|
130
|
+
" project uses. Possible values are: django, celery. This will be"
|
|
131
131
|
" autodetected by default, but if autodetection doesn't"
|
|
132
132
|
" work, manually specify them using this flag.",
|
|
133
133
|
},
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from prospector.formatters.base import Formatter
|
|
6
|
+
from prospector.message import Location, Message
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SummaryFormatter(Formatter):
|
|
10
|
+
"""
|
|
11
|
+
This abstract formatter is used to output a summary of the prospector run.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
summary_labels = (
|
|
15
|
+
("started", "Started", None),
|
|
16
|
+
("completed", "Finished", None),
|
|
17
|
+
("time_taken", "Time Taken", lambda x: f"{x} seconds"),
|
|
18
|
+
("formatter", "Formatter", None),
|
|
19
|
+
("profiles", "Profiles", None),
|
|
20
|
+
("strictness", "Strictness", None),
|
|
21
|
+
("libraries", "Libraries Used", ", ".join),
|
|
22
|
+
("tools", "Tools Run", ", ".join),
|
|
23
|
+
("adaptors", "Adaptors", ", ".join),
|
|
24
|
+
("message_count", "Messages Found", None),
|
|
25
|
+
("external_config", "External Config", None),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def render_summary(self) -> str:
|
|
29
|
+
output = [
|
|
30
|
+
"Check Information",
|
|
31
|
+
"=================",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
label_width = max(len(label[1]) for label in self.summary_labels)
|
|
35
|
+
|
|
36
|
+
for key, label, formatter in self.summary_labels:
|
|
37
|
+
if key in self.summary:
|
|
38
|
+
value = self.summary[key]
|
|
39
|
+
if formatter is not None:
|
|
40
|
+
value = formatter(value)
|
|
41
|
+
output.append(f" {label.rjust(label_width)}: {value}")
|
|
42
|
+
|
|
43
|
+
return "\n".join(output)
|
|
44
|
+
|
|
45
|
+
def render_profile(self) -> str:
|
|
46
|
+
output = ["Profile", "=======", "", self.profile.as_yaml().strip()]
|
|
47
|
+
|
|
48
|
+
return "\n".join(output)
|
|
49
|
+
|
|
50
|
+
def get_ci_annotation(self, message: Message) -> Optional[str]:
|
|
51
|
+
intro = (
|
|
52
|
+
f"({message.source})"
|
|
53
|
+
if message.code is None
|
|
54
|
+
else f"{message.code}({message.source})"
|
|
55
|
+
if message.location.function is None
|
|
56
|
+
else f"[{message.code}({message.source}), {message.location.function}]"
|
|
57
|
+
)
|
|
58
|
+
ci_prefix = self._get_ci_prefix(message.location, intro)
|
|
59
|
+
if ci_prefix:
|
|
60
|
+
github_message = f"{ci_prefix}{intro}%0A{message.message.strip()}"
|
|
61
|
+
if message.doc_url:
|
|
62
|
+
github_message += f"%0ASee: {message.doc_url}"
|
|
63
|
+
return github_message
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
def _get_ci_prefix(self, location: Location, title: str) -> Optional[str]:
|
|
67
|
+
if location.path is None:
|
|
68
|
+
return None
|
|
69
|
+
if os.environ.get("GITHUB_ACTIONS") == "true":
|
|
70
|
+
path = location.path
|
|
71
|
+
if "PROSPECTOR_FILE_PREFIX" in os.environ:
|
|
72
|
+
path = Path(os.environ["PROSPECTOR_FILE_PREFIX"]) / path
|
|
73
|
+
# pylint: disable-next=line-too-long
|
|
74
|
+
# See: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#setting-an-error-message
|
|
75
|
+
parameters = {
|
|
76
|
+
"file": path,
|
|
77
|
+
"line": location.line,
|
|
78
|
+
"title": title,
|
|
79
|
+
}
|
|
80
|
+
if location.line_end:
|
|
81
|
+
parameters["endLine"] = location.line_end
|
|
82
|
+
if location.character:
|
|
83
|
+
parameters["col"] = location.character
|
|
84
|
+
if location.character_end:
|
|
85
|
+
parameters["endColumn"] = location.character_end
|
|
86
|
+
|
|
87
|
+
parameters_str = ",".join(f"{key}={value}" for key, value in parameters.items())
|
|
88
|
+
return f"::error {parameters_str}::"
|
|
89
|
+
|
|
90
|
+
return None
|
|
@@ -52,18 +52,23 @@ class PylintFormatter(SummaryFormatter):
|
|
|
52
52
|
message_str = message.message.strip()
|
|
53
53
|
if message.doc_url:
|
|
54
54
|
message_str += f" (See: {message.doc_url})"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
55
|
+
template_args = {
|
|
56
|
+
"path": self._make_path(message.location),
|
|
57
|
+
"line": message.location.line,
|
|
58
|
+
"character": message.location.character,
|
|
59
|
+
"source": message.source,
|
|
60
|
+
"code": message.code,
|
|
61
|
+
"function": message.location.function,
|
|
62
|
+
"message": message.message.strip(),
|
|
63
|
+
}
|
|
64
|
+
output.append(template % template_args)
|
|
65
|
+
if message.doc_url:
|
|
66
|
+
template_args["message"] = ""
|
|
67
|
+
indent = len(template % template_args)
|
|
68
|
+
output.append(f"{' ' * indent}See: {message.doc_url}")
|
|
69
|
+
ci_annotation = self.get_ci_annotation(message)
|
|
70
|
+
if ci_annotation:
|
|
71
|
+
output.append(ci_annotation)
|
|
67
72
|
return output
|
|
68
73
|
|
|
69
74
|
def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str:
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Largely inspired by https://github.com/pre-commit/identify/blob/main/identify/identify.py#L178
|
|
2
|
+
|
|
3
|
+
import errno
|
|
4
|
+
import os
|
|
5
|
+
import shlex
|
|
6
|
+
import string
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import IO
|
|
9
|
+
|
|
10
|
+
printable = frozenset(string.printable)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _shebang_split(line: str) -> tuple[str, ...]:
|
|
14
|
+
try:
|
|
15
|
+
# shebangs aren't supposed to be quoted, though some tools such as
|
|
16
|
+
# setuptools will write them with quotes so we'll best-guess parse
|
|
17
|
+
# with shlex first
|
|
18
|
+
return tuple(shlex.split(line))
|
|
19
|
+
except ValueError:
|
|
20
|
+
# failing that, we'll do a more "traditional" shebang parsing which
|
|
21
|
+
# just involves splitting by whitespace
|
|
22
|
+
return tuple(line.split())
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _parse_nix_shebang(
|
|
26
|
+
bytes_io: IO[bytes],
|
|
27
|
+
cmd: tuple[str, ...],
|
|
28
|
+
) -> tuple[str, ...]:
|
|
29
|
+
while bytes_io.read(2) == b"#!":
|
|
30
|
+
next_line_b = bytes_io.readline()
|
|
31
|
+
try:
|
|
32
|
+
next_line = next_line_b.decode("UTF-8")
|
|
33
|
+
except UnicodeDecodeError:
|
|
34
|
+
return cmd
|
|
35
|
+
|
|
36
|
+
for c in next_line:
|
|
37
|
+
if c not in printable:
|
|
38
|
+
return cmd
|
|
39
|
+
|
|
40
|
+
line_tokens = _shebang_split(next_line.strip())
|
|
41
|
+
for i, token in enumerate(line_tokens[:-1]):
|
|
42
|
+
if token != "-i": # noqa: S105
|
|
43
|
+
continue
|
|
44
|
+
# The argument to -i flag
|
|
45
|
+
cmd = (line_tokens[i + 1],)
|
|
46
|
+
return cmd
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _parse_shebang(bytes_io: IO[bytes]) -> tuple[str, ...]:
|
|
50
|
+
"""Parse the shebang from a file opened for reading binary."""
|
|
51
|
+
if bytes_io.read(2) != b"#!":
|
|
52
|
+
return ()
|
|
53
|
+
first_line_b = bytes_io.readline()
|
|
54
|
+
try:
|
|
55
|
+
first_line = first_line_b.decode("UTF-8")
|
|
56
|
+
except UnicodeDecodeError:
|
|
57
|
+
return ()
|
|
58
|
+
|
|
59
|
+
# Require only printable ascii
|
|
60
|
+
for c in first_line:
|
|
61
|
+
if c not in printable:
|
|
62
|
+
return ()
|
|
63
|
+
|
|
64
|
+
cmd = _shebang_split(first_line.strip())
|
|
65
|
+
if cmd[:2] == ("/usr/bin/env", "-S"):
|
|
66
|
+
cmd = cmd[2:]
|
|
67
|
+
elif cmd[:1] == ("/usr/bin/env",):
|
|
68
|
+
cmd = cmd[1:]
|
|
69
|
+
|
|
70
|
+
if cmd == ("nix-shell",):
|
|
71
|
+
return _parse_nix_shebang(bytes_io, cmd)
|
|
72
|
+
|
|
73
|
+
return cmd
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def parse_shebang_from_file(path: Path) -> tuple[str, ...]:
|
|
77
|
+
"""Parse the shebang given a file path."""
|
|
78
|
+
if not path.exists():
|
|
79
|
+
return ()
|
|
80
|
+
if not os.access(path, os.X_OK):
|
|
81
|
+
return ()
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
with path.open("rb") as f:
|
|
85
|
+
return _parse_shebang(f)
|
|
86
|
+
except OSError as e:
|
|
87
|
+
if e.errno == errno.EINVAL:
|
|
88
|
+
return ()
|
|
89
|
+
else:
|
|
90
|
+
raise
|
|
@@ -1,14 +1,28 @@
|
|
|
1
|
+
import mimetypes
|
|
1
2
|
import os
|
|
3
|
+
import re
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
|
|
6
|
+
from prospector import identify
|
|
7
|
+
|
|
8
|
+
_PYTHON_COMMAND_RE = re.compile(r"^python[0-9]?$")
|
|
9
|
+
|
|
4
10
|
|
|
5
11
|
def is_python_package(path: Path) -> bool:
|
|
6
12
|
return path.is_dir() and (path / "__init__.py").exists()
|
|
7
13
|
|
|
8
14
|
|
|
9
15
|
def is_python_module(path: Path) -> bool:
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
mimetype, encoding = mimetypes.guess_type(path)
|
|
17
|
+
del encoding
|
|
18
|
+
if mimetype == "text/x-python":
|
|
19
|
+
return True
|
|
20
|
+
|
|
21
|
+
executor = identify.parse_shebang_from_file(path)
|
|
22
|
+
if executor is not None and len(executor) > 0:
|
|
23
|
+
return _PYTHON_COMMAND_RE.match(Path(executor[0]).name) is not None
|
|
24
|
+
|
|
25
|
+
return False
|
|
12
26
|
|
|
13
27
|
|
|
14
28
|
def is_virtualenv(path: Path) -> bool:
|
|
@@ -26,7 +26,7 @@ class ProspectorProfile:
|
|
|
26
26
|
self.output_target = profile_dict.get("output-target")
|
|
27
27
|
self.autodetect = profile_dict.get("autodetect", True)
|
|
28
28
|
self.uses = [
|
|
29
|
-
uses for uses in _ensure_list(profile_dict.get("uses", [])) if uses in ("django", "celery"
|
|
29
|
+
uses for uses in _ensure_list(profile_dict.get("uses", [])) if uses in ("django", "celery")
|
|
30
30
|
]
|
|
31
31
|
self.max_line_length = profile_dict.get("max-line-length")
|
|
32
32
|
|
|
@@ -133,7 +133,10 @@ def _load_content_package(name: str) -> Optional[dict[str, Any]]:
|
|
|
133
133
|
used_name = None
|
|
134
134
|
for file_name in file_names:
|
|
135
135
|
used_name = f"{module_name}:{file_name}"
|
|
136
|
-
|
|
136
|
+
try:
|
|
137
|
+
data = pkgutil.get_data(module_name, file_name)
|
|
138
|
+
except (ModuleNotFoundError, FileNotFoundError):
|
|
139
|
+
continue
|
|
137
140
|
if data is not None:
|
|
138
141
|
break
|
|
139
142
|
|
|
@@ -174,8 +174,8 @@ def get_suppressions(
|
|
|
174
174
|
for line_number, line_content in enumerate(file_contents):
|
|
175
175
|
for tool_name, tool in tools.items():
|
|
176
176
|
tool_ignores = tool.get_ignored_codes(line_content)
|
|
177
|
-
for tool_ignore in tool_ignores:
|
|
178
|
-
tools_ignore[filepath][line_number + 1].add(Ignore(tool_name, tool_ignore))
|
|
177
|
+
for tool_ignore, offset in tool_ignores:
|
|
178
|
+
tools_ignore[filepath][line_number + 1 + offset].add(Ignore(tool_name, tool_ignore))
|
|
179
179
|
|
|
180
180
|
# Ignore the blending messages
|
|
181
181
|
if blending:
|
|
@@ -44,9 +44,9 @@ class ToolBase(ABC):
|
|
|
44
44
|
"""
|
|
45
45
|
raise NotImplementedError
|
|
46
46
|
|
|
47
|
-
def get_ignored_codes(self, line: str) -> list[str]:
|
|
47
|
+
def get_ignored_codes(self, line: str) -> list[tuple[str, int]]:
|
|
48
48
|
"""
|
|
49
|
-
Return a list of error codes that the tool will ignore from a line of code.
|
|
49
|
+
Return a list of error codes and line offset that the tool will ignore from a line of code.
|
|
50
50
|
"""
|
|
51
51
|
del line # unused
|
|
52
52
|
return []
|
|
@@ -154,8 +154,8 @@ class MypyTool(ToolBase):
|
|
|
154
154
|
|
|
155
155
|
return messages
|
|
156
156
|
|
|
157
|
-
def get_ignored_codes(self, line: str) -> list[str]:
|
|
157
|
+
def get_ignored_codes(self, line: str) -> list[tuple[str, int]]:
|
|
158
158
|
match = _IGNORE_RE.search(line)
|
|
159
159
|
if match:
|
|
160
|
-
return [e.strip() for e in match.group(1).split(",")]
|
|
160
|
+
return [(e.strip(), 0) for e in match.group(1).split(",")]
|
|
161
161
|
return []
|
|
@@ -123,7 +123,7 @@ class ProfileValidationTool(ToolBase):
|
|
|
123
123
|
)
|
|
124
124
|
|
|
125
125
|
if "uses" in parsed:
|
|
126
|
-
possible_libs = ("django", "celery"
|
|
126
|
+
possible_libs = ("django", "celery")
|
|
127
127
|
parsed_list = parsed["uses"] if isinstance(parsed["uses"], list) else [parsed["uses"]]
|
|
128
128
|
for uses in parsed_list:
|
|
129
129
|
if uses not in possible_libs:
|
|
@@ -22,6 +22,7 @@ if TYPE_CHECKING:
|
|
|
22
22
|
_UNUSED_WILDCARD_IMPORT_RE = re.compile(r"^Unused import(\(s\))? (.*) from wildcard import")
|
|
23
23
|
|
|
24
24
|
_IGNORE_RE = re.compile(r"#\s*pylint:\s*disable=([^#]*[^#\s])(\s*#.*)?$", re.IGNORECASE)
|
|
25
|
+
_IGNORE_NEXT_RE = re.compile(r"#\s*pylint:\s*disable-next=([^#]*[^#\s])(\s*#.*)?$", re.IGNORECASE)
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
def _is_in_dir(subpath: Path, path: Path) -> bool:
|
|
@@ -46,8 +47,6 @@ class PylintTool(ToolBase):
|
|
|
46
47
|
linter.load_plugin_modules(["pylint_django"])
|
|
47
48
|
if "celery" in prospector_config.libraries:
|
|
48
49
|
linter.load_plugin_modules(["pylint_celery"])
|
|
49
|
-
if "flask" in prospector_config.libraries:
|
|
50
|
-
linter.load_plugin_modules(["pylint_flask"])
|
|
51
50
|
|
|
52
51
|
profile_path = os.path.join(prospector_config.workdir, prospector_config.profile.name)
|
|
53
52
|
for plugin in prospector_config.profile.pylint.get("load-plugins", []): # type: ignore[attr-defined]
|
|
@@ -269,8 +268,11 @@ class PylintTool(ToolBase):
|
|
|
269
268
|
messages = self._collector.get_messages()
|
|
270
269
|
return self.combine(messages)
|
|
271
270
|
|
|
272
|
-
def get_ignored_codes(self, line: str) -> list[str]:
|
|
271
|
+
def get_ignored_codes(self, line: str) -> list[tuple[str, int]]:
|
|
273
272
|
match = _IGNORE_RE.search(line)
|
|
274
273
|
if match:
|
|
275
|
-
return [e.strip() for e in match.group(1).split(",")]
|
|
274
|
+
return [(e.strip(), 0) for e in match.group(1).split(",")]
|
|
275
|
+
match = _IGNORE_NEXT_RE.search(line)
|
|
276
|
+
if match:
|
|
277
|
+
return [(e.strip(), 1) for e in match.group(1).split(",")]
|
|
276
278
|
return []
|
|
@@ -88,8 +88,8 @@ class RuffTool(ToolBase):
|
|
|
88
88
|
)
|
|
89
89
|
return messages
|
|
90
90
|
|
|
91
|
-
def get_ignored_codes(self, line: str) -> list[str]:
|
|
91
|
+
def get_ignored_codes(self, line: str) -> list[tuple[str, int]]:
|
|
92
92
|
match = PEP8_IGNORE_LINE_CODE.search(line)
|
|
93
93
|
if match:
|
|
94
|
-
return [e.strip() for e in match.group(1).split(",")]
|
|
94
|
+
return [(e.strip(), 0) for e in match.group(1).split(",")]
|
|
95
95
|
return []
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "prospector"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.16.0"
|
|
4
4
|
description = "Prospector is a tool to analyse Python code by aggregating the result of other tools."
|
|
5
5
|
authors = ["Carl Crowder <git@carlcrowder.com>"]
|
|
6
6
|
maintainers = ["Carl Crowder <git@carlcrowder.com>",
|
|
@@ -42,7 +42,6 @@ python = ">=3.9,<4.0"
|
|
|
42
42
|
pylint = ">=3.0"
|
|
43
43
|
pylint-celery = "0.3"
|
|
44
44
|
pylint-django = ">=2.6.1"
|
|
45
|
-
pylint-flask = "0.6"
|
|
46
45
|
requirements-detector = ">=1.3.2"
|
|
47
46
|
PyYAML = "*"
|
|
48
47
|
mccabe = "^0.7.0"
|
|
@@ -36,7 +36,6 @@ install_requires = \
|
|
|
36
36
|
'pyflakes>=2.2.0',
|
|
37
37
|
'pylint-celery==0.3',
|
|
38
38
|
'pylint-django>=2.6.1',
|
|
39
|
-
'pylint-flask==0.6',
|
|
40
39
|
'pylint>=3.0',
|
|
41
40
|
'requirements-detector>=1.3.2',
|
|
42
41
|
'setoptconf-tmp>=0.3.1,<0.4.0',
|
|
@@ -61,7 +60,7 @@ entry_points = \
|
|
|
61
60
|
|
|
62
61
|
setup_kwargs = {
|
|
63
62
|
'name': 'prospector',
|
|
64
|
-
'version': '1.
|
|
63
|
+
'version': '1.16.0',
|
|
65
64
|
'description': 'Prospector is a tool to analyse Python code by aggregating the result of other tools.',
|
|
66
65
|
'long_description': 'prospector\n==========\n\n.. image:: https://img.shields.io/pypi/v/prospector.svg\n :target: https://pypi.python.org/pypi/prospector\n :alt: Latest Version of Prospector\n.. image:: https://github.com/PyCQA/prospector/actions/workflows/tests.yml/badge.svg\n :target: https://github.com/PyCQA/prospector/actions/workflows/tests.yml\n :alt: Build Status\n.. image:: https://img.shields.io/coveralls/PyCQA/prospector.svg?style=flat\n :target: https://coveralls.io/r/PyCQA/prospector\n :alt: Test Coverage\n.. image:: https://readthedocs.org/projects/prospector/badge/?version=latest\n :target: https://prospector.readthedocs.io/\n :alt: Documentation\n\n\nAbout\n-----\n\nProspector is a tool to analyse Python code and output information about\nerrors, potential problems, convention violations and complexity.\n\nIt brings together the functionality of other Python analysis tools such as\n`Pylint <https://docs.pylint.org/>`_,\n`pycodestyle <https://pycodestyle.pycqa.org/>`_,\nand `McCabe complexity <https://pypi.python.org/pypi/mccabe>`_.\nSee the `Supported Tools <https://prospector.readthedocs.io/en/latest/supported_tools.html>`_\ndocumentation section for a complete list.\n\nThe primary aim of Prospector is to be useful \'out of the box\'. A common complaint of other\nPython analysis tools is that it takes a long time to filter through which errors are relevant\nor interesting to your own coding style. Prospector provides some default profiles, which\nhopefully will provide a good starting point and will be useful straight away, and adapts\nthe output depending on the libraries your project uses.\n\nInstallation\n------------\n\nProspector can be installed from PyPI using ``pip`` by running the following command::\n\n pip install prospector\n\nOptional dependencies for Prospector, such as ``pyroma`` can also be installed by running::\n\n pip install prospector[with_pyroma]\n\nSome shells (such as ``Zsh``, the default shell of macOS Catalina) require brackets to be escaped::\n\n pip install prospector\\[with_pyroma\\]\n\nFor a list of all of the optional dependencies, see the optional extras section on the ReadTheDocs\npage on `Supported Tools Extras <https://prospector.readthedocs.io/en/latest/supported_tools.html#optional-extras>`_.\n\nFor local development, `poetry <https://python-poetry.org/>`_ is used. Check out the code, then run::\n\n poetry install\n\nAnd for extras::\n\n poetry install -E with_everything\n\nFor more detailed information on installing the tool, see the\n`installation section <https://prospector.readthedocs.io/en/latest/#installation>`_ of the tool\'s main page\non ReadTheDocs.\n\nDocumentation\n-------------\n\nFull `documentation is available at ReadTheDocs <https://prospector.readthedocs.io>`_.\n\nUsage\n-----\n\nSimply run prospector from the root of your project::\n\n prospector\n\nThis will output a list of messages pointing out potential problems or errors, for example::\n\n prospector.tools.base (prospector/tools/base.py):\n L5:0 ToolBase: pylint - R0922\n Abstract class is only referenced 1 times\n\nOptions\n```````\n\nRun ``prospector --help`` for a full list of options and their effects.\n\nOutput Format\n~~~~~~~~~~~~~\n\nThe default output format of ``prospector`` is designed to be human readable. For parsing\n(for example, for reporting), you can use the ``--output-format json`` flag to get JSON-formatted\noutput.\n\nProfiles\n~~~~~~~~\n\nProspector is configurable using "profiles". These are composable YAML files with directives to\ndisable or enable tools or messages. For more information, read\n`the documentation about profiles <https://prospector.readthedocs.io/en/latest/profiles.html>`_.\n\nIf your code uses frameworks and libraries\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nOften tools such as pylint find errors in code which is not an error, for example due to attributes of classes being\ncreated at run time by a library or framework used by your project.\nFor example, by default, pylint will generate an error for Django models when accessing ``objects``, as the\n``objects`` attribute is not part of the ``Model`` class definition.\n\nProspector mitigates this by providing an understanding of these frameworks to the underlying tools.\n\nProspector will try to intuit which libraries your project uses by\n`detecting dependencies <https://github.com/landscapeio/requirements-detector>`_ and automatically turning on\nsupport for the requisite libraries. You can see which adaptors were run in the metadata section of the report.\n\nIf Prospector does not correctly detect your project\'s dependencies, you can specify them manually from the commandline::\n\n prospector --uses django celery\n\nAdditionally, if Prospector is automatically detecting a library that you do not in fact use, you can turn\noff autodetection completely::\n\n prospector --no-autodetect\n\nNote that as far as possible, these adaptors have been written as plugins or augmentations for the underlying\ntools so that they can be used without requiring Prospector. For example, the Django support is available as a pylint plugin.\n\nStrictness\n~~~~~~~~~~\n\nProspector has a configurable \'strictness\' level which will determine how harshly it searches for errors::\n\n prospector --strictness high\n\nPossible values are ``verylow``, ``low``, ``medium``, ``high``, ``veryhigh``.\n\nProspector does not include documentation warnings by default, but you can turn\nthis on using the ``--doc-warnings`` flag.\n\npre-commit\n----------\n\nIf you\'d like Prospector to be run automatically when making changes to files in your Git\nrepository, you can install `pre-commit <https://pre-commit.com/>`_ and add the following\ntext to your repositories\' ``.pre-commit-config.yaml``::\n\n repos:\n - repo: https://github.com/PyCQA/prospector\n rev: 1.10.0 # The version of Prospector to use, if not \'master\' for latest\n hooks:\n - id: prospector\n\nThis only installs base prospector - if you also use optional tools, for example bandit and/or mypy, then you can add\nthem to the hook configuration like so::\n\n repos:\n - repo: https://github.com/PyCQA/prospector\n rev: 1.10.0\n hooks:\n - id: prospector\n additional_dependencies:\n - ".[with_mypy,with_bandit]"\n - args: [\n \'--with-tool=mypy\',\n \'--with-tool=bandit\',\n ]\n\nAdditional dependencies can be `individually configured <https://prospector.landscape.io/en/master/profiles.html#individual-configuration-options>`_ in your `prospector.yml` file ::\n\n # https://bandit.readthedocs.io/en/latest/config.html\n bandit:\n options:\n skips:\n - B201\n - B601\n - B610\n - B611\n - B703\n\n # https://mypy.readthedocs.io/en/stable/command_line.html\n mypy:\n options:\n ignore-missing-imports: true\n\nFor prospector options which affect display only - those which are not configurable using a profile - these can be\nadded as command line arguments to the hook. For example::\n\n repos:\n - repo: https://github.com/PyCQA/prospector\n rev: 1.10.0\n hooks:\n - id: prospector\n additional_dependencies:\n - ".[with_mypy,with_bandit]"\n args:\n - --with-tool=mypy\n - --with-tool=bandit\n - --summary-only\n - --zero-exit\n\n\n\nLicense\n-------\n\nProspector is available under the GPLv2 License.\n',
|
|
67
66
|
'author': 'Carl Crowder',
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
from prospector.formatters.base import Formatter
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class SummaryFormatter(Formatter):
|
|
5
|
-
"""
|
|
6
|
-
This abstract formatter is used to output a summary of the prospector run.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
summary_labels = (
|
|
10
|
-
("started", "Started", None),
|
|
11
|
-
("completed", "Finished", None),
|
|
12
|
-
("time_taken", "Time Taken", lambda x: f"{x} seconds"),
|
|
13
|
-
("formatter", "Formatter", None),
|
|
14
|
-
("profiles", "Profiles", None),
|
|
15
|
-
("strictness", "Strictness", None),
|
|
16
|
-
("libraries", "Libraries Used", ", ".join),
|
|
17
|
-
("tools", "Tools Run", ", ".join),
|
|
18
|
-
("adaptors", "Adaptors", ", ".join),
|
|
19
|
-
("message_count", "Messages Found", None),
|
|
20
|
-
("external_config", "External Config", None),
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
def render_summary(self) -> str:
|
|
24
|
-
output = [
|
|
25
|
-
"Check Information",
|
|
26
|
-
"=================",
|
|
27
|
-
]
|
|
28
|
-
|
|
29
|
-
label_width = max(len(label[1]) for label in self.summary_labels)
|
|
30
|
-
|
|
31
|
-
for key, label, formatter in self.summary_labels:
|
|
32
|
-
if key in self.summary:
|
|
33
|
-
value = self.summary[key]
|
|
34
|
-
if formatter is not None:
|
|
35
|
-
value = formatter(value)
|
|
36
|
-
output.append(f" {label.rjust(label_width)}: {value}")
|
|
37
|
-
|
|
38
|
-
return "\n".join(output)
|
|
39
|
-
|
|
40
|
-
def render_profile(self) -> str:
|
|
41
|
-
output = ["Profile", "=======", "", self.profile.as_yaml().strip()]
|
|
42
|
-
|
|
43
|
-
return "\n".join(output)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/no_member_warnings.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_veryhigh.yaml
RENAMED
|
File without changes
|
{prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_verylow.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|