dycw-pre-commit-hooks 0.14.26__py3-none-any.whl → 0.14.53__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,317 @@
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,
33
+ ci_pytest_os_option,
34
+ ci_pytest_python_version_option,
35
+ ci_pytest_runs_on_option,
36
+ description_option,
19
37
  paths_argument,
38
+ python_option,
39
+ python_package_name_external_option,
40
+ python_package_name_internal_option,
41
+ python_uv_index_option,
42
+ python_uv_native_tls_option,
20
43
  python_version_option,
44
+ repo_name_option,
45
+ )
46
+ from pre_commit_hooks.utilities import (
47
+ apply,
48
+ ensure_contains_partial_dict,
49
+ get_set_list_dicts,
50
+ run_all_maybe_raise,
51
+ yield_yaml_dict,
21
52
  )
22
- from pre_commit_hooks.utilities import add_pre_commit_config_repo, run_all_maybe_raise
23
53
 
24
54
  if TYPE_CHECKING:
25
- from collections.abc import Callable
55
+ from collections.abc import Callable, MutableSet
26
56
  from pathlib import Path
27
57
 
28
- from utilities.types import PathLike
58
+ from utilities.types import IntOrAll, MaybeSequenceStr, PathLike
29
59
 
30
60
 
31
61
  @command(**CONTEXT_SETTINGS)
32
62
  @paths_argument
33
- @option("--python", is_flag=True, default=False)
63
+ @option("--ci", is_flag=True, default=False)
64
+ @option("--ci-github", is_flag=True, default=False)
65
+ @option("--ci-gitea", is_flag=True, default=False)
66
+ @ci_pytest_os_option
67
+ @ci_pytest_python_version_option
68
+ @ci_pytest_runs_on_option
69
+ @description_option
70
+ @option("--direnv", is_flag=True, default=False)
71
+ @option("--docker", is_flag=True, default=False)
72
+ @option("--fish", is_flag=True, default=False)
73
+ @option("--lua", is_flag=True, default=False)
74
+ @option("--prettier", is_flag=True, default=False)
75
+ @python_option
76
+ @python_package_name_external_option
77
+ @python_package_name_internal_option
78
+ @python_uv_index_option
79
+ @python_uv_native_tls_option
34
80
  @python_version_option
81
+ @repo_name_option
82
+ @option("--shell", is_flag=True, default=False)
83
+ @option("--toml", is_flag=True, default=False)
84
+ @option("--xml", is_flag=True, default=False)
85
+ @option("--max-workers", type=int, default=None)
35
86
  def _main(
36
87
  *,
37
88
  paths: tuple[Path, ...],
89
+ ci: bool = False,
90
+ ci_github: bool = False,
91
+ ci_gitea: bool = False,
92
+ ci_pytest_os: MaybeSequenceStr | None = None,
93
+ ci_pytest_python_version: MaybeSequenceStr | None = None,
94
+ ci_pytest_runs_on: MaybeSequenceStr | None = None,
95
+ description: str | None = None,
96
+ direnv: bool = False,
97
+ docker: bool = False,
98
+ fish: bool = False,
99
+ lua: bool = False,
100
+ prettier: bool = False,
38
101
  python: bool = False,
102
+ python_package_name_external: str | None = None,
103
+ python_package_name_internal: str | None = None,
104
+ python_uv_index: MaybeSequenceStr | None = None,
105
+ python_uv_native_tls: bool = False,
39
106
  python_version: str = DEFAULT_PYTHON_VERSION,
107
+ repo_name: str | None = None,
108
+ shell: bool = False,
109
+ toml: bool = False,
110
+ xml: bool = False,
111
+ max_workers: int | None = None,
40
112
  ) -> None:
41
113
  if is_pytest():
42
114
  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),
