crackerjack 0.29.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.

@@ -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
+ ]
@@ -4,7 +4,7 @@ requires = [ "hatchling" ]
4
4
 
5
5
  [project]
6
6
  name = "crackerjack"
7
- version = "0.28.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",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crackerjack
3
- Version: 0.29.0
3
+ Version: 0.30.3
4
4
  Summary: Crackerjack: code quality toolkit
5
5
  Project-URL: documentation, https://github.com/lesleslie/crackerjack
6
6
  Project-URL: homepage, https://github.com/lesleslie/crackerjack
@@ -26,6 +26,7 @@ Requires-Python: >=3.13
26
26
  Requires-Dist: aiofiles>=24.1
27
27
  Requires-Dist: autotyping>=24.9
28
28
  Requires-Dist: hatchling>=1.25
29
+ Requires-Dist: jinja2>=3.1
29
30
  Requires-Dist: keyring>=25.6
30
31
  Requires-Dist: pre-commit>=4.2
31
32
  Requires-Dist: pydantic>=2.11.7
@@ -0,0 +1,16 @@
1
+ crackerjack/.gitignore,sha256=ELNEeIblDM1mKM-LJyj_iLcBzZJPbeJ89YXupvXuLug,498
2
+ crackerjack/.libcst.codemod.yaml,sha256=a8DlErRAIPV1nE6QlyXPAzTOgkB24_spl2E9hphuf5s,772
3
+ crackerjack/.pdm.toml,sha256=dZe44HRcuxxCFESGG8SZIjmc-cGzSoyK3Hs6t4NYA8w,23
4
+ crackerjack/__init__.py,sha256=8tBSPAru_YDuPpjz05cL7pNbZjYFoRT_agGd_FWa3gY,839
5
+ crackerjack/__main__.py,sha256=VqY8ptjQCjaISQfjit-Wn2B-MnLc9pvC56tnIx2hRak,10604
6
+ crackerjack/code_cleaner.py,sha256=mCgXfQDcbnetp5ZGdrmyOeFaZrvYHNNgKVN4Wr4ybCk,38021
7
+ crackerjack/crackerjack.py,sha256=ATfmSadUoSvMTIFdGE-3WVvzggD6DlhWFE3g7RvM7FU,149205
8
+ crackerjack/dynamic_config.py,sha256=siPYhVIggzKJ9EzSUV6QJxSrt-ke9bH7TpcbTdxx2QY,17700
9
+ crackerjack/errors.py,sha256=Wcv0rXfzV9pHOoXYrhQEjyJd4kUUBbdiY-5M9nI8pDw,4050
10
+ crackerjack/interactive.py,sha256=JLeKnGbQSgACh20xjZTLVSJLGrU5xBAOHJt9bbshUyM,15911
11
+ crackerjack/py313.py,sha256=1imwWZUQwcZt09yIrnTSWr73ITTKH8yXlgIe2ESTeLA,5977
12
+ crackerjack/pyproject.toml,sha256=H4gFzwNie8uPnknofGaKlA22cfGaw4RKqooPrHOOVdw,6931
13
+ crackerjack-0.30.3.dist-info/METADATA,sha256=-EQu7n5zY6mP78qWNuInmbQKHXuHeoC8s6IzlpJ5xMI,47785
14
+ crackerjack-0.30.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
+ crackerjack-0.30.3.dist-info/licenses/LICENSE,sha256=fDt371P6_6sCu7RyqiZH_AhT1LdN3sN1zjBtqEhDYCk,1531
16
+ crackerjack-0.30.3.dist-info/RECORD,,