dycw-pre-commit-hooks 0.11.0__tar.gz → 0.11.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dycw-pre-commit-hooks might be problematic. Click here for more details.
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/PKG-INFO +5 -2
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/README.md +1 -0
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/pyproject.toml +28 -19
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/src/pre_commit_hooks/__init__.py +1 -1
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/src/pre_commit_hooks/common.py +1 -1
- dycw_pre_commit_hooks-0.11.2/src/pre_commit_hooks/format_requirements/__init__.py +109 -0
- dycw_pre_commit_hooks-0.11.2/src/pre_commit_hooks/format_requirements/__main__.py +6 -0
- dycw_pre_commit_hooks-0.11.2/src/pre_commit_hooks/replace_sequence_str/__init__.py +55 -0
- dycw_pre_commit_hooks-0.11.2/src/pre_commit_hooks/replace_sequence_str/__main__.py +6 -0
- dycw_pre_commit_hooks-0.11.2/src/tests/format_requirements/__init__.py +0 -0
- dycw_pre_commit_hooks-0.11.2/src/tests/format_requirements/in.toml +30 -0
- dycw_pre_commit_hooks-0.11.2/src/tests/format_requirements/out.toml +30 -0
- dycw_pre_commit_hooks-0.11.2/src/tests/format_requirements/test_format_requirements.py +15 -0
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/.gitignore +0 -0
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/src/pre_commit_hooks/py.typed +0 -0
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/src/pre_commit_hooks/run_bump_my_version/__init__.py +0 -0
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/src/pre_commit_hooks/run_bump_my_version/__main__.py +0 -0
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/src/pre_commit_hooks/run_ruff_format/__init__.py +0 -0
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/src/pre_commit_hooks/run_ruff_format/__main__.py +0 -0
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/src/tests/__init__.py +0 -0
- {dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/src/tests/test_main.py +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dycw-pre-commit-hooks
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.2
|
|
4
4
|
Author-email: Derek Wan <d.wan@icloud.com>
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Requires-Dist: click<8.3,>=8.2.1
|
|
7
|
-
Requires-Dist: dycw-utilities<0.
|
|
7
|
+
Requires-Dist: dycw-utilities<0.151,>=0.150.11
|
|
8
|
+
Requires-Dist: libcst<1.9,>=1.8.2
|
|
8
9
|
Requires-Dist: loguru<0.8,>=0.7.3
|
|
10
|
+
Requires-Dist: packaging<25.1,>=25.0
|
|
9
11
|
Requires-Dist: tomlkit<0.14,>=0.13.2
|
|
10
12
|
Description-Content-Type: text/markdown
|
|
11
13
|
|
|
@@ -26,6 +28,7 @@ My [`pre-commit`](https://pre-commit.com/) hooks.
|
|
|
26
28
|
- repo: https://github.com/dycw/pre-commit-hooks
|
|
27
29
|
rev: master
|
|
28
30
|
hooks:
|
|
31
|
+
- id: replace-sequence-str
|
|
29
32
|
- id: run-bump-my-version
|
|
30
33
|
- id: run-ruff-format
|
|
31
34
|
```
|
|
@@ -9,7 +9,8 @@ requires = ["hatchling"]
|
|
|
9
9
|
[dependency-groups]
|
|
10
10
|
dev = [
|
|
11
11
|
"dycw-utilities[test]",
|
|
12
|
-
"pyright[nodejs]
|
|
12
|
+
"pyright[nodejs]",
|
|
13
|
+
"rich",
|
|
13
14
|
]
|
|
14
15
|
|
|
15
16
|
# project
|
|
@@ -17,24 +18,27 @@ dev = [
|
|
|
17
18
|
authors = [{name = "Derek Wan", email = "d.wan@icloud.com"}]
|
|
18
19
|
dependencies = [
|
|
19
20
|
"click >= 8.2.1, < 8.3",
|
|
20
|
-
"dycw-utilities >= 0.
|
|
21
|
+
"dycw-utilities >= 0.150.11, < 0.151",
|
|
22
|
+
"libcst >= 1.8.2, < 1.9",
|
|
21
23
|
"loguru >= 0.7.3, < 0.8",
|
|
24
|
+
"packaging >= 25.0, < 25.1",
|
|
22
25
|
"tomlkit >= 0.13.2, < 0.14",
|
|
23
26
|
]
|
|
24
27
|
name = "dycw-pre-commit-hooks"
|
|
25
28
|
readme = "README.md"
|
|
26
29
|
requires-python = ">= 3.12"
|
|
27
|
-
version = "0.11.
|
|
30
|
+
version = "0.11.2"
|
|
28
31
|
|
|
29
32
|
[project.scripts]
|
|
33
|
+
format-requirements = "pre_commit_hooks.format_requirements:main"
|
|
34
|
+
replace-sequence-str = "pre_commit_hooks.replace_sequence_str:main"
|
|
30
35
|
run-bump-my-version = "pre_commit_hooks.run_bump_my_version:main"
|
|
31
|
-
run-bump2version = "pre_commit_hooks.run_bump2version:main"
|
|
32
36
|
run-ruff-format = "pre_commit_hooks.run_ruff_format:main"
|
|
33
37
|
|
|
34
38
|
# bump-my-version
|
|
35
39
|
[tool.bumpversion]
|
|
36
40
|
allow_dirty = true
|
|
37
|
-
current_version = "0.11.
|
|
41
|
+
current_version = "0.11.2"
|
|
38
42
|
|
|
39
43
|
[[tool.bumpversion.files]]
|
|
40
44
|
filename = "src/pre_commit_hooks/__init__.py"
|
|
@@ -106,7 +110,15 @@ typeCheckingMode = "strict"
|
|
|
106
110
|
[tool.pytest]
|
|
107
111
|
|
|
108
112
|
[tool.pytest.ini_options]
|
|
109
|
-
addopts = [
|
|
113
|
+
addopts = [
|
|
114
|
+
"-ra",
|
|
115
|
+
"-vv",
|
|
116
|
+
"--color=auto",
|
|
117
|
+
"--strict-markers",
|
|
118
|
+
]
|
|
119
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
120
|
+
asyncio_mode = "auto"
|
|
121
|
+
collect_imported_tests = false
|
|
110
122
|
filterwarnings = ["error"]
|
|
111
123
|
minversion = "8.0"
|
|
112
124
|
testpaths = ["src/tests"]
|
|
@@ -120,8 +132,10 @@ unsafe-fixes = true
|
|
|
120
132
|
|
|
121
133
|
[tool.ruff.format]
|
|
122
134
|
preview = true
|
|
135
|
+
skip-magic-trailing-comma = true
|
|
123
136
|
|
|
124
137
|
[tool.ruff.lint]
|
|
138
|
+
explicit-preview-rules = true
|
|
125
139
|
fixable = ["ALL"]
|
|
126
140
|
ignore = [
|
|
127
141
|
"ANN401", # any-type
|
|
@@ -137,14 +151,17 @@ ignore = [
|
|
|
137
151
|
"D107", # undocumented-public-init
|
|
138
152
|
"D203", # one-blank-line-before-class
|
|
139
153
|
"D213", # multi-line-summary-second-line
|
|
154
|
+
"DOC", # pydoclint
|
|
140
155
|
"E501", # line-too-long
|
|
141
156
|
"PD", # pandas-vet
|
|
142
157
|
"PERF203", # try-except-in-loop
|
|
158
|
+
"PLC0415", # import-outside-top-level
|
|
143
159
|
"PLR0911", # too-many-return-statements
|
|
144
160
|
"PLR0912", # too-many-branches
|
|
145
161
|
"PLR0913", # too-many-arguments
|
|
146
162
|
"PLR0915", # too-many-statements
|
|
147
163
|
"PLR2004", # magic-value-comparison
|
|
164
|
+
"PT012", # pytest-raises-with-multiple-statements
|
|
148
165
|
"PT013", # pytest-incorrect-pytest-import
|
|
149
166
|
"S311", # suspicious-non-cryptographic-random-usage
|
|
150
167
|
"S603", # subprocess-without-shell-equals-true
|
|
@@ -167,19 +184,10 @@ ignore = [
|
|
|
167
184
|
"ISC001", # single-line-implicit-string-concatenation
|
|
168
185
|
"ISC002", # multi-line-implicit-string-concatenation
|
|
169
186
|
]
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
"
|
|
173
|
-
"
|
|
174
|
-
"F601", # multi-value-repeated-key-literal
|
|
175
|
-
"PIE794", # duplicate-class-field-definition
|
|
176
|
-
"PLR5501", # collapsible-else-if
|
|
177
|
-
"PT014", # pytest-duplicate-parametrize-test-cases
|
|
178
|
-
"RET504", # unnecessary-assign
|
|
179
|
-
"SIM102", # collapsible-if
|
|
180
|
-
"SIM105", # suppressible-exception
|
|
181
|
-
"SIM114", # if-with-same-arms
|
|
182
|
-
"T201", # print
|
|
187
|
+
preview = true
|
|
188
|
+
select = [
|
|
189
|
+
"ALL",
|
|
190
|
+
"RUF022", # unsorted-dunder-all
|
|
183
191
|
]
|
|
184
192
|
|
|
185
193
|
[tool.ruff.lint.extend-per-file-ignores]
|
|
@@ -199,3 +207,4 @@ ban-relative-imports = "all"
|
|
|
199
207
|
|
|
200
208
|
[tool.ruff.lint.isort]
|
|
201
209
|
required-imports = ["from __future__ import annotations"]
|
|
210
|
+
split-on-trailing-comma = false
|
{dycw_pre_commit_hooks-0.11.0 → dycw_pre_commit_hooks-0.11.2}/src/pre_commit_hooks/common.py
RENAMED
|
@@ -4,7 +4,7 @@ from dataclasses import dataclass
|
|
|
4
4
|
|
|
5
5
|
from loguru import logger
|
|
6
6
|
from tomlkit import TOMLDocument, parse
|
|
7
|
-
from utilities.
|
|
7
|
+
from utilities.pathlib import get_repo_root
|
|
8
8
|
|
|
9
9
|
_ROOT = get_repo_root()
|
|
10
10
|
PYPROJECT_TOML = _ROOT.joinpath("pyproject.toml")
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, override
|
|
4
|
+
|
|
5
|
+
from click import argument, command
|
|
6
|
+
from packaging._tokenizer import ParserSyntaxError
|
|
7
|
+
from packaging.requirements import (
|
|
8
|
+
InvalidRequirement,
|
|
9
|
+
Requirement,
|
|
10
|
+
_parse_requirement, # pyright: ignore[reportPrivateImportUsage]
|
|
11
|
+
)
|
|
12
|
+
from packaging.specifiers import Specifier, SpecifierSet
|
|
13
|
+
from tomlkit import array, dumps, loads, string
|
|
14
|
+
from tomlkit.items import Array, Table
|
|
15
|
+
from utilities.atomicwrites import writer
|
|
16
|
+
from utilities.click import FilePath
|
|
17
|
+
|
|
18
|
+
from pre_commit_hooks.common import PYPROJECT_TOML
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from collections.abc import Iterator
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from tomlkit.toml_document import TOMLDocument
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@command()
|
|
28
|
+
@argument("paths", nargs=-1, type=FilePath)
|
|
29
|
+
def main(*, paths: tuple[Path, ...]) -> bool:
|
|
30
|
+
"""CLI for the `format_requirements` hook."""
|
|
31
|
+
results = list(map(_process, paths))
|
|
32
|
+
return all(results)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _process(path: Path, /) -> bool:
|
|
36
|
+
doc = loads(path.read_text())
|
|
37
|
+
expected = _format_path(path)
|
|
38
|
+
if doc == expected:
|
|
39
|
+
return True
|
|
40
|
+
with writer(path, overwrite=True) as temp:
|
|
41
|
+
_ = temp.write_text(dumps(expected))
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _format_path(path: Path, /) -> TOMLDocument:
|
|
46
|
+
doc = loads(path.read_text())
|
|
47
|
+
if isinstance(dep_grps := doc.get("dependency-groups"), Table):
|
|
48
|
+
for key, value in dep_grps.items():
|
|
49
|
+
if isinstance(value, Array):
|
|
50
|
+
dep_grps[key] = _format_array(value)
|
|
51
|
+
if isinstance(project := doc["project"], Table):
|
|
52
|
+
if isinstance(deps := project["dependencies"], Array):
|
|
53
|
+
project["dependencies"] = _format_array(deps)
|
|
54
|
+
if isinstance(optional := project.get("optional-dependencies"), Table):
|
|
55
|
+
for key, value in optional.items():
|
|
56
|
+
if isinstance(value, Array):
|
|
57
|
+
optional[key] = _format_array(value)
|
|
58
|
+
return doc
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _format_array(dependencies: Array, /) -> Array:
|
|
62
|
+
new = array().multiline(multiline=True)
|
|
63
|
+
new.extend(map(_format_item, dependencies))
|
|
64
|
+
return new
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _format_item(item: Any, /) -> Any:
|
|
68
|
+
if not isinstance(item, str):
|
|
69
|
+
return item
|
|
70
|
+
return string(str(_CustomRequirement(item)))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class _CustomRequirement(Requirement):
|
|
74
|
+
@override
|
|
75
|
+
def __init__(self, requirement_string: str) -> None:
|
|
76
|
+
super().__init__(requirement_string)
|
|
77
|
+
try:
|
|
78
|
+
parsed = _parse_requirement(requirement_string)
|
|
79
|
+
except ParserSyntaxError as e:
|
|
80
|
+
raise InvalidRequirement(str(e)) from e
|
|
81
|
+
self.specifier = _CustomSpecifierSet(parsed.specifier)
|
|
82
|
+
|
|
83
|
+
@override
|
|
84
|
+
def _iter_parts(self, name: str) -> Iterator[str]:
|
|
85
|
+
yield name
|
|
86
|
+
if self.extras:
|
|
87
|
+
formatted_extras = ",".join(sorted(self.extras))
|
|
88
|
+
yield f"[{formatted_extras}]"
|
|
89
|
+
if self.specifier:
|
|
90
|
+
yield f" {self.specifier}"
|
|
91
|
+
if self.url:
|
|
92
|
+
yield f"@ {self.url}"
|
|
93
|
+
if self.marker:
|
|
94
|
+
yield " "
|
|
95
|
+
if self.marker:
|
|
96
|
+
yield f"; {self.marker}"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class _CustomSpecifierSet(SpecifierSet):
|
|
100
|
+
@override
|
|
101
|
+
def __str__(self) -> str:
|
|
102
|
+
specs = sorted(self._specs, key=self._key)
|
|
103
|
+
return ", ".join(map(str, specs))
|
|
104
|
+
|
|
105
|
+
def _key(self, spec: Specifier, /) -> int:
|
|
106
|
+
return [">=", "<"].index(spec.operator)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
__all__ = ["PYPROJECT_TOML", "main"]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, override
|
|
4
|
+
|
|
5
|
+
from click import argument, command
|
|
6
|
+
from libcst import CSTTransformer, Name, Subscript, parse_module
|
|
7
|
+
from libcst.matchers import Index as MIndex
|
|
8
|
+
from libcst.matchers import Name as MName
|
|
9
|
+
from libcst.matchers import Subscript as MSubscript
|
|
10
|
+
from libcst.matchers import SubscriptElement as MSubscriptElement
|
|
11
|
+
from libcst.matchers import matches
|
|
12
|
+
from libcst.metadata import MetadataWrapper
|
|
13
|
+
from utilities.click import FilePath
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@command()
|
|
20
|
+
@argument("paths", nargs=-1, type=FilePath)
|
|
21
|
+
def main(*, paths: tuple[Path, ...]) -> bool:
|
|
22
|
+
"""CLI for the `replace_sequence_str` hook."""
|
|
23
|
+
results = list(map(_process, paths))
|
|
24
|
+
return all(results)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _process(path: Path, /) -> bool:
|
|
28
|
+
existing = path.read_text()
|
|
29
|
+
wrapper = MetadataWrapper(parse_module(existing))
|
|
30
|
+
transformed = wrapper.module.visit(SequenceToListTransformer())
|
|
31
|
+
new = transformed.code
|
|
32
|
+
if existing == new:
|
|
33
|
+
return True
|
|
34
|
+
_ = path.write_text(new)
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SequenceToListTransformer(CSTTransformer):
|
|
39
|
+
@override
|
|
40
|
+
def leave_Subscript(
|
|
41
|
+
self, original_node: Subscript, updated_node: Subscript
|
|
42
|
+
) -> Subscript:
|
|
43
|
+
_ = original_node
|
|
44
|
+
if matches(
|
|
45
|
+
updated_node,
|
|
46
|
+
MSubscript(
|
|
47
|
+
value=MName("Sequence"),
|
|
48
|
+
slice=[MSubscriptElement(slice=MIndex(value=MName("str")))],
|
|
49
|
+
),
|
|
50
|
+
):
|
|
51
|
+
return updated_node.with_changes(value=Name("list"))
|
|
52
|
+
return updated_node
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
__all__ = ["main"]
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[dependency-groups]
|
|
2
|
+
group = [
|
|
3
|
+
"unbounded",
|
|
4
|
+
"lower>=1.2.3",
|
|
5
|
+
"upper<1.3",
|
|
6
|
+
"lower-and-upper1>=1.2.3,<1.3",
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
dependencies = [
|
|
11
|
+
"unbounded",
|
|
12
|
+
"lower1>=1.2.3",
|
|
13
|
+
"lower2 >= 1.2.3",
|
|
14
|
+
"upper1<1.3",
|
|
15
|
+
"upper2 < 1.3",
|
|
16
|
+
"lower-and-upper1>=1.2.3,<1.3",
|
|
17
|
+
"lower-and-upper2<1.3,>=1.2.3",
|
|
18
|
+
"lower-and-upper3 >= 1.2.3 , <1.3",
|
|
19
|
+
"with-extra[extra]",
|
|
20
|
+
"with-extra[extra]>=1.2.3",
|
|
21
|
+
"with-extra[extra] >= 1.2.3",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
group = [
|
|
26
|
+
"unbounded",
|
|
27
|
+
"lower>=1.2.3",
|
|
28
|
+
"upper<1.3",
|
|
29
|
+
"lower-and-upper1>=1.2.3,<1.3",
|
|
30
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[dependency-groups]
|
|
2
|
+
group = [
|
|
3
|
+
"unbounded",
|
|
4
|
+
"lower >=1.2.3",
|
|
5
|
+
"upper <1.3",
|
|
6
|
+
"lower-and-upper1 >=1.2.3, <1.3",
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
dependencies = [
|
|
11
|
+
"unbounded",
|
|
12
|
+
"lower1 >=1.2.3",
|
|
13
|
+
"lower2 >=1.2.3",
|
|
14
|
+
"upper1 <1.3",
|
|
15
|
+
"upper2 <1.3",
|
|
16
|
+
"lower-and-upper1 >=1.2.3, <1.3",
|
|
17
|
+
"lower-and-upper2 >=1.2.3, <1.3",
|
|
18
|
+
"lower-and-upper3 >=1.2.3, <1.3",
|
|
19
|
+
"with-extra[extra]",
|
|
20
|
+
"with-extra[extra] >=1.2.3",
|
|
21
|
+
"with-extra[extra] >=1.2.3",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
group = [
|
|
26
|
+
"unbounded",
|
|
27
|
+
"lower >=1.2.3",
|
|
28
|
+
"upper <1.3",
|
|
29
|
+
"lower-and-upper1 >=1.2.3, <1.3",
|
|
30
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from tomlkit import dumps
|
|
6
|
+
|
|
7
|
+
from pre_commit_hooks.format_requirements import _format_path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestFormatRequirements:
|
|
11
|
+
def test_basic(self) -> None:
|
|
12
|
+
root = Path(__file__).parent
|
|
13
|
+
result = dumps(_format_path(root.joinpath("in.toml")))
|
|
14
|
+
expected = root.joinpath("out.toml").read_text()
|
|
15
|
+
assert result == expected
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|