dycw-pre-commit-hooks 0.14.26__py3-none-any.whl → 0.14.39__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.
@@ -1,80 +1,225 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Callable
3
4
  from functools import partial
4
- from itertools import chain
5
- from typing import TYPE_CHECKING
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING, Literal, assert_never
6
7
 
7
8
  from click import command, option
8
9
  from utilities.click import CONTEXT_SETTINGS
10
+ from utilities.concurrent import concurrent_map
11
+ from utilities.iterables import always_iterable
9
12
  from utilities.os import is_pytest
13
+ from utilities.types import PathLike
10
14
 
11
15
  from pre_commit_hooks.constants import (
12
16
  BUILTIN,
13
17
  DEFAULT_PYTHON_VERSION,
18
+ DOCKERFMT_URL,
14
19
  DYCW_PRE_COMMIT_HOOKS_URL,
20
+ FORMATTER_PRIORITY,
21
+ LINTER_PRIORITY,
22
+ LOCAL,
15
23
  PRE_COMMIT_CONFIG_YAML,
16
24
  PYPROJECT_TOML,
17
25
  RUFF_URL,
26
+ SHELLCHECK_URL,
27
+ SHFMT_URL,
18
28
  STD_PRE_COMMIT_HOOKS_URL,
29
+ STYLUA_URL,
30
+ TAPLO_URL,
31
+ UV_URL,
32
+ XMLFORMATTER_URL,
19
33
  paths_argument,
34
+ python_option,
35
+ python_package_name_option,
36
+ python_uv_index_option,
37
+ python_uv_native_tls_option,
20
38
  python_version_option,
21
39
  )
22
- from pre_commit_hooks.utilities import add_pre_commit_config_repo, run_all_maybe_raise
40
+ from pre_commit_hooks.utilities import (
41
+ apply,
42
+ ensure_contains_partial_dict,
43
+ get_set_list_dicts,
44
+ run_all_maybe_raise,
45
+ yield_yaml_dict,
46
+ )
23
47
 
24
48
  if TYPE_CHECKING:
25
- from collections.abc import Callable
49
+ from collections.abc import Callable, MutableSet
26
50
  from pathlib import Path
27
51
 
28
- from utilities.types import PathLike
52
+ from utilities.types import IntOrAll, MaybeSequenceStr, PathLike
29
53
 
30
54
 
31
55
  @command(**CONTEXT_SETTINGS)
32
56
  @paths_argument
33
- @option("--python", is_flag=True, default=False)
57
+ @option("--ci", is_flag=True, default=False)
58
+ @option("--direnv", is_flag=True, default=False)
59
+ @option("--docker", is_flag=True, default=False)
60
+ @option("--fish", is_flag=True, default=False)
61
+ @option("--lua", is_flag=True, default=False)
62
+ @option("--prettier", is_flag=True, default=False)
63
+ @python_option
64
+ @python_package_name_option
65
+ @python_uv_index_option
66
+ @python_uv_native_tls_option
34
67
  @python_version_option
68
+ @option("--shell", is_flag=True, default=False)
69
+ @option("--toml", is_flag=True, default=False)
70
+ @option("--xml", is_flag=True, default=False)
71
+ @option("--max-workers", type=int, default=None)
35
72
  def _main(
36
73
  *,
37
74
  paths: tuple[Path, ...],
75
+ ci: bool = False,
76
+ direnv: bool = False,
77
+ docker: bool = False,
78
+ fish: bool = False,
79
+ lua: bool = False,
80
+ prettier: bool = False,
38
81
  python: bool = False,
82
+ python_package_name: str | None = None,
83
+ python_uv_index: MaybeSequenceStr | None = None,
84
+ python_uv_native_tls: bool = False,
39
85
  python_version: str = DEFAULT_PYTHON_VERSION,
86
+ shell: bool = False,
87
+ toml: bool = False,
88
+ xml: bool = False,
89
+ max_workers: int | None = None,
40
90
  ) -> None:
