dycw-actions 0.6.4__py3-none-any.whl → 0.7.7__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.
Files changed (73) hide show
  1. actions/__init__.py +1 -1
  2. actions/action_dicts/lib.py +8 -8
  3. actions/clean_dir/cli.py +0 -12
  4. actions/clean_dir/constants.py +7 -0
  5. actions/clean_dir/lib.py +1 -0
  6. actions/cli.py +90 -29
  7. actions/constants.py +5 -1
  8. actions/pre_commit/click.py +15 -0
  9. actions/{conformalize_repo → pre_commit/conformalize_repo}/cli.py +2 -14
  10. actions/{conformalize_repo → pre_commit/conformalize_repo}/constants.py +8 -2
  11. actions/{conformalize_repo → pre_commit/conformalize_repo}/lib.py +97 -313
  12. actions/{conformalize_repo → pre_commit/conformalize_repo}/settings.py +1 -1
  13. actions/pre_commit/constants.py +8 -0
  14. actions/pre_commit/format_requirements/cli.py +24 -0
  15. actions/pre_commit/format_requirements/constants.py +7 -0
  16. actions/pre_commit/format_requirements/lib.py +52 -0
  17. actions/pre_commit/replace_sequence_strs/__init__.py +1 -0
  18. actions/pre_commit/replace_sequence_strs/cli.py +24 -0
  19. actions/pre_commit/replace_sequence_strs/constants.py +7 -0
  20. actions/{replace_sequence_strs → pre_commit/replace_sequence_strs}/lib.py +16 -22
  21. actions/pre_commit/touch_empty_py/__init__.py +1 -0
  22. actions/pre_commit/touch_empty_py/cli.py +24 -0
  23. actions/pre_commit/touch_empty_py/constants.py +7 -0
  24. actions/pre_commit/touch_empty_py/lib.py +62 -0
  25. actions/pre_commit/touch_py_typed/__init__.py +1 -0
  26. actions/pre_commit/touch_py_typed/cli.py +24 -0
  27. actions/pre_commit/touch_py_typed/constants.py +7 -0
  28. actions/pre_commit/touch_py_typed/lib.py +72 -0
  29. actions/pre_commit/update_requirements/__init__.py +1 -0
  30. actions/pre_commit/update_requirements/classes.py +130 -0
  31. actions/pre_commit/update_requirements/cli.py +24 -0
  32. actions/pre_commit/update_requirements/constants.py +7 -0
  33. actions/pre_commit/update_requirements/lib.py +140 -0
  34. actions/pre_commit/utilities.py +386 -0
  35. actions/publish_package/cli.py +7 -19
  36. actions/publish_package/constants.py +7 -0
  37. actions/publish_package/lib.py +3 -3
  38. actions/py.typed +0 -0
  39. actions/random_sleep/cli.py +6 -15
  40. actions/random_sleep/constants.py +7 -0
  41. actions/register_gitea_runner/__init__.py +1 -0
  42. actions/register_gitea_runner/cli.py +32 -0
  43. actions/register_gitea_runner/configs/config.yml +110 -0
  44. actions/register_gitea_runner/configs/entrypoint.sh +23 -0
  45. actions/register_gitea_runner/constants.py +23 -0
  46. actions/register_gitea_runner/lib.py +289 -0
  47. actions/register_gitea_runner/settings.py +33 -0
  48. actions/run_hooks/cli.py +3 -15
  49. actions/run_hooks/constants.py +7 -0
  50. actions/run_hooks/lib.py +2 -2
  51. actions/setup_cronjob/cli.py +0 -12
  52. actions/setup_cronjob/constants.py +5 -1
  53. actions/tag_commit/cli.py +7 -19
  54. actions/tag_commit/constants.py +7 -0
  55. actions/tag_commit/lib.py +8 -8
  56. actions/types.py +4 -1
  57. actions/utilities.py +68 -14
  58. {dycw_actions-0.6.4.dist-info → dycw_actions-0.7.7.dist-info}/METADATA +5 -3
  59. dycw_actions-0.7.7.dist-info/RECORD +84 -0
  60. actions/format_requirements/cli.py +0 -37
  61. actions/format_requirements/lib.py +0 -121
  62. actions/publish_package/doc.py +0 -6
  63. actions/random_sleep/doc.py +0 -6
  64. actions/replace_sequence_strs/cli.py +0 -37
  65. actions/run_hooks/doc.py +0 -6
  66. actions/tag_commit/doc.py +0 -6
  67. dycw_actions-0.6.4.dist-info/RECORD +0 -56
  68. /actions/{conformalize_repo → pre_commit}/__init__.py +0 -0
  69. /actions/{format_requirements → pre_commit/conformalize_repo}/__init__.py +0 -0
  70. /actions/{conformalize_repo → pre_commit/conformalize_repo}/configs/gitignore +0 -0
  71. /actions/{replace_sequence_strs → pre_commit/format_requirements}/__init__.py +0 -0
  72. {dycw_actions-0.6.4.dist-info → dycw_actions-0.7.7.dist-info}/WHEEL +0 -0
  73. {dycw_actions-0.6.4.dist-info → dycw_actions-0.7.7.dist-info}/entry_points.txt +0 -0
