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.
Files changed (74) hide show
  1. {prospector-1.15.3 → prospector-1.16.0}/PKG-INFO +1 -2
  2. {prospector-1.15.3 → prospector-1.16.0}/prospector/autodetect.py +1 -1
  3. {prospector-1.15.3 → prospector-1.16.0}/prospector/blender_combinations.yaml +88 -84
  4. {prospector-1.15.3 → prospector-1.16.0}/prospector/config/configuration.py +1 -1
  5. prospector-1.16.0/prospector/formatters/base_summary.py +90 -0
  6. {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/pylint.py +17 -12
  7. {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/text.py +3 -0
  8. prospector-1.16.0/prospector/identify.py +90 -0
  9. {prospector-1.15.3 → prospector-1.16.0}/prospector/pathutils.py +16 -2
  10. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profile.py +5 -2
  11. {prospector-1.15.3 → prospector-1.16.0}/prospector/suppression.py +2 -2
  12. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/base.py +2 -2
  13. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/mypy/__init__.py +2 -2
  14. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/profile_validator/__init__.py +1 -1
  15. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pylint/__init__.py +6 -4
  16. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/ruff/__init__.py +2 -2
  17. {prospector-1.15.3 → prospector-1.16.0}/pyproject.toml +1 -2
  18. {prospector-1.15.3 → prospector-1.16.0}/setup.py +1 -2
  19. prospector-1.15.3/prospector/formatters/base_summary.py +0 -43
  20. {prospector-1.15.3 → prospector-1.16.0}/LICENSE +0 -0
  21. {prospector-1.15.3 → prospector-1.16.0}/README.rst +0 -0
  22. {prospector-1.15.3 → prospector-1.16.0}/prospector/__init__.py +0 -0
  23. {prospector-1.15.3 → prospector-1.16.0}/prospector/__main__.py +0 -0
  24. {prospector-1.15.3 → prospector-1.16.0}/prospector/blender.py +0 -0
  25. {prospector-1.15.3 → prospector-1.16.0}/prospector/compat.py +0 -0
  26. {prospector-1.15.3 → prospector-1.16.0}/prospector/config/__init__.py +0 -0
  27. {prospector-1.15.3 → prospector-1.16.0}/prospector/config/datatype.py +0 -0
  28. {prospector-1.15.3 → prospector-1.16.0}/prospector/encoding.py +0 -0
  29. {prospector-1.15.3 → prospector-1.16.0}/prospector/exceptions.py +0 -0
  30. {prospector-1.15.3 → prospector-1.16.0}/prospector/finder.py +0 -0
  31. {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/__init__.py +0 -0
  32. {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/base.py +0 -0
  33. {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/emacs.py +0 -0
  34. {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/grouped.py +0 -0
  35. {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/json.py +0 -0
  36. {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/pylint_parseable.py +0 -0
  37. {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/vscode.py +0 -0
  38. {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/xunit.py +0 -0
  39. {prospector-1.15.3 → prospector-1.16.0}/prospector/formatters/yaml.py +0 -0
  40. {prospector-1.15.3 → prospector-1.16.0}/prospector/message.py +0 -0
  41. {prospector-1.15.3 → prospector-1.16.0}/prospector/postfilter.py +0 -0
  42. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/__init__.py +0 -0
  43. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/exceptions.py +0 -0
  44. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/default.yaml +0 -0
  45. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/doc_warnings.yaml +0 -0
  46. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/flake8.yaml +0 -0
  47. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/full_pep8.yaml +0 -0
  48. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/member_warnings.yaml +0 -0
  49. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/no_doc_warnings.yaml +0 -0
  50. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/no_member_warnings.yaml +0 -0
  51. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/no_pep8.yaml +0 -0
  52. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/no_test_warnings.yaml +0 -0
  53. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_high.yaml +0 -0
  54. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_low.yaml +0 -0
  55. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_medium.yaml +0 -0
  56. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_none.yaml +0 -0
  57. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_veryhigh.yaml +0 -0
  58. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/strictness_verylow.yaml +0 -0
  59. {prospector-1.15.3 → prospector-1.16.0}/prospector/profiles/profiles/test_warnings.yaml +0 -0
  60. {prospector-1.15.3 → prospector-1.16.0}/prospector/run.py +0 -0
  61. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/__init__.py +0 -0
  62. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/bandit/__init__.py +0 -0
  63. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/dodgy/__init__.py +0 -0
  64. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/exceptions.py +0 -0
  65. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/mccabe/__init__.py +0 -0
  66. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pycodestyle/__init__.py +0 -0
  67. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pydocstyle/__init__.py +0 -0
  68. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pyflakes/__init__.py +0 -0
  69. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pylint/collector.py +0 -0
  70. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pylint/linter.py +0 -0
  71. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pyright/__init__.py +0 -0
  72. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/pyroma/__init__.py +0 -0
  73. {prospector-1.15.3 → prospector-1.16.0}/prospector/tools/utils.py +0 -0
  74. {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.15.3
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", "flask")
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, flask. This will be"
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
- output.append(
56
- template
57
- % {
58
- "path": self._make_path(message.location),
59
- "line": message.location.line,
60
- "character": message.location.character,
61
- "source": message.source,
62
- "code": message.code,
63
- "function": message.location.function,
64
- "message": message.message.strip(),
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:
@@ -24,6 +24,9 @@ class TextFormatter(SummaryFormatter):
24
24
  )
25
25
 
26
26
  output.append(f" {message.message}")
27
+ ci_annotation = self.get_ci_annotation(message)
28
+ if ci_annotation:
29
+ output.append(ci_annotation)
27
30
 
28
31
  return "\n".join(output)
29
32
 
@@ -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
- # TODO: is this too simple?
11
- return path.suffix == ".py"
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", "flask")
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
- data = pkgutil.get_data(module_name, file_name)
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", "flask")
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.15.3"
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.15.3',
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