crackerjack 0.28.0__py3-none-any.whl → 0.30.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of crackerjack might be problematic. Click here for more details.
- crackerjack/__main__.py +56 -2
- crackerjack/code_cleaner.py +980 -0
- crackerjack/crackerjack.py +1552 -1288
- crackerjack/dynamic_config.py +586 -0
- crackerjack/interactive.py +7 -16
- crackerjack/pyproject.toml +4 -1
- {crackerjack-0.28.0.dist-info → crackerjack-0.30.3.dist-info}/METADATA +637 -52
- crackerjack-0.30.3.dist-info/RECORD +16 -0
- crackerjack/.pre-commit-config-ai.yaml +0 -149
- crackerjack/.pre-commit-config-fast.yaml +0 -69
- crackerjack/.pre-commit-config.yaml +0 -114
- crackerjack-0.28.0.dist-info/RECORD +0 -17
- {crackerjack-0.28.0.dist-info → crackerjack-0.30.3.dist-info}/WHEEL +0 -0
- {crackerjack-0.28.0.dist-info → crackerjack-0.30.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
import tempfile
|
|
2
|
+
import typing as t
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import jinja2
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HookMetadata(t.TypedDict):
|
|
9
|
+
id: str
|
|
10
|
+
name: str | None
|
|
11
|
+
repo: str
|
|
12
|
+
rev: str
|
|
13
|
+
tier: int
|
|
14
|
+
time_estimate: float
|
|
15
|
+
stages: list[str] | None
|
|
16
|
+
args: list[str] | None
|
|
17
|
+
files: str | None
|
|
18
|
+
exclude: str | None
|
|
19
|
+
additional_dependencies: list[str] | None
|
|
20
|
+
types_or: list[str] | None
|
|
21
|
+
language: str | None
|
|
22
|
+
entry: str | None
|
|
23
|
+
experimental: bool
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ConfigMode(t.TypedDict):
|
|
27
|
+
max_time: float
|
|
28
|
+
tiers: list[int]
|
|
29
|
+
experimental: bool
|
|
30
|
+
stages: list[str]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
|
|
34
|
+
"structure": [
|
|
35
|
+
{
|
|
36
|
+
"id": "trailing-whitespace",
|
|
37
|
+
"name": "trailing-whitespace",
|
|
38
|
+
"repo": "https://github.com/pre-commit/pre-commit-hooks",
|
|
39
|
+
"rev": "v5.0.0",
|
|
40
|
+
"tier": 1,
|
|
41
|
+
"time_estimate": 0.2,
|
|
42
|
+
"stages": None,
|
|
43
|
+
"args": None,
|
|
44
|
+
"files": None,
|
|
45
|
+
"exclude": None,
|
|
46
|
+
"additional_dependencies": None,
|
|
47
|
+
"types_or": None,
|
|
48
|
+
"language": None,
|
|
49
|
+
"entry": None,
|
|
50
|
+
"experimental": False,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "end-of-file-fixer",
|
|
54
|
+
"name": "end-of-file-fixer",
|
|
55
|
+
"repo": "https://github.com/pre-commit/pre-commit-hooks",
|
|
56
|
+
"rev": "v5.0.0",
|
|
57
|
+
"tier": 1,
|
|
58
|
+
"time_estimate": 0.2,
|
|
59
|
+
"stages": None,
|
|
60
|
+
"args": None,
|
|
61
|
+
"files": None,
|
|
62
|
+
"exclude": None,
|
|
63
|
+
"additional_dependencies": None,
|
|
64
|
+
"types_or": None,
|
|
65
|
+
"language": None,
|
|
66
|
+
"entry": None,
|
|
67
|
+
"experimental": False,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": "check-yaml",
|
|
71
|
+
"name": "check-yaml",
|
|
72
|
+
"repo": "https://github.com/pre-commit/pre-commit-hooks",
|
|
73
|
+
"rev": "v5.0.0",
|
|
74
|
+
"tier": 1,
|
|
75
|
+
"time_estimate": 0.3,
|
|
76
|
+
"stages": None,
|
|
77
|
+
"args": None,
|
|
78
|
+
"files": None,
|
|
79
|
+
"exclude": None,
|
|
80
|
+
"additional_dependencies": None,
|
|
81
|
+
"types_or": None,
|
|
82
|
+
"language": None,
|
|
83
|
+
"entry": None,
|
|
84
|
+
"experimental": False,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "check-toml",
|
|
88
|
+
"name": "check-toml",
|
|
89
|
+
"repo": "https://github.com/pre-commit/pre-commit-hooks",
|
|
90
|
+
"rev": "v5.0.0",
|
|
91
|
+
"tier": 1,
|
|
92
|
+
"time_estimate": 0.3,
|
|
93
|
+
"stages": None,
|
|
94
|
+
"args": None,
|
|
95
|
+
"files": None,
|
|
96
|
+
"exclude": None,
|
|
97
|
+
"additional_dependencies": None,
|
|
98
|
+
"types_or": None,
|
|
99
|
+
"language": None,
|
|
100
|
+
"entry": None,
|
|
101
|
+
"experimental": False,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "check-added-large-files",
|
|
105
|
+
"name": "check-added-large-files",
|
|
106
|
+
"repo": "https://github.com/pre-commit/pre-commit-hooks",
|
|
107
|
+
"rev": "v5.0.0",
|
|
108
|
+
"tier": 1,
|
|
109
|
+
"time_estimate": 0.5,
|
|
110
|
+
"stages": None,
|
|
111
|
+
"args": None,
|
|
112
|
+
"files": None,
|
|
113
|
+
"exclude": None,
|
|
114
|
+
"additional_dependencies": None,
|
|
115
|
+
"types_or": None,
|
|
116
|
+
"language": None,
|
|
117
|
+
"entry": None,
|
|
118
|
+
"experimental": False,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
"package_management": [
|
|
122
|
+
{
|
|
123
|
+
"id": "pyproject-fmt",
|
|
124
|
+
"name": None,
|
|
125
|
+
"repo": "https://github.com/tox-dev/pyproject-fmt",
|
|
126
|
+
"rev": "v2.6.0",
|
|
127
|
+
"tier": 1,
|
|
128
|
+
"time_estimate": 0.5,
|
|
129
|
+
"stages": None,
|
|
130
|
+
"args": ["-n"],
|
|
131
|
+
"files": None,
|
|
132
|
+
"exclude": None,
|
|
133
|
+
"additional_dependencies": None,
|
|
134
|
+
"types_or": None,
|
|
135
|
+
"language": None,
|
|
136
|
+
"entry": None,
|
|
137
|
+
"experimental": False,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"id": "uv-lock",
|
|
141
|
+
"name": None,
|
|
142
|
+
"repo": "https://github.com/astral-sh/uv-pre-commit",
|
|
143
|
+
"rev": "0.7.21",
|
|
144
|
+
"tier": 1,
|
|
145
|
+
"time_estimate": 0.5,
|
|
146
|
+
"stages": None,
|
|
147
|
+
"args": None,
|
|
148
|
+
"files": "^pyproject\\.toml$",
|
|
149
|
+
"exclude": None,
|
|
150
|
+
"additional_dependencies": None,
|
|
151
|
+
"types_or": None,
|
|
152
|
+
"language": None,
|
|
153
|
+
"entry": None,
|
|
154
|
+
"experimental": False,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
"security": [
|
|
158
|
+
{
|
|
159
|
+
"id": "detect-secrets",
|
|
160
|
+
"name": None,
|
|
161
|
+
"repo": "https://github.com/Yelp/detect-secrets",
|
|
162
|
+
"rev": "v1.5.0",
|
|
163
|
+
"tier": 2,
|
|
164
|
+
"time_estimate": 1.0,
|
|
165
|
+
"stages": None,
|
|
166
|
+
"args": None,
|
|
167
|
+
"files": None,
|
|
168
|
+
"exclude": "uv\\.lock|pyproject\\.toml|tests/.*|docs/.*|.*\\.md",
|
|
169
|
+
"additional_dependencies": None,
|
|
170
|
+
"types_or": None,
|
|
171
|
+
"language": None,
|
|
172
|
+
"entry": None,
|
|
173
|
+
"experimental": False,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"id": "bandit",
|
|
177
|
+
"name": None,
|
|
178
|
+
"repo": "https://github.com/PyCQA/bandit",
|
|
179
|
+
"rev": "1.8.6",
|
|
180
|
+
"tier": 3,
|
|
181
|
+
"time_estimate": 3.0,
|
|
182
|
+
"stages": ["pre-push", "manual"],
|
|
183
|
+
"args": ["-c", "pyproject.toml", "-r", "-ll"],
|
|
184
|
+
"files": None,
|
|
185
|
+
"exclude": None,
|
|
186
|
+
"additional_dependencies": None,
|
|
187
|
+
"types_or": None,
|
|
188
|
+
"language": None,
|
|
189
|
+
"entry": None,
|
|
190
|
+
"experimental": False,
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
"formatting": [
|
|
194
|
+
{
|
|
195
|
+
"id": "codespell",
|
|
196
|
+
"name": None,
|
|
197
|
+
"repo": "https://github.com/codespell-project/codespell",
|
|
198
|
+
"rev": "v2.4.1",
|
|
199
|
+
"tier": 2,
|
|
200
|
+
"time_estimate": 1.0,
|
|
201
|
+
"stages": None,
|
|
202
|
+
"args": None,
|
|
203
|
+
"files": None,
|
|
204
|
+
"exclude": None,
|
|
205
|
+
"additional_dependencies": ["tomli"],
|
|
206
|
+
"types_or": None,
|
|
207
|
+
"language": None,
|
|
208
|
+
"entry": None,
|
|
209
|
+
"experimental": False,
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"id": "ruff-check",
|
|
213
|
+
"name": None,
|
|
214
|
+
"repo": "https://github.com/astral-sh/ruff-pre-commit",
|
|
215
|
+
"rev": "v0.12.3",
|
|
216
|
+
"tier": 2,
|
|
217
|
+
"time_estimate": 1.5,
|
|
218
|
+
"stages": None,
|
|
219
|
+
"args": None,
|
|
220
|
+
"files": None,
|
|
221
|
+
"exclude": None,
|
|
222
|
+
"additional_dependencies": None,
|
|
223
|
+
"types_or": None,
|
|
224
|
+
"language": None,
|
|
225
|
+
"entry": None,
|
|
226
|
+
"experimental": False,
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"id": "ruff-format",
|
|
230
|
+
"name": None,
|
|
231
|
+
"repo": "https://github.com/astral-sh/ruff-pre-commit",
|
|
232
|
+
"rev": "v0.12.3",
|
|
233
|
+
"tier": 2,
|
|
234
|
+
"time_estimate": 1.0,
|
|
235
|
+
"stages": None,
|
|
236
|
+
"args": None,
|
|
237
|
+
"files": None,
|
|
238
|
+
"exclude": None,
|
|
239
|
+
"additional_dependencies": None,
|
|
240
|
+
"types_or": None,
|
|
241
|
+
"language": None,
|
|
242
|
+
"entry": None,
|
|
243
|
+
"experimental": False,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"id": "mdformat",
|
|
247
|
+
"name": None,
|
|
248
|
+
"repo": "https://github.com/executablebooks/mdformat",
|
|
249
|
+
"rev": "0.7.22",
|
|
250
|
+
"tier": 2,
|
|
251
|
+
"time_estimate": 0.5,
|
|
252
|
+
"stages": None,
|
|
253
|
+
"args": None,
|
|
254
|
+
"files": None,
|
|
255
|
+
"exclude": None,
|
|
256
|
+
"additional_dependencies": ["mdformat-ruff"],
|
|
257
|
+
"types_or": None,
|
|
258
|
+
"language": None,
|
|
259
|
+
"entry": None,
|
|
260
|
+
"experimental": False,
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
"analysis": [
|
|
264
|
+
{
|
|
265
|
+
"id": "vulture",
|
|
266
|
+
"name": None,
|
|
267
|
+
"repo": "https://github.com/jendrikseipp/vulture",
|
|
268
|
+
"rev": "v2.14",
|
|
269
|
+
"tier": 3,
|
|
270
|
+
"time_estimate": 2.0,
|
|
271
|
+
"stages": ["pre-push", "manual"],
|
|
272
|
+
"args": None,
|
|
273
|
+
"files": None,
|
|
274
|
+
"exclude": None,
|
|
275
|
+
"additional_dependencies": None,
|
|
276
|
+
"types_or": None,
|
|
277
|
+
"language": None,
|
|
278
|
+
"entry": None,
|
|
279
|
+
"experimental": False,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"id": "creosote",
|
|
283
|
+
"name": None,
|
|
284
|
+
"repo": "https://github.com/fredrikaverpil/creosote",
|
|
285
|
+
"rev": "v4.0.3",
|
|
286
|
+
"tier": 3,
|
|
287
|
+
"time_estimate": 1.5,
|
|
288
|
+
"stages": ["pre-push", "manual"],
|
|
289
|
+
"args": None,
|
|
290
|
+
"files": None,
|
|
291
|
+
"exclude": None,
|
|
292
|
+
"additional_dependencies": None,
|
|
293
|
+
"types_or": None,
|
|
294
|
+
"language": None,
|
|
295
|
+
"entry": None,
|
|
296
|
+
"experimental": False,
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"id": "complexipy",
|
|
300
|
+
"name": None,
|
|
301
|
+
"repo": "https://github.com/rohaquinlop/complexipy-pre-commit",
|
|
302
|
+
"rev": "v3.0.0",
|
|
303
|
+
"tier": 3,
|
|
304
|
+
"time_estimate": 2.0,
|
|
305
|
+
"stages": ["pre-push", "manual"],
|
|
306
|
+
"args": ["-d", "low"],
|
|
307
|
+
"files": None,
|
|
308
|
+
"exclude": None,
|
|
309
|
+
"additional_dependencies": None,
|
|
310
|
+
"types_or": None,
|
|
311
|
+
"language": None,
|
|
312
|
+
"entry": None,
|
|
313
|
+
"experimental": False,
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"id": "refurb",
|
|
317
|
+
"name": None,
|
|
318
|
+
"repo": "https://github.com/dosisod/refurb",
|
|
319
|
+
"rev": "v2.1.0",
|
|
320
|
+
"tier": 3,
|
|
321
|
+
"time_estimate": 3.0,
|
|
322
|
+
"stages": ["pre-push", "manual"],
|
|
323
|
+
"args": None,
|
|
324
|
+
"files": None,
|
|
325
|
+
"exclude": None,
|
|
326
|
+
"additional_dependencies": None,
|
|
327
|
+
"types_or": None,
|
|
328
|
+
"language": None,
|
|
329
|
+
"entry": None,
|
|
330
|
+
"experimental": False,
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"id": "autotyping",
|
|
334
|
+
"name": "autotyping",
|
|
335
|
+
"repo": "local",
|
|
336
|
+
"rev": "",
|
|
337
|
+
"tier": 3,
|
|
338
|
+
"time_estimate": 7.0,
|
|
339
|
+
"stages": ["pre-push", "manual"],
|
|
340
|
+
"args": [
|
|
341
|
+
"--aggressive",
|
|
342
|
+
"--only-without-imports",
|
|
343
|
+
"--guess-common-names",
|
|
344
|
+
"crackerjack",
|
|
345
|
+
],
|
|
346
|
+
"files": "^crackerjack/.*\\.py$",
|
|
347
|
+
"exclude": None,
|
|
348
|
+
"additional_dependencies": ["autotyping>=24.3.0", "libcst>=1.1.0"],
|
|
349
|
+
"types_or": ["python", "pyi"],
|
|
350
|
+
"language": "python",
|
|
351
|
+
"entry": "python -m autotyping",
|
|
352
|
+
"experimental": False,
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
"id": "pyright",
|
|
356
|
+
"name": None,
|
|
357
|
+
"repo": "https://github.com/RobertCraigie/pyright-python",
|
|
358
|
+
"rev": "v1.1.403",
|
|
359
|
+
"tier": 3,
|
|
360
|
+
"time_estimate": 5.0,
|
|
361
|
+
"stages": ["pre-push", "manual"],
|
|
362
|
+
"args": None,
|
|
363
|
+
"files": None,
|
|
364
|
+
"exclude": None,
|
|
365
|
+
"additional_dependencies": None,
|
|
366
|
+
"types_or": None,
|
|
367
|
+
"language": None,
|
|
368
|
+
"entry": None,
|
|
369
|
+
"experimental": False,
|
|
370
|
+
},
|
|
371
|
+
],
|
|
372
|
+
"experimental": [
|
|
373
|
+
{
|
|
374
|
+
"id": "pyrefly",
|
|
375
|
+
"name": "pyrefly",
|
|
376
|
+
"repo": "local",
|
|
377
|
+
"rev": "",
|
|
378
|
+
"tier": 3,
|
|
379
|
+
"time_estimate": 5.0,
|
|
380
|
+
"stages": ["manual"],
|
|
381
|
+
"args": ["--check"],
|
|
382
|
+
"files": "^crackerjack/.*\\.py$",
|
|
383
|
+
"exclude": None,
|
|
384
|
+
"additional_dependencies": ["pyrefly>=0.1.0"],
|
|
385
|
+
"types_or": ["python"],
|
|
386
|
+
"language": "python",
|
|
387
|
+
"entry": "python -m pyrefly",
|
|
388
|
+
"experimental": True,
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
"id": "ty",
|
|
392
|
+
"name": "ty",
|
|
393
|
+
"repo": "local",
|
|
394
|
+
"rev": "",
|
|
395
|
+
"tier": 3,
|
|
396
|
+
"time_estimate": 2.0,
|
|
397
|
+
"stages": ["manual"],
|
|
398
|
+
"args": ["--check"],
|
|
399
|
+
"files": "^crackerjack/.*\\.py$",
|
|
400
|
+
"exclude": None,
|
|
401
|
+
"additional_dependencies": ["ty>=0.1.0"],
|
|
402
|
+
"types_or": ["python"],
|
|
403
|
+
"language": "python",
|
|
404
|
+
"entry": "python -m ty",
|
|
405
|
+
"experimental": True,
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
CONFIG_MODES: dict[str, ConfigMode] = {
|
|
411
|
+
"fast": {
|
|
412
|
+
"max_time": 5.0,
|
|
413
|
+
"tiers": [1, 2],
|
|
414
|
+
"experimental": False,
|
|
415
|
+
"stages": ["pre-commit"],
|
|
416
|
+
},
|
|
417
|
+
"comprehensive": {
|
|
418
|
+
"max_time": 30.0,
|
|
419
|
+
"tiers": [1, 2, 3],
|
|
420
|
+
"experimental": False,
|
|
421
|
+
"stages": ["pre-commit", "pre-push", "manual"],
|
|
422
|
+
},
|
|
423
|
+
"experimental": {
|
|
424
|
+
"max_time": 60.0,
|
|
425
|
+
"tiers": [1, 2, 3],
|
|
426
|
+
"experimental": True,
|
|
427
|
+
"stages": ["pre-commit", "pre-push", "manual"],
|
|
428
|
+
},
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
PRE_COMMIT_TEMPLATE = """repos:
|
|
432
|
+
{%- for repo_group in repos %}
|
|
433
|
+
{%- if repo_group.comment %}
|
|
434
|
+
{%- endif %}
|
|
435
|
+
- repo: {{ repo_group.repo }}
|
|
436
|
+
{%- if repo_group.rev %}
|
|
437
|
+
rev: {{ repo_group.rev }}
|
|
438
|
+
{%- endif %}
|
|
439
|
+
hooks:
|
|
440
|
+
{%- for hook in repo_group.hooks %}
|
|
441
|
+
- id: {{ hook.id }}
|
|
442
|
+
{%- if hook.name %}
|
|
443
|
+
name: {{ hook.name }}
|
|
444
|
+
{%- endif %}
|
|
445
|
+
{%- if hook.entry %}
|
|
446
|
+
entry: {{ hook.entry }}
|
|
447
|
+
{%- endif %}
|
|
448
|
+
{%- if hook.language %}
|
|
449
|
+
language: {{ hook.language }}
|
|
450
|
+
{%- endif %}
|
|
451
|
+
{%- if hook.args %}
|
|
452
|
+
args: {{ hook.args | tojson }}
|
|
453
|
+
{%- endif %}
|
|
454
|
+
{%- if hook.files %}
|
|
455
|
+
files: {{ hook.files }}
|
|
456
|
+
{%- endif %}
|
|
457
|
+
{%- if hook.exclude %}
|
|
458
|
+
exclude: {{ hook.exclude }}
|
|
459
|
+
{%- endif %}
|
|
460
|
+
{%- if hook.types_or %}
|
|
461
|
+
types_or: {{ hook.types_or | tojson }}
|
|
462
|
+
{%- endif %}
|
|
463
|
+
{%- if hook.stages %}
|
|
464
|
+
stages: {{ hook.stages | tojson }}
|
|
465
|
+
{%- endif %}
|
|
466
|
+
{%- if hook.additional_dependencies %}
|
|
467
|
+
additional_dependencies: {{ hook.additional_dependencies | tojson }}
|
|
468
|
+
{%- endif %}
|
|
469
|
+
{%- endfor %}
|
|
470
|
+
|
|
471
|
+
{%- endfor %}
|
|
472
|
+
"""
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
class DynamicConfigGenerator:
|
|
476
|
+
def __init__(self) -> None:
|
|
477
|
+
self.template = jinja2.Template(PRE_COMMIT_TEMPLATE)
|
|
478
|
+
|
|
479
|
+
def _should_include_hook(
|
|
480
|
+
self, hook: HookMetadata, config: ConfigMode, enabled_experimental: list[str]
|
|
481
|
+
) -> bool:
|
|
482
|
+
if hook["tier"] not in config["tiers"]:
|
|
483
|
+
return False
|
|
484
|
+
if hook["experimental"]:
|
|
485
|
+
if not config["experimental"]:
|
|
486
|
+
return False
|
|
487
|
+
if enabled_experimental and hook["id"] not in enabled_experimental:
|
|
488
|
+
return False
|
|
489
|
+
if hook["time_estimate"] > config["max_time"]:
|
|
490
|
+
return False
|
|
491
|
+
return True
|
|
492
|
+
|
|
493
|
+
def filter_hooks_for_mode(
|
|
494
|
+
self, mode: str, enabled_experimental: list[str] | None = None
|
|
495
|
+
) -> list[HookMetadata]:
|
|
496
|
+
config = CONFIG_MODES[mode]
|
|
497
|
+
filtered_hooks = []
|
|
498
|
+
enabled_experimental = enabled_experimental or []
|
|
499
|
+
for category_hooks in HOOKS_REGISTRY.values():
|
|
500
|
+
for hook in category_hooks:
|
|
501
|
+
if self._should_include_hook(hook, config, enabled_experimental):
|
|
502
|
+
filtered_hooks.append(hook)
|
|
503
|
+
|
|
504
|
+
return filtered_hooks
|
|
505
|
+
|
|
506
|
+
def group_hooks_by_repo(
|
|
507
|
+
self, hooks: list[HookMetadata]
|
|
508
|
+
) -> dict[tuple[str, str], list[HookMetadata]]:
|
|
509
|
+
repo_groups: dict[tuple[str, str], list[HookMetadata]] = {}
|
|
510
|
+
for hook in hooks:
|
|
511
|
+
key = (hook["repo"], hook["rev"])
|
|
512
|
+
if key not in repo_groups:
|
|
513
|
+
repo_groups[key] = []
|
|
514
|
+
repo_groups[key].append(hook)
|
|
515
|
+
|
|
516
|
+
return repo_groups
|
|
517
|
+
|
|
518
|
+
def _get_repo_comment(self, repo_url: str) -> str | None:
|
|
519
|
+
if repo_url == "https://github.com/pre-commit/pre-commit-hooks":
|
|
520
|
+
return "File structure and format validators"
|
|
521
|
+
elif (
|
|
522
|
+
"security" in repo_url
|
|
523
|
+
or "bandit" in repo_url
|
|
524
|
+
or "detect-secrets" in repo_url
|
|
525
|
+
):
|
|
526
|
+
return "Security checks"
|
|
527
|
+
elif "ruff" in repo_url or "mdformat" in repo_url or "codespell" in repo_url:
|
|
528
|
+
return "Code formatting and quality"
|
|
529
|
+
elif repo_url == "local":
|
|
530
|
+
return "Local tools and custom hooks"
|
|
531
|
+
return None
|
|
532
|
+
|
|
533
|
+
def generate_config(
|
|
534
|
+
self, mode: str, enabled_experimental: list[str] | None = None
|
|
535
|
+
) -> str:
|
|
536
|
+
filtered_hooks = self.filter_hooks_for_mode(mode, enabled_experimental)
|
|
537
|
+
repo_groups = self.group_hooks_by_repo(filtered_hooks)
|
|
538
|
+
repos = []
|
|
539
|
+
for (repo_url, rev), hooks in repo_groups.items():
|
|
540
|
+
repo_data = {
|
|
541
|
+
"repo": repo_url,
|
|
542
|
+
"rev": rev,
|
|
543
|
+
"hooks": hooks,
|
|
544
|
+
"comment": self._get_repo_comment(repo_url),
|
|
545
|
+
}
|
|
546
|
+
repos.append(repo_data)
|
|
547
|
+
|
|
548
|
+
return self.template.render(repos=repos)
|
|
549
|
+
|
|
550
|
+
def create_temp_config(
|
|
551
|
+
self, mode: str, enabled_experimental: list[str] | None = None
|
|
552
|
+
) -> Path:
|
|
553
|
+
config_content = self.generate_config(mode, enabled_experimental)
|
|
554
|
+
temp_file = tempfile.NamedTemporaryFile(
|
|
555
|
+
mode="w",
|
|
556
|
+
suffix=".yaml",
|
|
557
|
+
prefix=f"crackerjack-{mode}-",
|
|
558
|
+
delete=False,
|
|
559
|
+
encoding="utf-8",
|
|
560
|
+
)
|
|
561
|
+
temp_file.write(config_content)
|
|
562
|
+
temp_file.flush()
|
|
563
|
+
temp_file.close()
|
|
564
|
+
|
|
565
|
+
return Path(temp_file.name)
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def generate_config_for_mode(
|
|
569
|
+
mode: str, enabled_experimental: list[str] | None = None
|
|
570
|
+
) -> Path:
|
|
571
|
+
return DynamicConfigGenerator().create_temp_config(mode, enabled_experimental)
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def get_available_modes() -> list[str]:
|
|
575
|
+
return list(CONFIG_MODES.keys())
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def add_experimental_hook(hook_id: str, hook_config: HookMetadata) -> None:
|
|
579
|
+
hook_config["experimental"] = True
|
|
580
|
+
HOOKS_REGISTRY["experimental"].append(hook_config)
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def remove_experimental_hook(hook_id: str) -> None:
|
|
584
|
+
HOOKS_REGISTRY["experimental"] = [
|
|
585
|
+
hook for hook in HOOKS_REGISTRY["experimental"] if hook["id"] != hook_id
|
|
586
|
+
]
|
crackerjack/interactive.py
CHANGED
|
@@ -169,10 +169,7 @@ class InteractiveCLI:
|
|
|
169
169
|
version_text = Text(f"v{version}", style="dim cyan")
|
|
170
170
|
subtitle = Text("Your Python project management toolkit", style="italic")
|
|
171
171
|
panel = Panel(
|
|
172
|
-
f"{title} {version_text}\n{subtitle}",
|
|
173
|
-
box=ROUNDED,
|
|
174
|
-
border_style="cyan",
|
|
175
|
-
expand=False,
|
|
172
|
+
f"{title} {version_text}\n{subtitle}", border_style="cyan", expand=False
|
|
176
173
|
)
|
|
177
174
|
self.console.print(panel)
|
|
178
175
|
self.console.print()
|
|
@@ -198,9 +195,7 @@ class InteractiveCLI:
|
|
|
198
195
|
Layout(name="main"),
|
|
199
196
|
Layout(name="footer", size=3),
|
|
200
197
|
)
|
|
201
|
-
layout["main"].split_row(
|
|
202
|
-
Layout(name="tasks", ratio=1), Layout(name="details", ratio=2)
|
|
203
|
-
)
|
|
198
|
+
layout["main"].split_row(Layout(name="tasks"), Layout(name="details", ratio=2))
|
|
204
199
|
return layout
|
|
205
200
|
|
|
206
201
|
def show_task_status(self, task: Task) -> Panel:
|
|
@@ -233,8 +228,6 @@ class InteractiveCLI:
|
|
|
233
228
|
def show_task_table(self) -> Table:
|
|
234
229
|
table = Table(
|
|
235
230
|
title="Workflow Tasks",
|
|
236
|
-
box=ROUNDED,
|
|
237
|
-
show_header=True,
|
|
238
231
|
header_style="bold white",
|
|
239
232
|
)
|
|
240
233
|
table.add_column("Task", style="white")
|
|
@@ -262,7 +255,7 @@ class InteractiveCLI:
|
|
|
262
255
|
self.console.clear()
|
|
263
256
|
layout = self._setup_interactive_layout()
|
|
264
257
|
progress_tracker = self._create_progress_tracker()
|
|
265
|
-
with Live(layout
|
|
258
|
+
with Live(layout) as live:
|
|
266
259
|
try:
|
|
267
260
|
self._execute_workflow_loop(layout, progress_tracker, live)
|
|
268
261
|
self._display_final_summary(layout)
|
|
@@ -274,9 +267,9 @@ class InteractiveCLI:
|
|
|
274
267
|
def _setup_interactive_layout(self) -> Layout:
|
|
275
268
|
layout = self.setup_layout()
|
|
276
269
|
layout["header"].update(
|
|
277
|
-
Panel("Crackerjack Interactive Mode", style="bold white"
|
|
270
|
+
Panel("Crackerjack Interactive Mode", style="bold white")
|
|
278
271
|
)
|
|
279
|
-
layout["footer"].update(Panel("Press Ctrl+C to exit", style="dim"
|
|
272
|
+
layout["footer"].update(Panel("Press Ctrl+C to exit", style="dim"))
|
|
280
273
|
return layout
|
|
281
274
|
|
|
282
275
|
def _create_progress_tracker(self) -> dict[str, t.Any]:
|
|
@@ -353,7 +346,7 @@ class InteractiveCLI:
|
|
|
353
346
|
layout["tasks"].update(self.show_task_table())
|
|
354
347
|
task_counts = self._count_tasks_by_status()
|
|
355
348
|
summary = Panel(
|
|
356
|
-
f"Workflow completed!\n\n"
|
|
349
|
+
f"🏆 Workflow completed!\n\n"
|
|
357
350
|
f"[green]✅ Successful tasks: {task_counts['successful']}[/green]\n"
|
|
358
351
|
f"[red]❌ Failed tasks: {task_counts['failed']}[/red]\n"
|
|
359
352
|
f"[blue]⏩ Skipped tasks: {task_counts['skipped']}[/blue]",
|
|
@@ -382,9 +375,7 @@ class InteractiveCLI:
|
|
|
382
375
|
}
|
|
383
376
|
|
|
384
377
|
def _handle_user_interruption(self, layout: Layout) -> None:
|
|
385
|
-
layout["footer"].update(
|
|
386
|
-
Panel("Interrupted by user", style="yellow", box=ROUNDED)
|
|
387
|
-
)
|
|
378
|
+
layout["footer"].update(Panel("Interrupted by user", style="yellow"))
|
|
388
379
|
|
|
389
380
|
def ask_for_file(
|
|
390
381
|
self, prompt: str, directory: Path, default: str | None = None
|
crackerjack/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ requires = [ "hatchling" ]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "crackerjack"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.30.2"
|
|
8
8
|
description = "Crackerjack: code quality toolkit"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
keywords = [
|
|
@@ -44,6 +44,7 @@ dependencies = [
|
|
|
44
44
|
"aiofiles>=24.1",
|
|
45
45
|
"autotyping>=24.9",
|
|
46
46
|
"hatchling>=1.25",
|
|
47
|
+
"jinja2>=3.1",
|
|
47
48
|
"keyring>=25.6",
|
|
48
49
|
"pre-commit>=4.2",
|
|
49
50
|
"pydantic>=2.11.7",
|
|
@@ -67,7 +68,9 @@ urls.repository = "https://github.com/lesleslie/crackerjack"
|
|
|
67
68
|
|
|
68
69
|
[dependency-groups]
|
|
69
70
|
dev = [
|
|
71
|
+
"complexipy>=3.3",
|
|
70
72
|
"pyyaml>=6.0.2",
|
|
73
|
+
"refurb>=2.1",
|
|
71
74
|
]
|
|
72
75
|
|
|
73
76
|
[tool.ruff]
|