@@ -1,36 +1,27 @@
1
1
  from __future__ import annotations
2
2
 
3
- import json
4
3
  import sys
5
4
  from contextlib import contextmanager, suppress
6
- from io import StringIO
5
+ from hashlib import blake2b
7
6
  from itertools import product
8
7
  from pathlib import Path
9
- from re import MULTILINE, escape, sub
8
+ from re import MULTILINE, escape, search, sub
10
9
  from shlex import join
11
10
  from string import Template
12
11
  from subprocess import CalledProcessError
13
- from typing import TYPE_CHECKING, Any, Literal, assert_never
12
+ from typing import TYPE_CHECKING, Literal, assert_never
14
13
 
15
14
  import tomlkit
16
- from rich.pretty import pretty_repr
17
15
  from ruamel.yaml.scalarstring import LiteralScalarString
18
- from tomlkit import TOMLDocument, aot, array, document, table
16
+ from tomlkit import TOMLDocument, table
19
17
  from tomlkit.exceptions import NonExistentKey
20
- from tomlkit.items import AoT, Array, Table
21
- from utilities.atomicwrites import writer
22
- from utilities.functions import ensure_class
23
18
  from utilities.inflect import counted_noun
24
- from utilities.iterables import OneEmptyError, OneNonUniqueError, one
25
- from utilities.pathlib import get_repo_root
26
19
  from utilities.re import extract_groups
27
- from utilities.subprocess import append_text, ripgrep, run
28
- from utilities.tempfile import TemporaryFile
20
+ from utilities.subprocess import ripgrep
29
21
  from utilities.text import repr_str, strip_and_dedent
22
+ from utilities.throttle import throttle
30
23
  from utilities.version import ParseVersionError, Version, parse_version
31
- from utilities.whenever import HOUR, get_now
32
- from whenever import ZonedDateTime
33
- from xdg_base_dirs import xdg_cache_home
24
+ from utilities.whenever import HOUR
34
25
 
35
26
  from actions import __version__