115
+ funcs: list[Callable[[], bool]] = [
116
+ partial(
117
+ _run,
118
+ path=p,
119
+ ci=ci,
120
+ ci_github=ci_github,
121
+ ci_gitea=ci_gitea,
122
+ ci_pytest_os=ci_pytest_os,
123
+ ci_pytest_python_version=ci_pytest_python_version,
124
+ ci_pytest_runs_on=ci_pytest_runs_on,
125
+ description=description,
126
+ direnv=direnv,
127
+ docker=docker,
128
+ fish=fish,
129
+ lua=lua,
130
+ prettier=prettier,
131
+ python=python,
132
+ python_package_name_external=python_package_name_external,
133
+ python_package_name_internal=python_package_name_internal,
134
+ python_uv_index=python_uv_index,
135
+ python_uv_native_tls=python_uv_native_tls,
136
+ python_version=python_version,
137
+ repo_name=repo_name,
138
+ shell=shell,
139
+ toml=toml,
140
+ xml=xml,
141
+ max_workers="all" if max_workers is None else max_workers,
50
142
  )
51
- )
143
+ for p in paths
144
+ ]
145
+ run_all_maybe_raise(*funcs)
146
+
147
+
148
+ def _run(
149
+ *,
150
+ path: PathLike = PRE_COMMIT_CONFIG_YAML,
151
+ ci: bool = False,
152
+ ci_github: bool = False,
153
+ ci_gitea: bool = False,
154
+ ci_pytest_os: MaybeSequenceStr | None = None,
155
+ ci_pytest_python_version: MaybeSequenceStr | None = None,
156
+ ci_pytest_runs_on: MaybeSequenceStr | None = None,
157
+ description: str | None = None,
158
+ direnv: bool = False,
159
+ docker: bool = False,
160
+ fish: bool = False,
161
+ lua: bool = False,
162
+ prettier: bool = False,
163
+ python: bool = False,
164
+ python_package_name_external: str | None = None,
165
+ python_package_name_internal: str | None = None,
166
+ python_uv_index: MaybeSequenceStr | None = None,
167
+ python_uv_native_tls: bool = False,
168
+ python_version: str = DEFAULT_PYTHON_VERSION,
169
+ repo_name: str | None = None,
170
+ shell: bool = False,
171
+ toml: bool = False,
172
+ xml: bool = False,
173
+ max_workers: IntOrAll = "all",
174
+ ) -> bool:
175
+ funcs: list[Callable[[], bool]] = [
176
+ partial(_add_check_version_bumped, path=path),
177
+ partial(_add_check_versions_consistent, path=path),
178
+ partial(_add_format_pre_commit_config, path=path),
179
+ partial(_add_run_prek_autoupdate, path=path),
180
+ partial(_add_run_version_bump, path=path),
181
+ partial(_add_setup_bump_my_version, path=path),
182
+ partial(
183
+ _add_setup_readme, path=path, repo_name=repo_name, description=description
184
+ ),
185
+ partial(_add_standard_hooks, path=path),
186
+ ]
187
+ if ci or ci_github or ci_gitea:
188
+ funcs.append(partial(_add_update_ci_action_versions, path=path))
189
+ funcs.append(partial(_add_update_ci_extensions, path=path))
190
+ if ci_github:
191
+ funcs.append(
192
+ partial(
193
+ _add_setup_ci_pull_request,
194
+ path=path,
195
+ ci_pytest_os=ci_pytest_os,
196
+ ci_pytest_runs_on=ci_pytest_runs_on,
197
+ ci_pytest_python_version=ci_pytest_python_version,
198
+ python_uv_native_tls=python_uv_native_tls,
199
+ python_version=python_version,
200
+ repo_name=repo_name,
201
+ )
202
+ )
203
+ funcs.append(
204
+ partial(
205
+ _add_setup_ci_push, path=path, python_uv_native_tls=python_uv_native_tls
206
+ )
207
+ )
208
+ if ci_gitea:
209
+ funcs.append(
210
+ partial(
211
+ _add_setup_ci_pull_request,
212
+ path=path,
213
+ gitea=True,
214
+ ci_pytest_os=ci_pytest_os,
215
+ ci_pytest_runs_on=ci_pytest_runs_on,
216
+ ci_pytest_python_version=ci_pytest_python_version,
217
+ python_uv_native_tls=python_uv_native_tls,
218
+ python_version=python_version,
219
+ repo_name=repo_name,
220
+ )
221
+ )
222
+ funcs.append(
223
+ partial(
224
+ _add_setup_ci_push,
225
+ path=path,
226
+ gitea=True,
227
+ python_uv_native_tls=python_uv_native_tls,
228
+ )
229
+ )
230
+ if direnv:
231
+ funcs.append(partial(_add_setup_direnv, path=path))
232
+ if docker:
233
+ funcs.append(partial(_add_dockerfmt, path=path))
234
+ if fish:
235
+ funcs.append(partial(_add_fish_indent, path=path))
236
+ if lua:
237
+ funcs.append(partial(_add_stylua, path=path))
238
+ if prettier:
239
+ funcs.append(partial(_add_prettier, path=path))
52
240
  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
241
+ funcs.append(partial(_add_add_future_import_annotations, path=path))
242
+ funcs.append(partial(_add_format_requirements, path=path))
243
+ funcs.append(partial(_add_replace_sequence_str, path=path))
244
+ funcs.append(partial(_add_ruff_check, path=path))
245
+ funcs.append(partial(_add_ruff_format, path=path))
246
+ funcs.append(
247
+ partial(
248
+ _add_setup_bump_my_version,
249
+ path=path,
250
+ python_package_name_internal=python_package_name_internal,
251
+ )
62
252
  )
