dycw-pre-commit-hooks 0.12.8__py3-none-any.whl → 0.14.26__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.
- dycw_pre_commit_hooks-0.14.26.dist-info/METADATA +57 -0
- dycw_pre_commit_hooks-0.14.26.dist-info/RECORD +23 -0
- {dycw_pre_commit_hooks-0.12.8.dist-info → dycw_pre_commit_hooks-0.14.26.dist-info}/WHEEL +1 -1
- dycw_pre_commit_hooks-0.14.26.dist-info/entry_points.txt +14 -0
- pre_commit_hooks/__init__.py +1 -1
- pre_commit_hooks/configs/gitignore +243 -0
- pre_commit_hooks/constants.py +149 -0
- pre_commit_hooks/hooks/__init__.py +1 -0
- pre_commit_hooks/hooks/add_future_import_annotations.py +63 -0
- pre_commit_hooks/hooks/add_hooks.py +373 -0
- pre_commit_hooks/hooks/check_versions_consistent.py +42 -0
- pre_commit_hooks/hooks/format_pre_commit_config.py +70 -0
- pre_commit_hooks/hooks/format_requirements.py +56 -0
- pre_commit_hooks/{replace_sequence_str/__init__.py → hooks/replace_sequence_str.py} +25 -19
- pre_commit_hooks/hooks/run_prek_autoupdate.py +56 -0
- pre_commit_hooks/hooks/run_version_bump.py +53 -0
- pre_commit_hooks/hooks/setup_git.py +40 -0
- pre_commit_hooks/hooks/setup_pyright.py +82 -0
- pre_commit_hooks/hooks/setup_ruff.py +129 -0
- pre_commit_hooks/hooks/update_requirements.py +165 -0
- pre_commit_hooks/types.py +18 -0
- pre_commit_hooks/utilities.py +658 -0
- dycw_pre_commit_hooks-0.12.8.dist-info/METADATA +0 -46
- dycw_pre_commit_hooks-0.12.8.dist-info/RECORD +0 -19
- dycw_pre_commit_hooks-0.12.8.dist-info/entry_points.txt +0 -7
- pre_commit_hooks/check_submodules/__init__.py +0 -50
- pre_commit_hooks/check_submodules/__main__.py +0 -6
- pre_commit_hooks/common.py +0 -131
- pre_commit_hooks/format_requirements/__init__.py +0 -107
- pre_commit_hooks/format_requirements/__main__.py +0 -6
- pre_commit_hooks/mirror_files/__init__.py +0 -57
- pre_commit_hooks/mirror_files/__main__.py +0 -6
- pre_commit_hooks/replace_sequence_str/__main__.py +0 -6
- pre_commit_hooks/run_bump_my_version/__init__.py +0 -53
- pre_commit_hooks/run_bump_my_version/__main__.py +0 -6
- pre_commit_hooks/tag_commits/__init__.py +0 -103
- pre_commit_hooks/tag_commits/__main__.py +0 -6
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from collections.abc import Iterator, MutableSet
|
|
5
|
+
from contextlib import contextmanager, suppress
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from functools import partial
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from subprocess import CalledProcessError
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Literal, assert_never, overload
|
|
11
|
+
|
|
12
|
+
import tomlkit
|
|
13
|
+
import yaml
|
|
14
|
+
from libcst import Module, parse_module
|
|
15
|
+
from tomlkit import TOMLDocument, aot, array, document, string, table
|
|
16
|
+
from tomlkit.items import AoT, Array, Table
|
|
17
|
+
from utilities.atomicwrites import writer
|
|
18
|
+
from utilities.concurrent import concurrent_map
|
|
19
|
+
from utilities.functions import ensure_class, ensure_str
|
|
20
|
+
from utilities.iterables import OneEmptyError, one
|
|
21
|
+
from utilities.packaging import Requirement
|
|
22
|
+
from utilities.subprocess import run
|
|
23
|
+
from utilities.types import PathLike, StrDict
|
|
24
|
+
from utilities.typing import is_str_dict
|
|
25
|
+
from utilities.version import Version3, Version3Error
|
|
26
|
+
|
|
27
|
+
from pre_commit_hooks.constants import (
|
|
28
|
+
BUMPVERSION_TOML,
|
|
29
|
+
FORMATTER_PRIORITY,
|
|
30
|
+
LINTER_PRIORITY,
|
|
31
|
+
PATH_CACHE,
|
|
32
|
+
PRE_COMMIT_CONFIG_YAML,
|
|
33
|
+
PYPROJECT_TOML,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from collections.abc import Callable, Iterable, Iterator, MutableSet
|
|
38
|
+
|
|
39
|
+
from utilities.types import PathLike, StrDict
|
|
40
|
+
|
|
41
|
+
from pre_commit_hooks.types import (
|
|
42
|
+
ArrayLike,
|
|
43
|
+
ContainerLike,
|
|
44
|
+
FuncRequirement,
|
|
45
|
+
TransformArray,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def add_pre_commit_config_repo(
|
|
50
|
+
url: str,
|
|
51
|
+
id_: str,
|
|
52
|
+
/,
|
|
53
|
+
*,
|
|
54
|
+
path: PathLike = PRE_COMMIT_CONFIG_YAML,
|
|
55
|
+
modifications: MutableSet[Path] | None = None,
|
|
56
|
+
rev: bool = False,
|
|
57
|
+
name: str | None = None,
|
|
58
|
+
entry: str | None = None,
|
|
59
|
+
language: str | None = None,
|
|
60
|
+
files: str | None = None,
|
|
61
|
+
types_or: list[str] | None = None,
|
|
62
|
+
args: tuple[Literal["add", "exact"], list[str]] | None = None,
|
|
63
|
+
type_: Literal["formatter", "linter"] | None = None,
|
|
64
|
+
) -> None:
|
|
65
|
+
with yield_yaml_dict(path, modifications=modifications) as dict_:
|
|
66
|
+
repos_list = get_set_list_dicts(dict_, "repos")
|
|
67
|
+
repo_dict = ensure_contains_partial_dict(
|
|
68
|
+
repos_list, {"repo": url}, extra={"rev": "master"} if rev else {}
|
|
69
|
+
)
|
|
70
|
+
hooks_list = get_set_list_dicts(repo_dict, "hooks")
|
|
71
|
+
hook_dict = ensure_contains_partial_dict(hooks_list, {"id": id_})
|
|
72
|
+
if name is not None:
|
|
73
|
+
hook_dict["name"] = name
|
|
74
|
+
if entry is not None:
|
|
75
|
+
hook_dict["entry"] = entry
|
|
76
|
+
if language is not None:
|
|
77
|
+
hook_dict["language"] = language
|
|
78
|
+
if files is not None:
|
|
79
|
+
hook_dict["files"] = files
|
|
80
|
+
if types_or is not None:
|
|
81
|
+
hook_dict["types_or"] = types_or
|
|
82
|
+
if args is not None:
|
|
83
|
+
match args:
|
|
84
|
+
case "add", list() as args_i:
|
|
85
|
+
hook_args = get_set_list_strs(hook_dict, "args")
|
|
86
|
+
ensure_contains(hook_args, *args_i)
|
|
87
|
+
case "exact", list() as args_i:
|
|
88
|
+
hook_dict["args"] = args_i
|
|
89
|
+
case never:
|
|
90
|
+
assert_never(never)
|
|
91
|
+
match type_:
|
|
92
|
+
case "formatter":
|
|
93
|
+
hook_dict["priority"] = FORMATTER_PRIORITY
|
|
94
|
+
case "linter":
|
|
95
|
+
hook_dict["priority"] = LINTER_PRIORITY
|
|
96
|
+
case None:
|
|
97
|
+
...
|
|
98
|
+
case never:
|
|
99
|
+
assert_never(never)
|
|
100
|
+
run_prettier(path)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
##
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def are_equal_modulo_new_line(x: str, y: str, /) -> bool:
|
|
107
|
+
return ensure_new_line(x) == ensure_new_line(y)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
##
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@overload
|
|
114
|
+
def ensure_contains(container: AoT, /, *objs: Table) -> None: ...
|
|
115
|
+
@overload
|
|
116
|
+
def ensure_contains(container: list[str], /, *objs: str) -> None: ...
|
|
117
|
+
@overload
|
|
118
|
+
def ensure_contains(container: list[StrDict], /, *objs: StrDict) -> None: ...
|
|
119
|
+
def ensure_contains(container: ArrayLike, /, *objs: Any) -> None:
|
|
120
|
+
for obj in objs:
|
|
121
|
+
if obj not in container:
|
|
122
|
+
container.append(obj)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def ensure_contains_partial_dict(
|
|
126
|
+
container: list[StrDict], partial: StrDict, /, *, extra: StrDict | None = None
|
|
127
|
+
) -> StrDict:
|
|
128
|
+
try:
|
|
129
|
+
return get_partial_dict(container, partial)
|
|
130
|
+
except OneEmptyError:
|
|
131
|
+
dict_ = partial | ({} if extra is None else extra)
|
|
132
|
+
container.append(dict_)
|
|
133
|
+
return dict_
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def ensure_contains_partial_str(list_: Array | list[str], text: str, /) -> str:
|
|
137
|
+
try:
|
|
138
|
+
return get_partial_str(list_, text)
|
|
139
|
+
except OneEmptyError:
|
|
140
|
+
list_.append(text)
|
|
141
|
+
return text
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@overload
|
|
145
|
+
def ensure_not_contains(container: AoT, /, *objs: Table) -> None: ...
|
|
146
|
+
@overload
|
|
147
|
+
def ensure_not_contains(container: list[str], /, *objs: str) -> None: ...
|
|
148
|
+
@overload
|
|
149
|
+
def ensure_not_contains(container: list[StrDict], /, *objs: StrDict) -> None: ...
|
|
150
|
+
def ensure_not_contains(container: ArrayLike, /, *objs: Any) -> None:
|
|
151
|
+
for obj in objs:
|
|
152
|
+
try:
|
|
153
|
+
index = next(i for i, o in enumerate(container) if o == obj)
|
|
154
|
+
except StopIteration:
|
|
155
|
+
pass
|
|
156
|
+
else:
|
|
157
|
+
del container[index]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
##
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def ensure_new_line(text: str, /) -> str:
|
|
164
|
+
return text.strip("\n") + "\n"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
##
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def get_aot(container: ContainerLike, key: str, /) -> AoT:
|
|
171
|
+
return ensure_class(container[key], AoT)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def get_array(container: ContainerLike, key: str, /) -> Array:
|
|
175
|
+
return ensure_class(container[key], Array)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def get_dict(dict_: StrDict, key: str, /) -> StrDict:
|
|
179
|
+
if is_str_dict(value := dict_[key]):
|
|
180
|
+
return value
|
|
181
|
+
raise TypeError(value)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def get_list_dicts(dict_: StrDict, key: str, /) -> list[StrDict]:
|
|
185
|
+
list_ = ensure_class(dict_[key], list)
|
|
186
|
+
for i in list_:
|
|
187
|
+
if not is_str_dict(i):
|
|
188
|
+
raise TypeError(i)
|
|
189
|
+
return list_
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def get_list_strs(dict_: StrDict, key: str, /) -> list[str]:
|
|
193
|
+
list_ = ensure_class(dict_[key], list)
|
|
194
|
+
for i in list_:
|
|
195
|
+
if not isinstance(i, str):
|
|
196
|
+
raise TypeError(i)
|
|
197
|
+
return list_
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_table(container: ContainerLike, key: str, /) -> Table:
|
|
201
|
+
return ensure_class(container[key], Table)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
##
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def get_set_aot(container: ContainerLike, key: str, /) -> AoT:
|
|
208
|
+
try:
|
|
209
|
+
return get_aot(container, key)
|
|
210
|
+
except KeyError:
|
|
211
|
+
value = container[key] = aot()
|
|
212
|
+
return value
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def get_set_array(container: ContainerLike, key: str, /) -> Array:
|
|
216
|
+
try:
|
|
217
|
+
return get_array(container, key)
|
|
218
|
+
except KeyError:
|
|
219
|
+
value = container[key] = array()
|
|
220
|
+
return value
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def get_set_dict(dict_: StrDict, key: str, /) -> StrDict:
|
|
224
|
+
try:
|
|
225
|
+
return get_dict(dict_, key)
|
|
226
|
+
except KeyError:
|
|
227
|
+
value = dict_[key] = {}
|
|
228
|
+
return value
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def get_set_list_dicts(dict_: StrDict, key: str, /) -> list[StrDict]:
|
|
232
|
+
try:
|
|
233
|
+
return get_list_dicts(dict_, key)
|
|
234
|
+
except KeyError:
|
|
235
|
+
value = dict_[key] = []
|
|
236
|
+
return value
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def get_set_list_strs(dict_: StrDict, key: str, /) -> list[str]:
|
|
240
|
+
try:
|
|
241
|
+
return get_list_strs(dict_, key)
|
|
242
|
+
except KeyError:
|
|
243
|
+
value = dict_[key] = []
|
|
244
|
+
return value
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def get_set_table(container: ContainerLike, key: str, /) -> Table:
|
|
248
|
+
try:
|
|
249
|
+
return get_table(container, key)
|
|
250
|
+
except KeyError:
|
|
251
|
+
value = container[key] = table()
|
|
252
|
+
return value
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
##
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def get_partial_dict(iterable: Iterable[StrDict], dict_: StrDict, /) -> StrDict:
|
|
259
|
+
return one(i for i in iterable if _is_partial_dict(dict_, i))
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _is_partial_dict(obj: Any, dict_: StrDict, /) -> bool:
|
|
263
|
+
if not isinstance(obj, dict):
|
|
264
|
+
return False
|
|
265
|
+
results: dict[str, bool] = {}
|
|
266
|
+
for key, obj_value in obj.items():
|
|
267
|
+
try:
|
|
268
|
+
dict_value = dict_[key]
|
|
269
|
+
except KeyError:
|
|
270
|
+
results[key] = False
|
|
271
|
+
else:
|
|
272
|
+
if isinstance(obj_value, dict) and isinstance(dict_value, dict):
|
|
273
|
+
results[key] = _is_partial_dict(obj_value, dict_value)
|
|
274
|
+
else:
|
|
275
|
+
results[key] = obj_value == dict_value
|
|
276
|
+
return all(results.values())
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
##
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_partial_str(iterable: Iterable[Any], text: str, /) -> str:
|
|
283
|
+
return one(i for i in iterable if _is_partial_str(i, text))
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _is_partial_str(obj: Any, text: str, /) -> bool:
|
|
287
|
+
return isinstance(obj, str) and (text in obj)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
##
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def get_pyproject_dependencies(doc: TOMLDocument, /) -> PyProjectDependencies:
|
|
294
|
+
out = PyProjectDependencies()
|
|
295
|
+
try:
|
|
296
|
+
project = get_table(doc, "project")
|
|
297
|
+
except KeyError:
|
|
298
|
+
pass
|
|
299
|
+
else:
|
|
300
|
+
with suppress(KeyError):
|
|
301
|
+
out.dependencies = get_array(project, "dependencies")
|
|
302
|
+
try:
|
|
303
|
+
opt_dependencies = get_table(project, "optional-dependencies")
|
|
304
|
+
except KeyError:
|
|
305
|
+
pass
|
|
306
|
+
else:
|
|
307
|
+
out.opt_dependencies = {}
|
|
308
|
+
for key in opt_dependencies:
|
|
309
|
+
out.opt_dependencies[ensure_str(key)] = get_array(opt_dependencies, key)
|
|
310
|
+
try:
|
|
311
|
+
dep_grps = get_table(doc, "dependency-groups")
|
|
312
|
+
except KeyError:
|
|
313
|
+
pass
|
|
314
|
+
else:
|
|
315
|
+
out.dep_groups = {}
|
|
316
|
+
for key in dep_grps:
|
|
317
|
+
out.dep_groups[ensure_str(key)] = get_array(dep_grps, key)
|
|
318
|
+
return out
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@dataclass(kw_only=True, slots=True)
|
|
322
|
+
class PyProjectDependencies:
|
|
323
|
+
dependencies: Array | None = None
|
|
324
|
+
opt_dependencies: dict[str, Array] | None = None
|
|
325
|
+
dep_groups: dict[str, Array] | None = None
|
|
326
|
+
|
|
327
|
+
def map_array(self, func: TransformArray, /) -> None:
|
|
328
|
+
if (deps := self.dependencies) is not None:
|
|
329
|
+
func(deps)
|
|
330
|
+
if (opt_depedencies := self.opt_dependencies) is not None:
|
|
331
|
+
for deps in opt_depedencies.values():
|
|
332
|
+
func(deps)
|
|
333
|
+
if (dep_grps := self.dep_groups) is not None:
|
|
334
|
+
for deps in dep_grps.values():
|
|
335
|
+
func(deps)
|
|
336
|
+
|
|
337
|
+
def map_requirements(self, func: FuncRequirement, /) -> None:
|
|
338
|
+
if (deps := self.dependencies) is not None:
|
|
339
|
+
self._map_requirements1(deps, func)
|
|
340
|
+
if (opt_depedencies := self.opt_dependencies) is not None:
|
|
341
|
+
for deps in opt_depedencies.values():
|
|
342
|
+
self._map_requirements1(deps, func)
|
|
343
|
+
if (dep_grps := self.dep_groups) is not None:
|
|
344
|
+
for deps in dep_grps.values():
|
|
345
|
+
self._map_requirements1(deps, func)
|
|
346
|
+
|
|
347
|
+
def _map_requirements1(self, array: Array, func: FuncRequirement, /) -> None:
|
|
348
|
+
new: list[str] = []
|
|
349
|
+
for curr_i in array:
|
|
350
|
+
req = Requirement(ensure_str(curr_i))
|
|
351
|
+
new.append(str(func(req)))
|
|
352
|
+
array.clear()
|
|
353
|
+
for new_i in sorted(new):
|
|
354
|
+
array.append(string(new_i))
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
##
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def get_version_from_git_show(*, path: PathLike = BUMPVERSION_TOML) -> Version3:
|
|
361
|
+
text = run("git", "show", f"origin/master:{path}", return_=True)
|
|
362
|
+
return _get_version_from_toml_text(text)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def get_version_from_git_tag() -> Version3:
|
|
366
|
+
text = run("git", "tag", "--points-at", "origin/master", return_=True)
|
|
367
|
+
for line in text.splitlines():
|
|
368
|
+
with suppress(Version3Error):
|
|
369
|
+
return Version3.parse(line)
|
|
370
|
+
msg = "No valid version from 'git tag'"
|
|
371
|
+
raise ValueError(msg)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def get_version_from_path(*, path: PathLike = BUMPVERSION_TOML) -> Version3:
|
|
375
|
+
text = Path(path).read_text()
|
|
376
|
+
return _get_version_from_toml_text(text)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _get_version_from_toml_text(text: str, /) -> Version3:
|
|
380
|
+
doc = tomlkit.parse(text)
|
|
381
|
+
tool = get_table(doc, "tool")
|
|
382
|
+
bumpversion = get_table(tool, "bumpversion")
|
|
383
|
+
return Version3.parse(str(bumpversion["current_version"]))
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
##
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def path_throttle_cache(name: str, /) -> Path:
|
|
390
|
+
cwd_name = Path.cwd().name
|
|
391
|
+
return PATH_CACHE / "throttle" / f"{name}--{cwd_name}"
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
##
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def run_all_maybe_raise(*funcs: Callable[[], bool]) -> None:
|
|
398
|
+
"""Run all of a set of jobs."""
|
|
399
|
+
|
|
400
|
+
results = concurrent_map(_apply, funcs, parallelism="threads")
|
|
401
|
+
if not all(results):
|
|
402
|
+
raise SystemExit(1)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def _apply[T](func: Callable[[], T], /) -> T:
|
|
406
|
+
return func()
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
##
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def run_bump_my_version(
|
|
413
|
+
version: Version3, /, *, path: PathLike = BUMPVERSION_TOML
|
|
414
|
+
) -> None:
|
|
415
|
+
run("bump-my-version", "replace", "--new-version", str(version), str(path))
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
##
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def run_prettier(path: PathLike, /) -> None:
|
|
422
|
+
with suppress(CalledProcessError):
|
|
423
|
+
run("prettier", "-w", str(path))
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def run_taplo(path: PathLike, /) -> None:
|
|
427
|
+
with suppress(CalledProcessError):
|
|
428
|
+
run(
|
|
429
|
+
"taplo",
|
|
430
|
+
"format",
|
|
431
|
+
"--option",
|
|
432
|
+
"indent_tables=true",
|
|
433
|
+
"--option",
|
|
434
|
+
"indent_entries=true",
|
|
435
|
+
"--option",
|
|
436
|
+
"reorder_keys=true",
|
|
437
|
+
str(path),
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
##
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def write_text(
|
|
445
|
+
path: PathLike, text: str, /, *, modifications: MutableSet[Path] | None = None
|
|
446
|
+
) -> None:
|
|
447
|
+
with writer(path, overwrite=True) as temp:
|
|
448
|
+
_ = temp.write_text(ensure_new_line(text))
|
|
449
|
+
if modifications is not None:
|
|
450
|
+
modifications.add(Path(path))
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
##
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
@contextmanager
|
|
457
|
+
def yield_immutable_write_context[T](
|
|
458
|
+
path: PathLike,
|
|
459
|
+
loads: Callable[[str], T],
|
|
460
|
+
get_default: Callable[[], T],
|
|
461
|
+
dumps: Callable[[T], str],
|
|
462
|
+
/,
|
|
463
|
+
*,
|
|
464
|
+
modifications: MutableSet[Path] | None = None,
|
|
465
|
+
) -> Iterator[_WriteContext[T]]:
|
|
466
|
+
try:
|
|
467
|
+
current = Path(path).read_text()
|
|
468
|
+
except FileNotFoundError:
|
|
469
|
+
current = None
|
|
470
|
+
input_ = get_default()
|
|
471
|
+
output = get_default()
|
|
472
|
+
else:
|
|
473
|
+
input_ = loads(current)
|
|
474
|
+
output = loads(current)
|
|
475
|
+
yield (context := _WriteContext(input=input_, output=output))
|
|
476
|
+
if current is None:
|
|
477
|
+
write_text(path, dumps(context.output), modifications=modifications)
|
|
478
|
+
else:
|
|
479
|
+
match context.output, loads(current):
|
|
480
|
+
case Module() as output_module, Module() as current_module:
|
|
481
|
+
if not are_equal_modulo_new_line(
|
|
482
|
+
output_module.code, current_module.code
|
|
483
|
+
):
|
|
484
|
+
write_text(path, dumps(output_module), modifications=modifications)
|
|
485
|
+
case TOMLDocument() as output_doc, TOMLDocument() as current_doc:
|
|
486
|
+
if not (output_doc == current_doc): # noqa: SIM201
|
|
487
|
+
write_text(path, dumps(output_doc), modifications=modifications)
|
|
488
|
+
case str() as output_text, str() as current_text:
|
|
489
|
+
if not are_equal_modulo_new_line(output_text, current_text):
|
|
490
|
+
write_text(path, dumps(output_text), modifications=modifications)
|
|
491
|
+
case output_obj, current_obj:
|
|
492
|
+
if output_obj != current_obj:
|
|
493
|
+
write_text(path, dumps(output_obj), modifications=modifications)
|
|
494
|
+
case never:
|
|
495
|
+
assert_never(never)
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
@dataclass(kw_only=True, slots=True)
|
|
499
|
+
class _WriteContext[T]:
|
|
500
|
+
input: T
|
|
501
|
+
output: T
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
##
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
@contextmanager
|
|
508
|
+
def yield_json_dict(
|
|
509
|
+
path: PathLike, /, *, modifications: MutableSet[Path] | None = None
|
|
510
|
+
) -> Iterator[StrDict]:
|
|
511
|
+
with yield_mutable_write_context(
|
|
512
|
+
path, json.loads, dict, json.dumps, modifications=modifications
|
|
513
|
+
) as dict_:
|
|
514
|
+
yield dict_
|
|
515
|
+
run_prettier(path)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
##
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
@contextmanager
|
|
522
|
+
def yield_pyproject_toml(
|
|
523
|
+
*, modifications: MutableSet[Path] | None = None
|
|
524
|
+
) -> Iterator[TOMLDocument]:
|
|
525
|
+
with yield_toml_doc(PYPROJECT_TOML, modifications=modifications) as doc:
|
|
526
|
+
yield doc
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
##
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
@contextmanager
|
|
533
|
+
def yield_mutable_write_context[T](
|
|
534
|
+
path: PathLike,
|
|
535
|
+
loads: Callable[[str], T],
|
|
536
|
+
get_default: Callable[[], T],
|
|
537
|
+
dumps: Callable[[T], str],
|
|
538
|
+
/,
|
|
539
|
+
*,
|
|
540
|
+
modifications: MutableSet[Path] | None = None,
|
|
541
|
+
) -> Iterator[T]:
|
|
542
|
+
with yield_immutable_write_context(
|
|
543
|
+
path, loads, get_default, dumps, modifications=modifications
|
|
544
|
+
) as context:
|
|
545
|
+
yield context.output
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
##
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
@contextmanager
|
|
552
|
+
def yield_python_file(
|
|
553
|
+
path: PathLike, /, *, modifications: MutableSet[Path] | None = None
|
|
554
|
+
) -> Iterator[_WriteContext[Module]]:
|
|
555
|
+
with yield_immutable_write_context(
|
|
556
|
+
path,
|
|
557
|
+
parse_module,
|
|
558
|
+
lambda: Module(body=[]),
|
|
559
|
+
lambda module: module.code,
|
|
560
|
+
modifications=modifications,
|
|
561
|
+
) as context:
|
|
562
|
+
yield context
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
##
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
@contextmanager
|
|
569
|
+
def yield_text_file(
|
|
570
|
+
path: PathLike, /, *, modifications: MutableSet[Path] | None = None
|
|
571
|
+
) -> Iterator[_WriteContext[str]]:
|
|
572
|
+
with yield_immutable_write_context(
|
|
573
|
+
path, str, lambda: "", str, modifications=modifications
|
|
574
|
+
) as context:
|
|
575
|
+
yield context
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
##
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
@contextmanager
|
|
582
|
+
def yield_toml_doc(
|
|
583
|
+
path: PathLike, /, *, modifications: MutableSet[Path] | None = None
|
|
584
|
+
) -> Iterator[TOMLDocument]:
|
|
585
|
+
with yield_mutable_write_context(
|
|
586
|
+
path, tomlkit.parse, document, tomlkit.dumps, modifications=modifications
|
|
587
|
+
) as doc:
|
|
588
|
+
yield doc
|
|
589
|
+
run_taplo(path)
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
##
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
@contextmanager
|
|
596
|
+
def yield_yaml_dict(
|
|
597
|
+
path: PathLike,
|
|
598
|
+
/,
|
|
599
|
+
*,
|
|
600
|
+
sort_keys: bool = True,
|
|
601
|
+
modifications: MutableSet[Path] | None = None,
|
|
602
|
+
) -> Iterator[StrDict]:
|
|
603
|
+
with yield_mutable_write_context(
|
|
604
|
+
path,
|
|
605
|
+
yaml.safe_load,
|
|
606
|
+
dict,
|
|
607
|
+
partial(yaml.safe_dump, sort_keys=sort_keys),
|
|
608
|
+
modifications=modifications,
|
|
609
|
+
) as dict_:
|
|
610
|
+
yield dict_
|
|
611
|
+
run_prettier(path)
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
##
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
__all__ = [
|
|
618
|
+
"PyProjectDependencies",
|
|
619
|
+
"add_pre_commit_config_repo",
|
|
620
|
+
"are_equal_modulo_new_line",
|
|
621
|
+
"ensure_contains",
|
|
622
|
+
"ensure_contains_partial_dict",
|
|
623
|
+
"ensure_contains_partial_str",
|
|
624
|
+
"ensure_new_line",
|
|
625
|
+
"ensure_not_contains",
|
|
626
|
+
"get_aot",
|
|
627
|
+
"get_array",
|
|
628
|
+
"get_dict",
|
|
629
|
+
"get_list_dicts",
|
|
630
|
+
"get_list_strs",
|
|
631
|
+
"get_partial_dict",
|
|
632
|
+
"get_partial_str",
|
|
633
|
+
"get_pyproject_dependencies",
|
|
634
|
+
"get_set_aot",
|
|
635
|
+
"get_set_array",
|
|
636
|
+
"get_set_dict",
|
|
637
|
+
"get_set_list_dicts",
|
|
638
|
+
"get_set_list_strs",
|
|
639
|
+
"get_set_table",
|
|
640
|
+
"get_table",
|
|
641
|
+
"get_version_from_git_show",
|
|
642
|
+
"get_version_from_git_tag",
|
|
643
|
+
"get_version_from_path",
|
|
644
|
+
"path_throttle_cache",
|
|
645
|
+
"run_all_maybe_raise",
|
|
646
|
+
"run_bump_my_version",
|
|
647
|
+
"run_prettier",
|
|
648
|
+
"run_taplo",
|
|
649
|
+
"write_text",
|
|
650
|
+
"yield_immutable_write_context",
|
|
651
|
+
"yield_json_dict",
|
|
652
|
+
"yield_mutable_write_context",
|
|
653
|
+
"yield_pyproject_toml",
|
|
654
|
+
"yield_python_file",
|
|
655
|
+
"yield_text_file",
|
|
656
|
+
"yield_toml_doc",
|
|
657
|
+
"yield_yaml_dict",
|
|
658
|
+
]
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: dycw-pre-commit-hooks
|
|
3
|
-
Version: 0.12.8
|
|
4
|
-
Author-email: Derek Wan <d.wan@icloud.com>
|
|
5
|
-
Requires-Python: >=3.12
|
|
6
|
-
Requires-Dist: click<8.3,>=8.2.1
|
|
7
|
-
Requires-Dist: dycw-utilities<0.167,>=0.166.5
|
|
8
|
-
Requires-Dist: gitpython<3.2,>=3.1.45
|
|
9
|
-
Requires-Dist: libcst<1.9,>=1.8.2
|
|
10
|
-
Requires-Dist: loguru<0.8,>=0.7.3
|
|
11
|
-
Requires-Dist: more-itertools<10.8,>=10.7.0
|
|
12
|
-
Requires-Dist: orjson<3.12,>=3.11.3
|
|
13
|
-
Requires-Dist: packaging<25.1,>=25.0
|
|
14
|
-
Requires-Dist: tomlkit<0.14,>=0.13.2
|
|
15
|
-
Requires-Dist: xdg-base-dirs<6.1,>=6.0.2
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
|
|
18
|
-
# pre-commit-hooks
|
|
19
|
-
|
|
20
|
-
## Overview
|
|
21
|
-
|
|
22
|
-
My [`pre-commit`](https://pre-commit.com/) hooks.
|
|
23
|
-
|
|
24
|
-
## Installation
|
|
25
|
-
|
|
26
|
-
1. Install `pre-commit`.
|
|
27
|
-
|
|
28
|
-
1. Add the following to your `.pre-commit-config.yaml`:
|
|
29
|
-
|
|
30
|
-
```yaml
|
|
31
|
-
repos:
|
|
32
|
-
- repo: https://github.com/dycw/pre-commit-hooks
|
|
33
|
-
rev: master
|
|
34
|
-
hooks:
|
|
35
|
-
- id: check-submodules
|
|
36
|
-
- id: format-requirements
|
|
37
|
-
- id: replace-sequence-str
|
|
38
|
-
- id: run-bump-my-version
|
|
39
|
-
- id: tag-commits
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
1. Update your `.pre-commit-config.yaml`:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
pre-commit autoupdate
|
|
46
|
-
```
|