36
27
  from actions.action_dicts.lib import (
@@ -41,9 +32,12 @@ from actions.action_dicts.lib import (
41
32
  run_action_ruff_dict,
42
33
  run_action_tag_dict,
43
34
  )
44
- from actions.conformalize_repo.constants import (
35
+ from actions.constants import PATH_THROTTLE_CACHE, YAML_INSTANCE
36
+ from actions.logging import LOGGER
37
+ from actions.pre_commit.conformalize_repo.constants import (
45
38
  ACTIONS_URL,
46
39
  BUMPVERSION_TOML,
40
+ CONFORMALIZE_REPO_SUB_CMD,
47
41
  COVERAGERC_TOML,
48
42
  DOCKERFMT_URL,
49
43
  ENVRC,
@@ -65,16 +59,39 @@ from actions.conformalize_repo.constants import (
65
59
  TAPLO_URL,
66
60
  UV_URL,
67
61
  )
68
- from actions.conformalize_repo.settings import SETTINGS
69
- from actions.constants import YAML_INSTANCE
70
- from actions.logging import LOGGER
62
+ from actions.pre_commit.conformalize_repo.settings import SETTINGS
63
+ from actions.pre_commit.format_requirements.constants import FORMAT_REQUIREMENTS_SUB_CMD
64
+ from actions.pre_commit.replace_sequence_strs.constants import (
65
+ REPLACE_SEQUENCE_STRS_SUB_CMD,
66
+ )
67
+ from actions.pre_commit.touch_empty_py.constants import TOUCH_EMPTY_PY_SUB_CMD
68
+ from actions.pre_commit.touch_py_typed.constants import TOUCH_PY_TYPED_SUB_CMD
69
+ from actions.pre_commit.update_requirements.constants import UPDATE_REQUIREMENTS_SUB_CMD
70
+ from actions.pre_commit.utilities import (
71
+ ensure_aot_contains,
72
+ ensure_contains,
73
+ ensure_contains_partial_dict,
74
+ ensure_contains_partial_str,
75
+ ensure_not_contains,
76
+ get_aot,
77
+ get_array,
78
+ get_dict,
79
+ get_list,
80
+ get_table,
81
+ yield_json_dict,
82
+ yield_text_file,
83
+ yield_toml_doc,
84
+ yield_yaml_dict,
85
+ )
86
+ from actions.utilities import logged_run
71
87
 
72
88
  if TYPE_CHECKING:
73
- from collections.abc import Callable, Iterable, Iterator, MutableSet
89
+ from collections.abc import Iterator, MutableSet
74
90
 
91
+ from tomlkit.items import Table
75
92
  from utilities.types import PathLike
76
93
 
77
- from actions.types import HasAppend, HasSetDefault, StrDict
94
+ from actions.types import StrDict
78
95
 
79
96
 
80
97
  def conformalize_repo(
@@ -267,6 +284,7 @@ def conformalize_repo(
267
284
  pytest__ubuntu=github__pull_request__pytest__ubuntu,
268
285
  pytest__timeout=pytest__timeout,
269
286
  python_version=python_version,
287
+ repo_name=repo_name,
270
288
  ruff=ruff,
271
289
  script=script,
272
290
  )
@@ -411,31 +429,27 @@ def add_envrc(
411
429
  python_version: str = SETTINGS.python_version,
412
430
  script: str | None = SETTINGS.script,
413
431
  ) -> None:
414
- with yield_text_file(ENVRC, modifications=modifications) as temp:
432
+ with yield_text_file(ENVRC, modifications=modifications) as context:
415
433
  shebang = strip_and_dedent("""
416
434
  #!/usr/bin/env sh
417
435
  # shellcheck source=/dev/null
418
436
  """)
419
- append_text(temp, shebang, skip_if_present=True, flags=MULTILINE, blank_lines=2)
437
+ if search(escape(shebang), context.output, flags=MULTILINE) is None:
438
+ context.output += f"\n\n{shebang}"
420
439
 
421
440
  echo = strip_and_dedent("""
422
441
  # echo
423
442
  echo_date() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" >&2; }
424
443
  """)
425
- append_text(temp, echo, skip_if_present=True, flags=MULTILINE, blank_lines=2)
444
+ if search(escape(echo), context.output, flags=MULTILINE) is None:
445
+ context.output += f"\n\n{echo}"
426
446
 
427
447
  if uv:
428
- append_text(
429
- temp,
430
- _add_envrc_uv_text(
431
- native_tls=uv__native_tls,
432
- python_version=python_version,
433
- script=script,
434
- ),
435
- skip_if_present=True,
436
- flags=MULTILINE,
437
- blank_lines=2,
448
+ uv_text = _add_envrc_uv_text(
449
+ native_tls=uv__native_tls, python_version=python_version, script=script
438
450
  )
451
+ if search(escape(uv_text), context.output, flags=MULTILINE) is None:
452
+ context.output += f"\n\n{echo}"
439
453
 
440
454
 
441
455
  def _add_envrc_uv_text(
@@ -491,6 +505,7 @@ def add_github_pull_request_yaml(
491
505
  pytest__windows: bool = SETTINGS.github__pull_request__pytest__windows,
492
506
  pytest__timeout: int | None = SETTINGS.pytest__timeout,
493
507
  python_version: str = SETTINGS.python_version,
508
+ repo_name: str | None = SETTINGS.repo_name,
494
509
  ruff: bool = SETTINGS.github__pull_request__ruff,
495
510
  script: str | None = SETTINGS.script,
496
511
  ) -> None:
@@ -503,7 +518,7 @@ def add_github_pull_request_yaml(
503
518
  branches = get_list(pull_request, "branches")
504
519
  ensure_contains(branches, "master")
505
520
  schedule = get_list(on, "schedule")
506
- ensure_contains(schedule, {"cron": "0 0 * * *"})
521
+ ensure_contains(schedule, {"cron": get_cron_job(repo_name=repo_name)})
507
522
  jobs = get_dict(dict_, "jobs")
508
523
  if pre_commit:
509
524
  pre_commit_dict = get_dict(jobs, "pre-commit")
@@ -615,9 +630,10 @@ def add_github_push_yaml(
615
630
 
616
631
 
617
632
  def add_gitignore(*, modifications: MutableSet[Path] | None = None) -> None:
618
- with yield_text_file(GITIGNORE, modifications=modifications) as temp:
633
+ with yield_text_file(GITIGNORE, modifications=modifications) as context:
619
634
  text = (PATH_CONFIGS / "gitignore").read_text()
620
- append_text(temp, text, skip_if_present=True, flags=MULTILINE)
635
+ if search(escape(text), context.output, flags=MULTILINE) is None:
636
+ context.output += f"\n\n{text}"
621
637
 
622
638
 
623
639
  ##
@@ -636,7 +652,7 @@ def add_pre_commit_config_yaml(
636
652
  script: str | None = SETTINGS.script,
637
653
  ) -> None:
638
654
  with yield_yaml_dict(PRE_COMMIT_CONFIG_YAML, modifications=modifications) as dict_:
639
- _add_pre_commit_config_repo(dict_, ACTIONS_URL, "conformalize-repo")
655
+ _add_pre_commit_config_repo(dict_, ACTIONS_URL, CONFORMALIZE_REPO_SUB_CMD)
640
656
  _add_pre_commit_config_repo(
641
657
  dict_, PRE_COMMIT_HOOKS_URL, "check-executables-have-shebangs"
642
658
  )
@@ -675,8 +691,13 @@ def add_pre_commit_config_yaml(
675
691
  types_or=["markdown", "yaml"],
676
692
  )
677
693
  if python:
678
- _add_pre_commit_config_repo(dict_, ACTIONS_URL, "format-requirements")
679
- _add_pre_commit_config_repo(dict_, ACTIONS_URL, "replace-sequence-strs")
694
+ _add_pre_commit_config_repo(dict_, ACTIONS_URL, FORMAT_REQUIREMENTS_SUB_CMD)
695
+ _add_pre_commit_config_repo(
696
+ dict_, ACTIONS_URL, REPLACE_SEQUENCE_STRS_SUB_CMD
697
+ )
698
+ _add_pre_commit_config_repo(dict_, ACTIONS_URL, TOUCH_EMPTY_PY_SUB_CMD)
699
+ _add_pre_commit_config_repo(dict_, ACTIONS_URL, TOUCH_PY_TYPED_SUB_CMD)
700
+ _add_pre_commit_config_repo(dict_, ACTIONS_URL, UPDATE_REQUIREMENTS_SUB_CMD)
680
701
  if ruff:
681
702
  _add_pre_commit_config_repo(
682
703
  dict_, RUFF_URL, "ruff-check", args=("add", ["--fix"])
@@ -730,11 +751,11 @@ def _add_pre_commit_config_repo(
730
751
  args: tuple[Literal["add", "exact"], list[str]] | None = None,
731
752
  ) -> None:
732
753
  repos_list = get_list(pre_commit_dict, "repos")
733
- repo_dict = ensure_contains_partial(
754
+ repo_dict = ensure_contains_partial_dict(
734
755
  repos_list, {"repo": url}, extra={} if url == "local" else {"rev": "master"}
735
756
  )
736
757
  hooks_list = get_list(repo_dict, "hooks")
737
- hook_dict = ensure_contains_partial(hooks_list, {"id": id_})
758
+ hook_dict = ensure_contains_partial_dict(hooks_list, {"id": id_})
738
759
  if name is not None:
739
760
  hook_dict["name"] = name
740
761
  if entry is not None:
@@ -785,12 +806,13 @@ def add_pyproject_toml(
785
806
  project.setdefault("version", "0.1.0")
786
807
  dependency_groups = get_table(doc, "dependency-groups")
787
808
  dev = get_array(dependency_groups, "dev")
788
- ensure_contains(dev, "dycw-utilities[test]")
789
- ensure_contains(dev, "rich")
809
+ _ = ensure_contains_partial_str(dev, "dycw-utilities[test]")
810
+ _ = ensure_contains_partial_str(dev, "pyright")
811
+ _ = ensure_contains_partial_str(dev, "rich")
790
812
  if optional_dependencies__scripts:
791
813
  optional_dependencies = get_table(project, "optional-dependencies")
792
814
  scripts = get_array(optional_dependencies, "scripts")
793
- ensure_contains(scripts, "click >=8.3.1")
815
+ _ = ensure_contains_partial_str(scripts, "click")
794
816
  if python_package_name is not None:
795
817
  tool = get_table(doc, "tool")
796
818
  uv = get_table(tool, "uv")
@@ -921,13 +943,15 @@ def add_readme_md(
921
943
  name: str | None = SETTINGS.package_name,
922
944
  description: str | None = SETTINGS.description,
923
945
  ) -> None:
924
- with yield_text_file(README_MD, modifications=modifications) as temp:
946
+ with yield_text_file(README_MD, modifications=modifications) as context:
925
947
  lines: list[str] = []
926
948
  if name is not None:
927
949
  lines.append(f"# `{name}`")
928
950
  if description is not None:
929
951
  lines.append(description)
930
- _ = temp.write_text("\n\n".join(lines))
952
+ text = "\n\n".join(lines)
953
+ if search(escape(text), context.output, flags=MULTILINE) is None:
954
+ context.output += f"\n\n{text}"
931
955
 
932
956
 
933
957
  ##
@@ -1029,107 +1053,15 @@ def check_versions() -> None:
1029
1053
  ##
1030
1054
 
1031
1055
 
1032
- def ensure_aot_contains(array: AoT, /, *tables: Table) -> None:
1033
- for table_ in tables:
1034
- if table_ not in array:
1035
- array.append(table_)
1036
-
1037
-
1038
- def ensure_contains(array: HasAppend, /, *objs: Any) -> None:
1039
- if isinstance(array, AoT):
1040
- msg = f"Use {ensure_aot_contains.__name__!r} instead of {ensure_contains.__name__!r}"
1041
- raise TypeError(msg)
1042
- for obj in objs:
1043
- if obj not in array:
1044
- array.append(obj)
1045
-
1046
-
1047
- def ensure_contains_partial(
1048
- container: HasAppend, partial: StrDict, /, *, extra: StrDict | None = None
1049
- ) -> StrDict:
1050
- try:
1051
- return get_partial_dict(container, partial, skip_log=True)
1052
- except OneEmptyError:
1053
- dict_ = partial | ({} if extra is None else extra)
1054
- container.append(dict_)
1055
- return dict_
1056
-
1057
-
1058
- def ensure_not_contains(array: Array, /, *objs: Any) -> None:
1059
- for obj in objs:
1060
- try:
1061
- index = next(i for i, o in enumerate(array) if o == obj)
1062
- except StopIteration:
1063
- pass
1064
- else:
1065
- del array[index]
1066
-
1067
-
1068
- ##
1069
-
1070
-
1071
- def get_aot(container: HasSetDefault, key: str, /) -> AoT:
1072
- return ensure_class(container.setdefault(key, aot()), AoT)
1073
-
1074
-
1075
- def get_array(container: HasSetDefault, key: str, /) -> Array:
1076
- return ensure_class(container.setdefault(key, array()), Array)
1077
-
1078
-
1079
- def get_dict(container: HasSetDefault, key: str, /) -> StrDict:
1080
- return ensure_class(container.setdefault(key, {}), dict)
1081
-
1082
-
1083
- def get_list(container: HasSetDefault, key: str, /) -> list[Any]:
1084
- return ensure_class(container.setdefault(key, []), list)
1085
-
1086
-
1087
- def get_table(container: HasSetDefault, key: str, /) -> Table:
1088
- return ensure_class(container.setdefault(key, table()), Table)
1089
-
1090
-
1091
- ##
1092
-
1093
-
1094
- def get_partial_dict(
1095
- iterable: Iterable[Any], dict_: StrDict, /, *, skip_log: bool = False
1096
- ) -> StrDict:
1097
- try:
1098
- return one(i for i in iterable if is_partial_dict(dict_, i))
1099
- except OneEmptyError:
1100
- if not skip_log:
1101
- LOGGER.exception(
1102
- "Expected %s to contain %s (as a partial)",
1103
- pretty_repr(iterable),
1104
- pretty_repr(dict_),
1105
- )
1106
- raise
1107
- except OneNonUniqueError as error:
1108
- LOGGER.exception(
1109
- "Expected %s to contain %s uniquely (as a partial); got %s, %s and perhaps more",
1110
- pretty_repr(iterable),
1111
- pretty_repr(dict_),
1112
- pretty_repr(error.first),
1113
- pretty_repr(error.second),
1114
- )
1115
- raise
1116
-
1117
-
1118
- def is_partial_dict(obj: Any, dict_: StrDict, /) -> bool:
1119
- if not isinstance(obj, dict):
1120
- return False
1121
- results: dict[str, bool] = {}
1122
- for key, obj_value in obj.items():
1123
- try:
1124
- dict_value = dict_[key]
1125
- except KeyError:
1126
- results[key] = False
1127
- else:
1128
- if isinstance(obj_value, dict) and isinstance(dict_value, dict):
1129
- results[key] = is_partial_dict(obj_value, dict_value)
1130
- else:
1131
- results[key] = obj_value == dict_value
1132
- return all(results.values())
1056
+ def get_cron_job(*, repo_name: str | None = SETTINGS.repo_name) -> str:
1057
+ if repo_name is None:
1058
+ hour = minute = 0
1059
+ else:
1060
+ digest = blake2b(repo_name.encode(), digest_size=8).digest()
1061
+ value = int.from_bytes(digest, "big")
1062
+ minute = value % 60
1063
+ hour = (value // 60) % 24
1064
+ return f"{hour} {minute} * * *"
1133
1065
 
1134
1066
 
1135
1067
  ##
@@ -1168,12 +1100,12 @@ def get_version_from_bumpversion_toml(
1168
1100
 
1169
1101
 
1170
1102
  def get_version_from_git_show() -> Version:
1171
- text = run("git", "show", f"origin/master:{BUMPVERSION_TOML}", return_=True)
1103
+ text = logged_run("git", "show", f"origin/master:{BUMPVERSION_TOML}", return_=True)
1172
1104
  return get_version_from_bumpversion_toml(obj=text.rstrip("\n"))
1173
1105
 
1174
1106
 
1175
1107
  def get_version_from_git_tag() -> Version:
1176
- text = run("git", "tag", "--points-at", "origin/master", return_=True)
1108
+ text = logged_run("git", "tag", "--points-at", "origin/master", return_=True)
1177
1109
  for line in text.splitlines():
1178
1110
  with suppress(ParseVersionError):
1179
1111
  return parse_version(line)
@@ -1208,27 +1140,16 @@ def run_bump_my_version(*, modifications: MutableSet[Path] | None = None) -> Non
1208
1140
  ##
1209
1141
 
1210
1142
 
1211
- def run_pre_commit_update(*, modifications: MutableSet[Path] | None = None) -> None:
1212
- cache = xdg_cache_home() / "conformalize" / get_repo_root().name
1143
+ def _run_pre_commit_update(*, modifications: MutableSet[Path] | None = None) -> None:
1144
+ current = PRE_COMMIT_CONFIG_YAML.read_text()
1145
+ logged_run("pre-commit", "autoupdate", print=True)
1146
+ if (modifications is not None) and (PRE_COMMIT_CONFIG_YAML.read_text() != current):
1147
+ modifications.add(PRE_COMMIT_CONFIG_YAML)
1213
1148
 
1214
- def run_autoupdate() -> None:
1215
- current = PRE_COMMIT_CONFIG_YAML.read_text()
1216
- run("pre-commit", "autoupdate", print=True)
1217
- with writer(cache, overwrite=True) as temp:
1218
- _ = temp.write_text(get_now().format_iso())
1219
- if (modifications is not None) and (
1220
- PRE_COMMIT_CONFIG_YAML.read_text() != current
1221
- ):
1222
- modifications.add(PRE_COMMIT_CONFIG_YAML)
1223
1149
 
1224
- try:
1225
- text = cache.read_text()
1226
- except FileNotFoundError:
1227
- run_autoupdate()
1228
- else:
1229
- prev = ZonedDateTime.parse_iso(text.rstrip("\n"))
1230
- if prev < (get_now() - 12 * HOUR):
1231
- run_autoupdate()
1150
+ run_pre_commit_update = throttle(
1151
+ delta=12 * HOUR, path=PATH_THROTTLE_CACHE / _run_pre_commit_update.__name__
1152
+ )(_run_pre_commit_update)
1232
1153
 
1233
1154
 
1234
1155
  ##
@@ -1247,22 +1168,21 @@ def run_ripgrep_and_replace(
1247
1168
  )
1248
1169
  if result is None:
1249
1170
  return
1250
- for path in map(Path, result.splitlines()):
1251
- with yield_text_file(path, modifications=modifications) as temp:
1252
- text = sub(
1171
+ for path in result.splitlines():
1172
+ with yield_text_file(path, modifications=modifications) as context:
1173
+ context.output = sub(
1253
1174
  r'# requires-python = ">=\d+\.\d+"',
1254
1175
  rf'# requires-python = ">={version}"',
1255
- path.read_text(),
1176
+ context.input,
1256
1177
  flags=MULTILINE,
1257
1178
  )
1258
- _ = temp.write_text(text)
1259
1179
 
1260
1180
 
1261
1181
  ##
1262
1182
 
1263
1183
 
1264
1184
  def set_version(version: Version, /) -> None:
1265
- run(
1185
+ logged_run(
1266
1186
  "bump-my-version",
1267
1187
  "replace",
1268
1188
  "--new-version",
@@ -1318,35 +1238,6 @@ def update_action_versions(*, modifications: MutableSet[Path] | None = None) ->
1318
1238
  ##
1319
1239
 
1320
1240
 
1321
- def write_text(
1322
- verb: str,
1323
- src: PathLike,
1324
- dest: PathLike,
1325
- /,
1326
- *,
1327
- modifications: MutableSet[Path] | None = None,
1328
- ) -> None:
1329
- src, dest = map(Path, [src, dest])
1330
- LOGGER.info("%s '%s'...", verb, dest)
1331
- text = src.read_text().rstrip("\n") + "\n"
1332
- with writer(dest, overwrite=True) as temp:
1333
- _ = temp.write_text(text)
1334
- if modifications is not None:
1335
- modifications.add(dest)
1336
-
1337
-
1338
- ##
1339
-
1340
-
1341
- def yaml_dump(obj: Any, /) -> str:
1342
- stream = StringIO()
1343
- YAML_INSTANCE.dump(obj, stream)
1344
- return stream.getvalue()
1345
-
1346
-
1347
- ##
1348
-
1349
-
1350
1241
  @contextmanager
1351
1242
  def yield_bumpversion_toml(
1352
1243
  *, modifications: MutableSet[Path] | None = None
@@ -1362,19 +1253,6 @@ def yield_bumpversion_toml(
1362
1253
  ##
1363
1254
 
1364
1255
 
1365
- @contextmanager
1366
- def yield_json_dict(
1367
- path: PathLike, /, *, modifications: MutableSet[Path] | None = None
1368
- ) -> Iterator[StrDict]:
1369
- with yield_write_context(
1370
- path, json.loads, dict, json.dumps, modifications=modifications
1371
- ) as dict_:
1372
- yield dict_
1373
-
1374
-
1375
- ##
1376
-
1377
-
1378
1256
  def yield_python_versions(
1379
1257
  version: str, /, *, max_: str = MAX_PYTHON_VERSION
1380
1258
  ) -> Iterator[str]:
@@ -1398,83 +1276,6 @@ def _yield_python_version_tuple(version: str, /) -> tuple[int, int]:
1398
1276
  ##
1399
1277
 
1400
1278
 
1401
- @contextmanager
1402
- def yield_text_file(
1403
- path: PathLike, /, *, modifications: MutableSet[Path] | None = None
1404
- ) -> Iterator[Path]:
1405
- path = Path(path)
1406
- try:
1407
- current = path.read_text()
1408
- except FileNotFoundError:
1409
- with TemporaryFile() as temp:
1410
- yield temp
1411
- write_text("Writing", temp, path, modifications=modifications)
1412
- else:
1413
- with TemporaryFile(text=current) as temp:
1414
- yield temp
1415
- if temp.read_text().rstrip("\n") != current.rstrip("\n"):
1416
- write_text("Writing", temp, path, modifications=modifications)
1417
-
1418
-
1419
- ##
1420
-
1421
-
1422
- @contextmanager
1423
- def yield_toml_doc(
1424
- path: PathLike, /, *, modifications: MutableSet[Path] | None = None
1425
- ) -> Iterator[TOMLDocument]:
1426
- with yield_write_context(
1427
- path, tomlkit.parse, document, tomlkit.dumps, modifications=modifications
1428
- ) as doc:
1429
- yield doc
1430
-
1431
-
1432
- ##
1433
-
1434
-
1435
- @contextmanager
1436
- def yield_write_context[T](
1437
- path: PathLike,
1438
- loads: Callable[[str], T],
1439
- get_default: Callable[[], T],
1440
- dumps: Callable[[T], str],
1441
- /,
1442
- *,
1443
- modifications: MutableSet[Path] | None = None,
1444
- ) -> Iterator[T]:
1445
- path = Path(path)
1446
-
1447
- def run_write(verb: str, data: T, /) -> None:
1448
- with writer(path, overwrite=True) as temp:
1449
- _ = temp.write_text(dumps(data))
1450
- write_text(verb, temp, path, modifications=modifications)
1451
-
1452
- try:
1453
- current = path.read_text()
1454
- except FileNotFoundError:
1455
- yield (default := get_default())
1456
- run_write("Writing", default)
1457
- else:
1458
- data = loads(current)
1459
- yield data
1460
- is_equal = data == loads(current) # tomlkit cannot handle !=
1461
- if not is_equal:
1462
- run_write("Modifying", data)
1463
-
1464
-
1465
- ##
1466
-
1467
-
1468
- @contextmanager
1469
- def yield_yaml_dict(
1470
- path: PathLike, /, *, modifications: MutableSet[Path] | None = None
1471
- ) -> Iterator[StrDict]:
1472
- with yield_write_context(
1473
- path, YAML_INSTANCE.load, dict, yaml_dump, modifications=modifications
1474
- ) as dict_:
1475
- yield dict_
1476
-
1477
-
1478
1279
  __all__ = [
1479
1280
  "add_bumpversion_toml",
1480
1281
  "add_coveragerc_toml",
@@ -1489,34 +1290,17 @@ __all__ = [
1489
1290
  "add_readme_md",
1490
1291
  "add_ruff_toml",
1491
1292
  "check_versions",
1492
- "ensure_aot_contains",
1493
- "ensure_contains",
1494
- "ensure_contains_partial",
1495
- "ensure_not_contains",
1496
- "get_aot",
1497
- "get_array",
1498
- "get_dict",
1499
- "get_list",
1500
- "get_partial_dict",
1293
+ "get_cron_job",
1501
1294
  "get_python_package_name",
1502
- "get_table",
1503
1295
  "get_version_from_bumpversion_toml",
1504
1296
  "get_version_from_git_show",
1505
1297
  "get_version_from_git_tag",
1506
- "is_partial_dict",
1507
1298
  "run_bump_my_version",
1508
1299
  "run_pre_commit_update",
1509
1300
  "run_ripgrep_and_replace",
1510
1301
  "set_version",
1511
1302
  "update_action_file_extensions",
1512
1303
  "update_action_versions",
1513
- "write_text",
1514
- "yaml_dump",
1515
1304
  "yield_bumpversion_toml",
1516
- "yield_json_dict",
1517
1305
  "yield_python_versions",
1518
- "yield_text_file",
1519
- "yield_toml_doc",
1520
- "yield_write_context",
1521
- "yield_yaml_dict",
1522
1306
  ]
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typed_settings import load_settings, option, settings
4
4
 
5
- from actions.conformalize_repo.constants import RUN_VERSION_BUMP
5
+ from actions.pre_commit.conformalize_repo.constants import RUN_VERSION_BUMP
6
6
  from actions.utilities import LOADER
7
7
 
8
8
 
@@ -0,0 +1,8 @@
1
+ from __future__ import annotations
2
+
3
+ from actions.constants import PATH_ACTIONS
4
+
5
+ PATH_PRE_COMMIT = PATH_ACTIONS / "pre_commit"
6
+
7
+
8
+ __all__ = ["PATH_PRE_COMMIT"]
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from utilities.logging import basic_config
6
+ from utilities.os import is_pytest
7
+
8
+ from actions.logging import LOGGER
9
+ from actions.pre_commit.click import path_argument
10
+ from actions.pre_commit.format_requirements.lib import format_requirements
11
+
12
+ if TYPE_CHECKING:
13
+ from pathlib import Path
14
+
15
+
16
+ @path_argument
17
+ def format_requirements_sub_cmd(*, paths: tuple[Path, ...]) -> None:
18
+ if is_pytest():
19
+ return
20
+ basic_config(obj=LOGGER)
21
+ format_requirements(*paths)
22
+
23
+
24
+ __all__ = ["format_requirements_sub_cmd"]
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ FORMAT_REQUIREMENTS_DOCSTRING = "Format a set of requirements"
4
+ FORMAT_REQUIREMENTS_SUB_CMD = "format-requirements"
5
+
6
+
7
+ __all__ = ["FORMAT_REQUIREMENTS_DOCSTRING", "FORMAT_REQUIREMENTS_SUB_CMD"]