63
- funcs.extend(
64
- partial(_add_setup_ruff, path=p, python_version=python_version)
65
- for p in paths
253
+ funcs.append(partial(_add_setup_coverage, path=path))
254
+ funcs.append(
255
+ partial(
256
+ _add_setup_direnv,
257
+ path=path,
258
+ python=python,
259
+ python_uv_index=python_uv_index,
260
+ python_uv_native_tls=python_uv_native_tls,
261
+ python_version=python_version,
262
+ )
66
263
  )
67
- funcs.extend(partial(_add_update_requirements, path=p) for p in paths)
68
- run_all_maybe_raise(*funcs)
264
+ funcs.append(partial(_add_setup_git, path=path))
265
+ funcs.append(
266
+ partial(
267
+ _add_setup_pyproject,
268
+ path=path,
269
+ python_version=python_version,
270
+ description=description,
271
+ python_package_name_external=python_package_name_external,
272
+ python_package_name_internal=python_package_name_internal,
273
+ python_uv_index=python_uv_index,
274
+ )
275
+ )
276
+ funcs.append(
277
+ partial(_add_setup_pyright, path=path, python_version=python_version)
278
+ )
279
+ funcs.append(partial(_add_setup_ruff, path=path, python_version=python_version))
280
+ funcs.append(partial(_add_update_requirements, path=path))
281
+ funcs.append(partial(_add_uv_lock, path=path))
282
+ if shell:
283
+ funcs.append(partial(_add_shellcheck, path=path))
284
+ funcs.append(partial(_add_shfmt, path=path))
285
+ if toml:
286
+ funcs.append(partial(_add_taplo_format, path=path))
287
+ if xml:
288
+ funcs.append(partial(_add_xmlformatter, path=path))
289
+ return all(
290
+ concurrent_map(apply, funcs, parallelism="threads", max_workers=max_workers)
291
+ )
292
+
293
+
294
+ def _add_check_version_bumped(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
295
+ modifications: set[Path] = set()
296
+ _add_hook(
297
+ DYCW_PRE_COMMIT_HOOKS_URL,
298
+ "check-version-bumped",
299
+ path=path,
300
+ modifications=modifications,
301
+ rev=True,
302
+ type_="linter",
303
+ )
304
+ return len(modifications) == 0
69
305
 
70
306
 
71
307
  def _add_check_versions_consistent(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
72
308
  modifications: set[Path] = set()
73
- add_pre_commit_config_repo(
309
+ _add_hook(
74
310
  DYCW_PRE_COMMIT_HOOKS_URL,
75
311
  "check-versions-consistent",
76
312
  path=path,
77
313
  modifications=modifications,
314
+ rev=True,
78
315
  type_="linter",
79
316
  )
80
317
  return len(modifications) == 0
@@ -82,11 +319,12 @@ def _add_check_versions_consistent(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -
82
319
 
83
320
  def _add_format_pre_commit_config(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
84
321
  modifications: set[Path] = set()
85
- add_pre_commit_config_repo(
322
+ _add_hook(
86
323
  DYCW_PRE_COMMIT_HOOKS_URL,
87
324
  "format-pre-commit-config",
88
325
  path=path,
89
326
  modifications=modifications,
327
+ rev=True,
90
328
  type_="linter",
91
329
  )
92
330
  return len(modifications) == 0
@@ -94,11 +332,12 @@ def _add_format_pre_commit_config(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) ->
94
332
 
95
333
  def _add_run_prek_autoupdate(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
96
334
  modifications: set[Path] = set()
97
- add_pre_commit_config_repo(
335
+ _add_hook(
98
336
  DYCW_PRE_COMMIT_HOOKS_URL,
99
337
  "run-prek-autoupdate",
100
338
  path=path,
101
339
  modifications=modifications,
340
+ rev=True,
102
341
  type_="formatter",
103
342
  )
104
343
  return len(modifications) == 0
@@ -106,11 +345,70 @@ def _add_run_prek_autoupdate(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool
106
345
 
107
346
  def _add_run_version_bump(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
108
347
  modifications: set[Path] = set()
109
- add_pre_commit_config_repo(
348
+ _add_hook(
110
349
  DYCW_PRE_COMMIT_HOOKS_URL,
111
350
  "run-version-bump",
112
351
  path=path,
113
352
  modifications=modifications,
353
+ rev=True,
354
+ type_="formatter",
355
+ )
356
+ return len(modifications) == 0
357
+
358
+
359
+ def _add_setup_bump_my_version(
360
+ *,
361
+ path: PathLike = PRE_COMMIT_CONFIG_YAML,
362
+ python_package_name_internal: str | None = None,
363
+ ) -> bool:
364
+ modifications: set[Path] = set()
365
+ args: list[str] = []
366
+ if python_package_name_internal is not None:
367
+ args.append(f"--python-package-name-internal={python_package_name_internal}")
368
+ _add_hook(
369
+ DYCW_PRE_COMMIT_HOOKS_URL,
370
+ "setup-bump-my-version",
371
+ path=path,
372
+ modifications=modifications,
373
+ rev=True,
374
+ args=args if len(args) >= 1 else None,
375
+ type_="formatter",
376
+ )
377
+ return len(modifications) == 0
378
+
379
+
380
+ def _add_setup_coverage(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
381
+ modifications: set[Path] = set()
382
+ _add_hook(
383
+ DYCW_PRE_COMMIT_HOOKS_URL,
384
+ "setup-coverage",
385
+ path=path,
386
+ modifications=modifications,
387
+ rev=True,
388
+ type_="formatter",
389
+ )
390
+ return len(modifications) == 0
391
+
392
+
393
+ def _add_setup_readme(
394
+ *,
395
+ path: PathLike = PRE_COMMIT_CONFIG_YAML,
396
+ repo_name: str | None = None,
397
+ description: str | None = None,
398
+ ) -> bool:
399
+ modifications: set[Path] = set()
400
+ args: list[str] = []
401
+ if repo_name is not None:
402
+ args.append(f"--repo-name={repo_name}")
403
+ if description is not None:
404
+ args.append(f"--description={description}")
405
+ _add_hook(
406
+ DYCW_PRE_COMMIT_HOOKS_URL,
407
+ "setup-readme",
408
+ path=path,
409
+ modifications=modifications,
410
+ rev=True,
411
+ args=args if len(args) >= 1 else None,
114
412
  type_="formatter",
115
413
  )
116
414
  return len(modifications) == 0
@@ -118,100 +416,100 @@ def _add_run_version_bump(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
118
416
 
119
417
  def _add_standard_hooks(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
120
418
  modifications: set[Path] = set()
121
- add_pre_commit_config_repo(
419
+ _add_hook(
122
420
  BUILTIN,
123
421
  "check-added-large-files",
124
422
  path=path,
125
423
  modifications=modifications,
126
424
  type_="linter",
127
425
  )
128
- add_pre_commit_config_repo(
426
+ _add_hook(
129
427
  BUILTIN,
130
428
  "check-case-conflict",
131
429
  path=path,
132
430
  modifications=modifications,
133
431
  type_="linter",
134
432
  )
135
- add_pre_commit_config_repo(
433
+ _add_hook(
136
434
  BUILTIN,
137
435
  "check-executables-have-shebangs",
138
436
  path=path,
139
437
  modifications=modifications,
140
438
  type_="linter",
141
439
  )
142
- add_pre_commit_config_repo(
440
+ _add_hook(
143
441
  BUILTIN, "check-json", path=path, modifications=modifications, type_="linter"
144
442
  )
145
- add_pre_commit_config_repo(
443
+ _add_hook(
146
444
  BUILTIN, "check-json5", path=path, modifications=modifications, type_="linter"
147
445
  )
148
- add_pre_commit_config_repo(
446
+ _add_hook(
149
447
  BUILTIN,
150
448
  "check-merge-conflict",
151
449
  path=path,
152
450
  modifications=modifications,
153
451
  type_="linter",
154
452
  )
155
- add_pre_commit_config_repo(
453
+ _add_hook(
156
454
  BUILTIN,
157
455
  "check-symlinks",
158
456
  path=path,
159
457
  modifications=modifications,
160
458
  type_="linter",
161
459
  )
162
- add_pre_commit_config_repo(
460
+ _add_hook(
163
461
  BUILTIN, "check-toml", path=path, modifications=modifications, type_="linter"
164
462
  )
165
- add_pre_commit_config_repo(
463
+ _add_hook(
166
464
  BUILTIN, "check-xml", path=path, modifications=modifications, type_="linter"
167
465
  )
168
- add_pre_commit_config_repo(
466
+ _add_hook(
169
467
  BUILTIN, "check-yaml", path=path, modifications=modifications, type_="linter"
170
468
  )
171
- add_pre_commit_config_repo(
469
+ _add_hook(
172
470
  BUILTIN,
173
471
  "detect-private-key",
174
472
  path=path,
175
473
  modifications=modifications,
176
474
  type_="linter",
177
475
  )
178
- add_pre_commit_config_repo(
476
+ _add_hook(
179
477
  BUILTIN,
180
478
  "end-of-file-fixer",
181
479
  path=path,
182
480
  modifications=modifications,
183
481
  type_="formatter",
184
482
  )
185
- add_pre_commit_config_repo(
483
+ _add_hook(
186
484
  BUILTIN,
187
485
  "fix-byte-order-marker",
188
486
  path=path,
189
487
  modifications=modifications,
190
488
  type_="formatter",
191
489
  )
192
- add_pre_commit_config_repo(
490
+ _add_hook(
193
491
  BUILTIN,
194
492
  "mixed-line-ending",
195
493
  path=path,
196
494
  modifications=modifications,
197
- args=("add", ["--fix=lf"]),
495
+ args=["--fix=lf"],
198
496
  type_="formatter",
199
497
  )
200
- add_pre_commit_config_repo(
498
+ _add_hook(
201
499
  BUILTIN,
202
500
  "no-commit-to-branch",
203
501
  path=path,
204
502
  modifications=modifications,
205
503
  type_="linter",
206
504
  )
207
- add_pre_commit_config_repo(
505
+ _add_hook(
208
506
  BUILTIN,
209
507
  "trailing-whitespace",
210
508
  path=path,
211
509
  modifications=modifications,
212
510
  type_="formatter",
213
511
  )
214
- add_pre_commit_config_repo(
512
+ _add_hook(
215
513
  STD_PRE_COMMIT_HOOKS_URL,
216
514
  "check-illegal-windows-names",
217
515
  path=path,
@@ -219,7 +517,7 @@ def _add_standard_hooks(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
219
517
  rev=True,
220
518
  type_="linter",
221
519
  )
222
- add_pre_commit_config_repo(
520
+ _add_hook(
223
521
  STD_PRE_COMMIT_HOOKS_URL,
224
522
  "destroyed-symlinks",
225
523
  path=path,
@@ -227,13 +525,196 @@ def _add_standard_hooks(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
227
525
  rev=True,
228
526
  type_="linter",
229
527
  )
230
- add_pre_commit_config_repo(
528
+ _add_hook(
231
529
  STD_PRE_COMMIT_HOOKS_URL,
232
530
  "pretty-format-json",
233
531
  path=path,
234
532
  modifications=modifications,
235
533
  rev=True,
236
- args=("add", ["--autofix"]),
534
+ args=["--autofix"],
535
+ type_="formatter",
536
+ )
537
+ return len(modifications) == 0
538
+
539
+
540
+ def _add_update_ci_action_versions(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
541
+ modifications: set[Path] = set()
542
+ _add_hook(
543
+ DYCW_PRE_COMMIT_HOOKS_URL,
544
+ "update-ci-action-versions",
545
+ path=path,
546
+ modifications=modifications,
547
+ rev=True,
548
+ type_="formatter",
549
+ )
550
+ return len(modifications) == 0
551
+
552
+
553
+ def _add_update_ci_extensions(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
554
+ modifications: set[Path] = set()
555
+ _add_hook(
556
+ DYCW_PRE_COMMIT_HOOKS_URL,
557
+ "update-ci-extensions",
558
+ path=path,
559
+ modifications=modifications,
560
+ rev=True,
561
+ type_="formatter",
562
+ )
563
+ return len(modifications) == 0
564
+
565
+
566
+ def _add_setup_ci_pull_request(
567
+ *,
568
+ path: PathLike = PRE_COMMIT_CONFIG_YAML,
569
+ gitea: bool = False,
570
+ ci_pytest_os: MaybeSequenceStr | None = None,
571
+ ci_pytest_runs_on: MaybeSequenceStr | None = None,
572
+ ci_pytest_python_version: MaybeSequenceStr | None = None,
573
+ python_uv_native_tls: bool = False,
574
+ python_version: str = DEFAULT_PYTHON_VERSION,
575
+ repo_name: str | None = None,
576
+ ) -> bool:
577
+ modifications: set[Path] = set()
578
+ args: list[str] = []
579
+ if gitea:
580
+ args.append("--gitea")
581
+ if ci_pytest_os is not None:
582
+ args.append(f"--ci-pytest-os={','.join(always_iterable(ci_pytest_os))}")
583
+ if ci_pytest_runs_on is not None:
584
+ args.append(
585
+ f"--ci-pytest-runs-on={','.join(always_iterable(ci_pytest_runs_on))}"
586
+ )
587
+ if ci_pytest_python_version is not None:
588
+ args.append(
589
+ f"--ci-pytest-pythonnversion={','.join(always_iterable(ci_pytest_python_version))}"
590
+ )
591
+ if python_uv_native_tls:
592
+ args.append("--python-uv-native-tls")
593
+ if python_version is not None:
594
+ args.append(f"--python-version={python_version}")
595
+ if repo_name is not None:
596
+ args.append(f"--repo-name={repo_name}")
597
+ _add_hook(
598
+ DYCW_PRE_COMMIT_HOOKS_URL,
599
+ "setup-ci-pull-request",
600
+ path=path,
601
+ modifications=modifications,
602
+ args=args if len(args) >= 1 else None,
603
+ rev=True,
604
+ type_="formatter",
605
+ )
606
+ return len(modifications) == 0
607
+
608
+
609
+ def _add_setup_ci_push(
610
+ *,
611
+ path: PathLike = PRE_COMMIT_CONFIG_YAML,
612
+ gitea: bool = False,
613
+ python_uv_native_tls: bool = False,
614
+ ) -> bool:
615
+ modifications: set[Path] = set()
616
+ args: list[str] = []
617
+ if gitea:
618
+ args.append("--gitea")
619
+ if python_uv_native_tls:
620
+ args.append("--python-uv-native-tls")
621
+ _add_hook(
622
+ DYCW_PRE_COMMIT_HOOKS_URL,
623
+ "setup-ci-push",
624
+ path=path,
625
+ modifications=modifications,
626
+ args=args if len(args) >= 1 else None,
627
+ rev=True,
628
+ type_="formatter",
629
+ )
630
+ return len(modifications) == 0
631
+
632
+
633
+ def _add_setup_direnv(
634
+ *,
635
+ path: PathLike = PRE_COMMIT_CONFIG_YAML,
636
+ python: bool = False,
637
+ python_uv_index: MaybeSequenceStr | None = None,
638
+ python_uv_native_tls: bool = False,
639
+ python_version: str = DEFAULT_PYTHON_VERSION,
640
+ ) -> bool:
641
+ modifications: set[Path] = set()
642
+ args: list[str] = []
643
+ if python:
644
+ args.append("--python")
645
+ if python_uv_index is not None:
646
+ args.append(f"--python-uv-index={','.join(always_iterable(python_uv_index))}")
647
+ if python_uv_native_tls:
648
+ args.append("--python-uv-native-tls")
649
+ if python_version is not None:
650
+ args.append(f"--python-version={python_version}")
651
+ _add_hook(
652
+ DYCW_PRE_COMMIT_HOOKS_URL,
653
+ "setup-direnv",
654
+ path=path,
655
+ modifications=modifications,
656
+ rev=True,
657
+ args=args if len(args) >= 1 else None,
658
+ type_="formatter",
659
+ )
660
+ return len(modifications) == 0
661
+
662
+
663
+ def _add_dockerfmt(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
664
+ modifications: set[Path] = set()
665
+ _add_hook(
666
+ DOCKERFMT_URL,
667
+ "dockerfmt",
668
+ path=path,
669
+ modifications=modifications,
670
+ rev=True,
671
+ args=["--newline", "--write"],
672
+ type_="formatter",
673
+ )
674
+ return len(modifications) == 0
675
+
676
+
677
+ def _add_fish_indent(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
678
+ modifications: set[Path] = set()
679
+ _add_hook(
680
+ LOCAL,
681
+ "fish_indent",
682
+ path=path,
683
+ modifications=modifications,
684
+ name="fish_indent",
685
+ entry="fish_indent",
686
+ language="unsupported",
687
+ files=r"\.fish$",
688
+ args=["--write"],
689
+ type_="formatter",
690
+ )
691
+ return len(modifications) == 0
692
+
693
+
694
+ def _add_stylua(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
695
+ modifications: set[Path] = set()
696
+ _add_hook(
697
+ STYLUA_URL,
698
+ "stlya",
699
+ path=path,
700
+ modifications=modifications,
701
+ rev=True,
702
+ type_="formatter",
703
+ )
704
+ return len(modifications) == 0
705
+
706
+
707
+ def _add_prettier(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
708
+ modifications: set[Path] = set()
709
+ _add_hook(
710
+ LOCAL,
711
+ "prettier",
712
+ path=path,
713
+ modifications=modifications,
714
+ name="prettier",
715
+ entry="npx prettier --write",
716
+ language="unsupported",
717
+ types_or=["markdown", "yaml"],
237
718
  type_="formatter",
238
719
  )
239
720
  return len(modifications) == 0
@@ -243,7 +724,7 @@ def _add_add_future_import_annotations(
243
724
  *, path: PathLike = PRE_COMMIT_CONFIG_YAML
244
725
  ) -> bool:
245
726
  modifications: set[Path] = set()
246
- add_pre_commit_config_repo(
727
+ _add_hook(
247
728
  DYCW_PRE_COMMIT_HOOKS_URL,
248
729
  "add-future-import-annotations",
249
730
  path=path,
@@ -256,7 +737,7 @@ def _add_add_future_import_annotations(
256
737
 
257
738
  def _add_format_requirements(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
258
739
  modifications: set[Path] = set()
259
- add_pre_commit_config_repo(
740
+ _add_hook(
260
741
  DYCW_PRE_COMMIT_HOOKS_URL,
261
742
  "format-requirements",
262
743
  path=path,
@@ -269,7 +750,7 @@ def _add_format_requirements(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool
269
750
 
270
751
  def _add_replace_sequence_str(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
271
752
  modifications: set[Path] = set()
272
- add_pre_commit_config_repo(
753
+ _add_hook(
273
754
  DYCW_PRE_COMMIT_HOOKS_URL,
274
755
  "replace-sequence-str",
275
756
  path=path,
@@ -282,13 +763,13 @@ def _add_replace_sequence_str(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> boo
282
763
 
283
764
  def _add_ruff_check(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
284
765
  modifications: set[Path] = set()
285
- add_pre_commit_config_repo(
766
+ _add_hook(
286
767
  RUFF_URL,
287
768
  "ruff-check",
288
769
  path=path,
289
770
  modifications=modifications,
290
771
  rev=True,
291
- args=("exact", ["--fix"]),
772
+ args=["--fix"],
292
773
  type_="linter",
293
774
  )
294
775
  return len(modifications) == 0
@@ -296,7 +777,7 @@ def _add_ruff_check(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
296
777
 
297
778
  def _add_ruff_format(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
298
779
  modifications: set[Path] = set()
299
- add_pre_commit_config_repo(
780
+ _add_hook(
300
781
  RUFF_URL,
301
782
  "ruff-format",
302
783
  path=path,
@@ -309,7 +790,7 @@ def _add_ruff_format(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
309
790
 
310
791
  def _add_setup_git(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
311
792
  modifications: set[Path] = set()
312
- add_pre_commit_config_repo(
793
+ _add_hook(
313
794
  DYCW_PRE_COMMIT_HOOKS_URL,
314
795
  "setup-git",
315
796
  path=path,
@@ -320,19 +801,50 @@ def _add_setup_git(*, path: PathLike = PRE_COMMIT_CONFIG_YAML) -> bool:
320
801
  return len(modifications) == 0
321
802
 
322
803
 
804
+ def _add_setup_pyproject(
805
+ *,
806
+ path: PathLike = PRE_COMMIT_CONFIG_YAML,
807
+ python_version: str = DEFAULT_PYTHON_VERSION,
808
+ description: str | None = None,
809
+ python_package_name_external: str | None = None,
810
+ python_package_name_internal: str | None = None,
811
+ python_uv_index: MaybeSequenceStr | None = None,
812
+ ) -> bool:
813
+ modifications: set[Path] = set()
814
+ args: list[str] = [f"--python-version={python_version}"]
815
+ if description is not None:
816
+ args.append(f"--description={description}")
817
+ if python_package_name_external is not None:
818
+ args.append(f"--python-package-name-external={python_package_name_external}")
819
+ if python_package_name_internal is not None:
820
+ args.append(f"--python-package-name-internal={python_package_name_internal}")
821
+ if python_uv_index is not None:
822
+ args.append(f"--python-uv-index={','.join(always_iterable(python_uv_index))}")
823
+ _add_hook(
824
+ DYCW_PRE_COMMIT_HOOKS_URL,
825
+ "setup-pyproject",
826
+ path=path,
827
+ modifications=modifications,
828
+ rev=True,
829
+ args=args,
830
+ type_="formatter",
831
+ )
832
+ return len(modifications) == 0
833
+
834
+
323
835
  def _add_setup_pyright(
324
836
  *,
325
837
  path: PathLike = PRE_COMMIT_CONFIG_YAML,
326
838
  python_version: str = DEFAULT_PYTHON_VERSION,
327
839
  ) -> bool:
328
840
  modifications: set[Path] = set()
329
- add_pre_commit_config_repo(
841
+ _add_hook(
330
842
  DYCW_PRE_COMMIT_HOOKS_URL,
331
843
  "setup-pyright",
332
844
  path=path,
333
845
  modifications=modifications,
334
846
  rev=True,
335
- args=("exact", [f"--python-version={python_version}"]),
847
+ args=[f"--python-version={python_version}"],
336
848
  type_="formatter",
337
849
  )
338
850
  return len(modifications) == 0
@@ -344,13 +856,13 @@ def _add_setup_ruff(
344
856
  python_version: str = DEFAULT_PYTHON_VERSION,
345
857
  ) -> bool:
346
858
  modifications: set[Path] = set()
347
- add_pre_commit_config_repo(
859
+ _add_hook(
348
860
  DYCW_PRE_COMMIT_HOOKS_URL,
349
861
  "setup-ruff",
350
862
  path=path,
351
863
  modifications=modifications,
352
864
  rev=True,
353
- args=("exact", [f"--python-version={python_version}"]),
865
+ args=[f"--python-version={python_version}"],
354
866
  type_="formatter",
355
867
  )
356
868
  return len(modifications) == 0
@@ -358,7 +870,7 @@ def _add_setup_ruff(
358
870
 
359
871
  def _add_update_requirements(*, path: PathLike = PYPROJECT_TOML) -> bool:
360
872
  modifications: set[Path] = set()
361
- add_pre_commit_config_repo(
873
+ _add_hook(
362
874
  DYCW_PRE_COMMIT_HOOKS_URL,
363
875
  "update-requirements",
364
876
  path=path,
@@ -369,5 +881,134 @@ def _add_update_requirements(*, path: PathLike = PYPROJECT_TOML) -> bool:
369
881
  return len(modifications) == 0
370
882
 
371
883
 
884
+ def _add_uv_lock(*, path: PathLike = PYPROJECT_TOML) -> bool:
885
+ modifications: set[Path] = set()
886
+ _add_hook(
887
+ UV_URL,
888
+ "uv-lock",
889
+ path=path,
890
+ modifications=modifications,
891
+ rev=True,
892
+ args=["--upgrade", "--resolution", "highest", "--prerelease", "disallow"],
893
+ type_="formatter",
894
+ )
895
+ return len(modifications) == 0
896
+
897
+
898
+ def _add_shellcheck(*, path: PathLike = PYPROJECT_TOML) -> bool:
899
+ modifications: set[Path] = set()
900
+ _add_hook(
901
+ SHELLCHECK_URL,
902
+ "shellcheck",
903
+ path=path,
904
+ modifications=modifications,
905
+ rev=True,
906
+ type_="linter",
907
+ )
908
+ return len(modifications) == 0
909
+
910
+
911
+ def _add_shfmt(*, path: PathLike = PYPROJECT_TOML) -> bool:
912
+ modifications: set[Path] = set()
913
+ _add_hook(
914
+ SHFMT_URL,
915
+ "shfmt",
916
+ path=path,
917
+ modifications=modifications,
918
+ rev=True,
919
+ type_="formatter",
920
+ )
921
+ return len(modifications) == 0
922
+
923
+
924
+ def _add_taplo_format(*, path: PathLike = PYPROJECT_TOML) -> bool:
925
+ modifications: set[Path] = set()
926
+ _add_hook(
927
+ TAPLO_URL,
928
+ "taplo-format",
929
+ path=path,
930
+ modifications=modifications,
931
+ rev=True,
932
+ args=[
933
+ "--option",
934
+ "indent_tables=true",
935
+ "--option",
936
+ "indent_entries=true",
937
+ "--option",
938
+ "reorder_keys=true",
939
+ ],
940
+ type_="linter",
941
+ )
942
+ return len(modifications) == 0
943
+
944
+
945
+ def _add_xmlformatter(*, path: PathLike = PYPROJECT_TOML) -> bool:
946
+ modifications: set[Path] = set()
947
+ _add_hook(
948
+ XMLFORMATTER_URL,
949
+ "xml-formatter",
950
+ path=path,
951
+ modifications=modifications,
952
+ rev=True,
953
+ types=[],
954
+ types_or=["plist", "xml"],
955
+ args=["--eof-newline"],
956
+ type_="formatter",
957
+ )
958
+ return len(modifications) == 0
959
+
960
+
961
+ ##
962
+
963
+
964
+ def _add_hook(
965
+ url: str,
966
+ id_: str,
967
+ /,
968
+ *,
969
+ path: PathLike = PRE_COMMIT_CONFIG_YAML,
970
+ modifications: MutableSet[Path] | None = None,
971
+ rev: bool = False,
972
+ name: str | None = None,
973
+ entry: str | None = None,
974
+ language: str | None = None,
975
+ files: str | None = None,
976
+ types: list[str] | None = None,
977
+ types_or: list[str] | None = None,
978
+ args: list[str] | None = None,
979
+ type_: Literal["formatter", "linter"] | None = None,
980
+ ) -> None:
981
+ with yield_yaml_dict(path, modifications=modifications) as dict_:
982
+ repos = get_set_list_dicts(dict_, "repos")
983
+ repo = ensure_contains_partial_dict(repos, {"repo": url})
984
+ if rev:
985
+ repo.setdefault("rev", "master")
986
+ hooks = get_set_list_dicts(repo, "hooks")
987
+ hook = ensure_contains_partial_dict(hooks, {"id": id_})
988
+ if name is not None:
989
+ hook["name"] = name
990
+ if entry is not None:
991
+ hook["entry"] = entry
992
+ if language is not None:
993
+ hook["language"] = language
994
+ if files is not None:
995
+ hook["files"] = files
996
+ if types is not None:
997
+ hook["types"] = types
998
+ if types_or is not None:
999
+ hook["types_or"] = types_or
1000
+ if args is not None:
1001
+ hook["args"] = args
1002
+ match type_:
1003
+ case "formatter":
1004
+ hook["priority"] = FORMATTER_PRIORITY
1005
+ case "linter":
1006
+ hook["priority"] = LINTER_PRIORITY
1007
+ case None:
1008
+ ...
1009
+ case never:
1010
+ assert_never(never)
1011
+
1012
+
372
1013
  if __name__ == "__main__":
373
1014
  _main()