41
91
  if is_pytest():
42
92
  return
43
- funcs: list[Callable[[], bool]] = list(
44
- chain(
45
- (partial(_add_check_versions_consistent, path=p) for p in paths),
46
- (partial(_add_format_pre_commit_config, path=p) for p in paths),
47
- (partial(_add_run_prek_autoupdate, path=p) for p in paths),
48
- (partial(_add_run_version_bump, path=p) for p in paths),
49
- (partial(_add_standard_hooks, path=p) for p in paths),
93
+ run_all_maybe_raise(
94
+ *(
95
+ partial(
96
+ _run,
97
+ path=p,
98
+ ci=ci,
99
+ direnv=direnv,
100
+ docker=docker,
101
+ fish=fish,
102
+ lua=lua,
103
+ prettier=prettier,
104
+ python=python,
105
+ python_package_name=python_package_name,
106
+ python_uv_index=python_uv_index,
107
+ python_uv_native_tls=python_uv_native_tls,
108
+ python_version=python_version,
109
+ shell=shell,
110
+ toml=toml,
111
+ xml=xml,
112
+ max_workers="all" if max_workers is None else max_workers,
113
+ )
114
+ for p in paths
50
115
  )
51
116
  )
117
+
118
+
119
+ def _run(
120
+ *,
121
+ path: PathLike = PRE_COMMIT_CONFIG_YAML,
122
+ ci: bool = False,
123
+ direnv: bool = False,
124
+ docker: bool = False,
125
+ fish: bool = False,
126
+ lua: bool = False,
127
+ prettier: bool = False,
128
+ python: bool = False,
129
+ python_package_name: str | None = None,
130
+ python_uv_index: MaybeSequenceStr | None = None,
131
+ python_uv_native_tls: bool = False,
132
+ python_version: str = DEFAULT_PYTHON_VERSION,
133
+ shell: bool = False,
134
+ toml: bool = False,
135
+ xml: bool = False,
136
+ max_workers: IntOrAll = "all",
137
+ ) -> bool:
138
+ funcs: list[Callable[[], bool]] = [
139
+ partial(_add_check_version_bumped, path=path),
140
+ partial(_add_check_versions_consistent, path=path),
141
+ partial(_add_format_pre_commit_config, path=path),
142
+ partial(_add_run_prek_autoupdate, path=path),
143
+ partial(_add_run_version_bump, path=path),
144
+ partial(_add_setup_bump_my_version, path=path),
145
+ partial(_add_standard_hooks, path=path),
146
+ ]
147
+ if ci:
148
+ funcs.append(partial(_add_update_ci_action_versions, path=path))
149
+ funcs.append(partial(_add_update_ci_extensions, path=path))
150
+ if direnv:
151
+ funcs.append(partial(_add_setup_direnv, path=path))
152
+ if docker:
153
+ funcs.append(partial(_add_dockerfmt, path=path))
154
+ if fish:
155
+ funcs.append(partial(_add_fish_indent, path=path))
156
+ if lua:
157
+ funcs.append(partial(_add_stylua, path=path))
158
+ if prettier:
159
+ funcs.append(partial(_add_prettier, path=path))
52
160
  if python:
53
- funcs.extend(partial(_add_add_future_import_annotations, path=p) for p in paths)
54
- funcs.extend(partial(_add_format_requirements, path=p) for p in paths)
55
- funcs.extend(partial(_add_replace_sequence_str, path=p) for p in paths)
56
- funcs.extend(partial(_add_ruff_check, path=p) for p in paths)
57
- funcs.extend(partial(_add_ruff_format, path=p) for p in paths)
58
- funcs.extend(partial(_add_setup_git, path=p) for p in paths)
59
- funcs.extend(
60
- partial(_add_setup_pyright, path=p, python_version=python_version)
61
- for p in paths
161
+ funcs.append(partial(_add_add_future_import_annotations, path=path))
162
+ funcs.append(partial(_add_format_requirements, path=path))
163
+ funcs.append(partial(_add_replace_sequence_str, path=path))
164
+ funcs.append(partial(_add_ruff_check, path=path))
165
+ funcs.append(partial(_add_ruff_format, path=path))
166
+ funcs.append(
167
+ partial(
168
+ _add_setup_bump_my_version,
169
+ path=path,
170
+ python_package_name=python_package_name,
171
+ )
62
172
  )
63
- funcs.extend(
64
- partial(_add_setup_ruff, path=p, python_version=python_version)
65
- for p in paths
173
+ funcs.append(
174
+ partial(
175
+ _add_setup_direnv,
176
+ path=path,
177
+ python=python,
178
+ python_uv_index=python_uv_index,
179
+ python_uv_native_tls=python_uv_native_tls,
180
+ python_version=python_version,
181
+ )
182
+ )
183
+ funcs.append(partial(_add_setup_git, path=path))
184
+ funcs.append(
185
+ partial(_add_setup_pyright, path=path, python_version=python_version)
66
186
  )
67
- funcs.extend(partial(_add_update_requirements, path=p) for p in paths)
68
- run_all_maybe_raise(*funcs)
187
+ funcs.append(partial(_add_setup_ruff, path=path, python_version=python_version))
188
+ funcs.append(partial(_add_update_requirements, path=path))
189
+ funcs.append(partial(_add_uv_lock, path=path))
190
+ if shell:
191
+ funcs.append(partial(_add_shellcheck, path=path))
192
+ funcs.append(partial(_add_shfmt, path=path))
193
+ if toml:
194
+ funcs.append(partial(_add_taplo_format, path=path))
195
+ if xml:
196
+ funcs.append(partial(_add_xmlformatter, path=path))
197
+ return all(
198
+ concurrent_map(apply, funcs, parallelism="threads", max_workers=max_workers)
199
+ )
200
+
201
+
202
+ def _add_check_version_bumped(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
203
+ modifications: set[Path] = set()
204
+ _add_hook(
205
+ DYCW_PRE_COMMIT_HOOKS_URL,
206
+ "check-version-bumped",
207
+ path=path,
208
+ modifications=modifications,
209
+ rev=True,
210
+ type_="linter",
211
+ )
212
+ return len(modifications) == 0
69
213
 
70
214
 
71
215
  def _add_check_versions_consistent(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
72
216
  modifications: set[Path] = set()
73
- add_pre_commit_config_repo(
217
+ _add_hook(
74
218
  DYCW_PRE_COMMIT_HOOKS_URL,
75
219
  "check-versions-consistent",
76
220
  path=path,
77
221
  modifications=modifications,
222
+ rev=True,
78
223
  type_="linter",
79
224
  )
80
225
  return len(modifications) == 0
@@ -82,11 +227,12 @@ def _add_check_versions_consistent(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -
82
227
 
83
228
  def _add_format_pre_commit_config(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
84
229
  modifications: set[Path] = set()
85
- add_pre_commit_config_repo(
230
+ _add_hook(
86
231
  DYCW_PRE_COMMIT_HOOKS_URL,
87
232
  "format-pre-commit-config",
88
233
  path=path,
89
234
  modifications=modifications,
235
+ rev=True,
90
236
  type_="linter",
91
237
  )
92
238
  return len(modifications) == 0
@@ -94,11 +240,12 @@ def _add_format_pre_commit_config(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) ->
94
240
 
95
241
  def _add_run_prek_autoupdate(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
96
242
  modifications: set[Path] = set()
97
- add_pre_commit_config_repo(
243
+ _add_hook(
98
244
  DYCW_PRE_COMMIT_HOOKS_URL,
99
245
  "run-prek-autoupdate",
100
246
  path=path,
101
247
  modifications=modifications,
248
+ rev=True,
102
249
  type_="formatter",
103
250
  )
104
251
  return len(modifications) == 0
@@ -106,11 +253,31 @@ def _add_run_prek_autoupdate(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool
106
253
 
107
254
  def _add_run_version_bump(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
108
255
  modifications: set[Path] = set()
109
- add_pre_commit_config_repo(
256
+ _add_hook(
110
257
  DYCW_PRE_COMMIT_HOOKS_URL,
111
258
  "run-version-bump",
112
259
  path=path,
113
260
  modifications=modifications,
261
+ rev=True,
262
+ type_="formatter",
263
+ )
264
+ return len(modifications) == 0
265
+
266
+
267
+ def _add_setup_bump_my_version(
268
+ *, path: PathLike = PRE_COMMIT_CONFIG_YAML, python_package_name: str | None = None
269
+ ) -> bool:
270
+ modifications: set[Path] = set()
271
+ args: list[str] = []
272
+ if python_package_name is not None:
273
+ args.append(f"--python-package-name={python_package_name}")
274
+ _add_hook(
275
+ DYCW_PRE_COMMIT_HOOKS_URL,
276
+ "setup-bump-my-version",
277
+ path=path,
278
+ modifications=modifications,
279
+ rev=True,
280
+ args=args if len(args) >= 1 else None,
114
281
  type_="formatter",
115
282
  )
116
283
  return len(modifications) == 0
@@ -118,100 +285,100 @@ def _add_run_version_bump(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
118
285
 
119
286
  def _add_standard_hooks(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
120
287
  modifications: set[Path] = set()
121
- add_pre_commit_config_repo(
288
+ _add_hook(
122
289
  BUILTIN,
123
290
  "check-added-large-files",
124
291
  path=path,
125
292
  modifications=modifications,
126
293
  type_="linter",
127
294
  )
128
- add_pre_commit_config_repo(
295
+ _add_hook(
129
296
  BUILTIN,
130
297
  "check-case-conflict",
131
298
  path=path,
132
299
  modifications=modifications,
133
300
  type_="linter",
134
301
  )
135
- add_pre_commit_config_repo(
302
+ _add_hook(
136
303
  BUILTIN,
137
304
  "check-executables-have-shebangs",
138
305
  path=path,
139
306
  modifications=modifications,
140
307
  type_="linter",
141
308
  )
142
- add_pre_commit_config_repo(
309
+ _add_hook(
143
310
  BUILTIN, "check-json", path=path, modifications=modifications, type_="linter"
144
311
  )
145
- add_pre_commit_config_repo(
312
+ _add_hook(
146
313
  BUILTIN, "check-json5", path=path, modifications=modifications, type_="linter"
147
314
  )
148
- add_pre_commit_config_repo(
315
+ _add_hook(
149
316
  BUILTIN,
150
317
  "check-merge-conflict",
151
318
  path=path,
152
319
  modifications=modifications,
153
320
  type_="linter",
154
321
  )
155
- add_pre_commit_config_repo(
322
+ _add_hook(
156
323
  BUILTIN,
157
324
  "check-symlinks",
158
325
  path=path,
159
326
  modifications=modifications,
160
327
  type_="linter",
161
328
  )
162
- add_pre_commit_config_repo(
329
+ _add_hook(
163
330
  BUILTIN, "check-toml", path=path, modifications=modifications, type_="linter"
164
331
  )
165
- add_pre_commit_config_repo(
332
+ _add_hook(
166
333
  BUILTIN, "check-xml", path=path, modifications=modifications, type_="linter"
167
334
  )
168
- add_pre_commit_config_repo(
335
+ _add_hook(
169
336
  BUILTIN, "check-yaml", path=path, modifications=modifications, type_="linter"
170
337
  )
171
- add_pre_commit_config_repo(
338
+ _add_hook(
172
339
  BUILTIN,
173
340
  "detect-private-key",
174
341
  path=path,
175
342
  modifications=modifications,
176
343
  type_="linter",
177
344
  )
178
- add_pre_commit_config_repo(
345
+ _add_hook(
179
346
  BUILTIN,
180
347
  "end-of-file-fixer",
181
348
  path=path,
182
349
  modifications=modifications,
183
350
  type_="formatter",
184
351
  )
185
- add_pre_commit_config_repo(
352
+ _add_hook(
186
353
  BUILTIN,
187
354
  "fix-byte-order-marker",
188
355
  path=path,
189
356
  modifications=modifications,
190
357
  type_="formatter",
191
358
  )
192
- add_pre_commit_config_repo(
359
+ _add_hook(
193
360
  BUILTIN,
194
361
  "mixed-line-ending",
195
362
  path=path,
196
363
  modifications=modifications,
197
- args=("add", ["--fix=lf"]),
364
+ args=["--fix=lf"],
198
365
  type_="formatter",
199
366
  )
200
- add_pre_commit_config_repo(
367
+ _add_hook(
201
368
  BUILTIN,
202
369
  "no-commit-to-branch",
203
370
  path=path,
204
371
  modifications=modifications,
205
372
  type_="linter",
206
373
  )
207
- add_pre_commit_config_repo(
374
+ _add_hook(
208
375
  BUILTIN,
209
376
  "trailing-whitespace",
210
377
  path=path,
211
378
  modifications=modifications,
212
379
  type_="formatter",
213
380
  )
214
- add_pre_commit_config_repo(
381
+ _add_hook(
215
382
  STD_PRE_COMMIT_HOOKS_URL,
216
383
  "check-illegal-windows-names",
217
384
  path=path,
@@ -219,7 +386,7 @@ def _add_standard_hooks(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
219
386
  rev=True,
220
387
  type_="linter",
221
388
  )
222
- add_pre_commit_config_repo(
389
+ _add_hook(
223
390
  STD_PRE_COMMIT_HOOKS_URL,
224
391
  "destroyed-symlinks",
225
392
  path=path,
@@ -227,13 +394,129 @@ def _add_standard_hooks(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
227
394
  rev=True,
228
395
  type_="linter",
229
396
  )
230
- add_pre_commit_config_repo(
397
+ _add_hook(
231
398
  STD_PRE_COMMIT_HOOKS_URL,
232
399
  "pretty-format-json",
233
400
  path=path,
234
401
  modifications=modifications,
235
402
  rev=True,
236
- args=("add", ["--autofix"]),
403
+ args=["--autofix"],
404
+ type_="formatter",
405
+ )
406
+ return len(modifications) == 0
407
+
408
+
409
+ def _add_update_ci_action_versions(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
410
+ modifications: set[Path] = set()
411
+ _add_hook(
412
+ DYCW_PRE_COMMIT_HOOKS_URL,
413
+ "update-ci-action-versions",
414
+ path=path,
415
+ modifications=modifications,
416
+ rev=True,
417
+ type_="formatter",
418
+ )
419
+ return len(modifications) == 0
420
+
421
+
422
+ def _add_update_ci_extensions(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
423
+ modifications: set[Path] = set()
424
+ _add_hook(
425
+ DYCW_PRE_COMMIT_HOOKS_URL,
426
+ "update-ci-extensions",
427
+ path=path,
428
+ modifications=modifications,
429
+ rev=True,
430
+ type_="formatter",
431
+ )
432
+ return len(modifications) == 0
433
+
434
+
435
+ def _add_setup_direnv(
436
+ *,
437
+ path: PathLike = PRE_COMMIT_CONFIG_YAML,
438
+ python: bool = False,
439
+ python_uv_index: MaybeSequenceStr | None = None,
440
+ python_uv_native_tls: bool = False,
441
+ python_version: str = DEFAULT_PYTHON_VERSION,
442
+ ) -> bool:
443
+ modifications: set[Path] = set()
444
+ args: list[str] = []
445
+ if python:
446
+ args.append("--python")
447
+ if python_uv_index is not None:
448
+ args.append(f"--python-uv-index={','.join(always_iterable(python_uv_index))}")
449
+ if python_uv_native_tls:
450
+ args.append("--python-uv-native-tls")
451
+ if python_version is not None:
452
+ args.append(f"--python-version={python_version}")
453
+ _add_hook(
454
+ DYCW_PRE_COMMIT_HOOKS_URL,
455
+ "setup-direnv",
456
+ path=path,
457
+ modifications=modifications,
458
+ rev=True,
459
+ args=args if len(args) >= 1 else None,
460
+ type_="formatter",
461
+ )
462
+ return len(modifications) == 0
463
+
464
+
465
+ def _add_dockerfmt(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
466
+ modifications: set[Path] = set()
467
+ _add_hook(
468
+ DOCKERFMT_URL,
469
+ "dockerfmt",
470
+ path=path,
471
+ modifications=modifications,
472
+ rev=True,
473
+ args=["--newline", "--write"],
474
+ type_="formatter",
475
+ )
476
+ return len(modifications) == 0
477
+
478
+
479
+ def _add_fish_indent(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
480
+ modifications: set[Path] = set()
481
+ _add_hook(
482
+ LOCAL,
483
+ "fish_indent",
484
+ path=path,
485
+ modifications=modifications,
486
+ name="fish_indent",
487
+ entry="fish_indent",
488
+ language="unsupported",
489
+ files=r"\.fish$",
490
+ args=["--write"],
491
+ type_="formatter",
492
+ )
493
+ return len(modifications) == 0
494
+
495
+
496
+ def _add_stylua(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
497
+ modifications: set[Path] = set()
498
+ _add_hook(
499
+ STYLUA_URL,
500
+ "stlya",
501
+ path=path,
502
+ modifications=modifications,
503
+ rev=True,
504
+ type_="formatter",
505
+ )
506
+ return len(modifications) == 0
507
+
508
+
509
+ def _add_prettier(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
510
+ modifications: set[Path] = set()
511
+ _add_hook(
512
+ LOCAL,
513
+ "prettier",
514
+ path=path,
515
+ modifications=modifications,
516
+ name="prettier",
517
+ entry="npx prettier --write",
518
+ language="unsupported",
519
+ types_or=["markdown", "yaml"],
237
520
  type_="formatter",
238
521
  )
239
522
  return len(modifications) == 0
@@ -243,7 +526,7 @@ def _add_add_future_import_annotations(
243
526
  *, path: PathLike = PRE_COMMIT_CONFIG_YAML
244
527
  ) -> bool:
245
528
  modifications: set[Path] = set()
246
- add_pre_commit_config_repo(
529
+ _add_hook(
247
530
  DYCW_PRE_COMMIT_HOOKS_URL,
248
531
  "add-future-import-annotations",
249
532
  path=path,
@@ -256,7 +539,7 @@ def _add_add_future_import_annotations(
256
539
 
257
540
  def _add_format_requirements(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
258
541
  modifications: set[Path] = set()
259
- add_pre_commit_config_repo(
542
+ _add_hook(
260
543
  DYCW_PRE_COMMIT_HOOKS_URL,
261
544
  "format-requirements",
262
545
  path=path,
@@ -269,7 +552,7 @@ def _add_format_requirements(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool
269
552
 
270
553
  def _add_replace_sequence_str(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
271
554
  modifications: set[Path] = set()
272
- add_pre_commit_config_repo(
555
+ _add_hook(
273
556
  DYCW_PRE_COMMIT_HOOKS_URL,
274
557
  "replace-sequence-str",
275
558
  path=path,
@@ -282,13 +565,13 @@ def _add_replace_sequence_str(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> boo
282
565
 
283
566
  def _add_ruff_check(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
284
567
  modifications: set[Path] = set()
285
- add_pre_commit_config_repo(
568
+ _add_hook(
286
569
  RUFF_URL,
287
570
  "ruff-check",
288
571
  path=path,
289
572
  modifications=modifications,
290
573
  rev=True,
291
- args=("exact", ["--fix"]),
574
+ args=["--fix"],
292
575
  type_="linter",
293
576
  )
294
577
  return len(modifications) == 0
@@ -296,7 +579,7 @@ def _add_ruff_check(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
296
579
 
297
580
  def _add_ruff_format(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
298
581
  modifications: set[Path] = set()
299
- add_pre_commit_config_repo(
582
+ _add_hook(
300
583
  RUFF_URL,
301
584
  "ruff-format",
302
585
  path=path,
@@ -309,7 +592,7 @@ def _add_ruff_format(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
309
592
 
310
593
  def _add_setup_git(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
311
594
  modifications: set[Path] = set()
312
- add_pre_commit_config_repo(
595
+ _add_hook(
313
596
  DYCW_PRE_COMMIT_HOOKS_URL,
314
597
  "setup-git",
315
598
  path=path,
@@ -326,13 +609,13 @@ def _add_setup_pyright(
326
609
  python_version: str = DEFAULT_PYTHON_VERSION,
327
610
  ) -> bool:
328
611
  modifications: set[Path] = set()
329
- add_pre_commit_config_repo(
612
+ _add_hook(
330
613
  DYCW_PRE_COMMIT_HOOKS_URL,
331
614
  "setup-pyright",
332
615
  path=path,
333
616
  modifications=modifications,
334
617
  rev=True,
335
- args=("exact", [f"--python-version={python_version}"]),
618
+ args=[f"--python-version={python_version}"],
336
619
  type_="formatter",
337
620
  )
338
621
  return len(modifications) == 0
@@ -344,13 +627,13 @@ def _add_setup_ruff(
344
627
  python_version: str = DEFAULT_PYTHON_VERSION,
345
628
  ) -> bool:
346
629
  modifications: set[Path] = set()
347
- add_pre_commit_config_repo(
630
+ _add_hook(
348
631
  DYCW_PRE_COMMIT_HOOKS_URL,
349
632
  "setup-ruff",
350
633
  path=path,
351
634
  modifications=modifications,
352
635
  rev=True,
353
- args=("exact", [f"--python-version={python_version}"]),
636
+ args=[f"--python-version={python_version}"],
354
637
  type_="formatter",
355
638
  )
356
639
  return len(modifications) == 0
@@ -358,7 +641,7 @@ def _add_setup_ruff(
358
641
 
359
642
  def _add_update_requirements(*, path: PathLike = PYPROJECT_TOML) -> bool:
360
643
  modifications: set[Path] = set()
361
- add_pre_commit_config_repo(
644
+ _add_hook(
362
645
  DYCW_PRE_COMMIT_HOOKS_URL,
363
646
  "update-requirements",
364
647
  path=path,
@@ -369,5 +652,134 @@ def _add_update_requirements(*, path: PathLike = PYPROJECT_TOML) -> bool:
369
652
  return len(modifications) == 0
370
653
 
371
654
 
655
+ def _add_uv_lock(*, path: PathLike = PYPROJECT_TOML) -> bool:
656
+ modifications: set[Path] = set()
657
+ _add_hook(
658
+ UV_URL,
659
+ "uv-lock",
660
+ path=path,
661
+ modifications=modifications,
662
+ rev=True,
663
+ args=["--upgrade", "--resolution", "highest", "--prerelease", "disallow"],
664
+ type_="formatter",
665
+ )
666
+ return len(modifications) == 0
667
+
668
+
669
+ def _add_shellcheck(*, path: PathLike = PYPROJECT_TOML) -> bool:
670
+ modifications: set[Path] = set()
671
+ _add_hook(
672
+ SHELLCHECK_URL,
673
+ "shellcheck",
674
+ path=path,
675
+ modifications=modifications,
676
+ rev=True,
677
+ type_="linter",
678
+ )
679
+ return len(modifications) == 0
680
+
681
+
682
+ def _add_shfmt(*, path: PathLike = PYPROJECT_TOML) -> bool:
683
+ modifications: set[Path] = set()
684
+ _add_hook(
685
+ SHFMT_URL,
686
+ "shfmt",
687
+ path=path,
688
+ modifications=modifications,
689
+ rev=True,
690
+ type_="formatter",
691
+ )
692
+ return len(modifications) == 0
693
+
694
+
695
+ def _add_taplo_format(*, path: PathLike = PYPROJECT_TOML) -> bool:
696
+ modifications: set[Path] = set()
697
+ _add_hook(
698
+ TAPLO_URL,
699
+ "taplo-format",
700
+ path=path,
701
+ modifications=modifications,
702
+ rev=True,
703
+ args=[
704
+ "--option",
705
+ "indent_tables=true",
706
+ "--option",
707
+ "indent_entries=true",
708
+ "--option",
709
+ "reorder_keys=true",
710
+ ],
711
+ type_="linter",
712
+ )
713
+ return len(modifications) == 0
714
+
715
+
716
+ def _add_xmlformatter(*, path: PathLike = PYPROJECT_TOML) -> bool:
717
+ modifications: set[Path] = set()
718
+ _add_hook(
719
+ XMLFORMATTER_URL,
720
+ "xml-formatter",
721
+ path=path,
722
+ modifications=modifications,
723
+ rev=True,
724
+ types=[],
725
+ types_or=["plist", "xml"],
726
+ args=["--eof-newline"],
727
+ type_="formatter",
728
+ )
729
+ return len(modifications) == 0
730
+
731
+
732
+ ##
733
+
734
+
735
+ def _add_hook(
736
+ url: str,
737
+ id_: str,
738
+ /,
739
+ *,
740
+ path: PathLike = PRE_COMMIT_CONFIG_YAML,
741
+ modifications: MutableSet[Path] | None = None,
742
+ rev: bool = False,
743
+ name: str | None = None,
744
+ entry: str | None = None,
745
+ language: str | None = None,
746
+ files: str | None = None,
747
+ types: list[str] | None = None,
748
+ types_or: list[str] | None = None,
749
+ args: list[str] | None = None,
750
+ type_: Literal["formatter", "linter"] | None = None,
751
+ ) -> None:
752
+ with yield_yaml_dict(path, modifications=modifications) as dict_:
753
+ repos_list = get_set_list_dicts(dict_, "repos")
754
+ repo_dict = ensure_contains_partial_dict(
755
+ repos_list, {"repo": url}, extra={"rev": "master"} if rev else {}
756
+ )
757
+ hooks_list = get_set_list_dicts(repo_dict, "hooks")
758
+ hook_dict = ensure_contains_partial_dict(hooks_list, {"id": id_})
759
+ if name is not None:
760
+ hook_dict["name"] = name
761
+ if entry is not None:
762
+ hook_dict["entry"] = entry
763
+ if language is not None:
764
+ hook_dict["language"] = language
765
+ if files is not None:
766
+ hook_dict["files"] = files
767
+ if types is not None:
768
+ hook_dict["types"] = types
769
+ if types_or is not None:
770
+ hook_dict["types_or"] = types_or
771
+ if args is not None:
772
+ hook_dict["args"] = args
773
+ match type_:
774
+ case "formatter":
775
+ hook_dict["priority"] = FORMATTER_PRIORITY
776
+ case "linter":
777
+ hook_dict["priority"] = LINTER_PRIORITY
778
+ case None:
779
+ ...
780
+ case never:
781
+ assert_never(never)
782
+
783
+
372
784
  if __name__ == "__main__":
373
785
  _main()