crackerjack 0.11.5__py3-none-any.whl → 0.12.0__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.
- crackerjack/.coverage +0 -0
- crackerjack/.gitignore +2 -0
- crackerjack/.pytest_cache/.gitignore +2 -0
- crackerjack/.pytest_cache/CACHEDIR.TAG +4 -0
- crackerjack/.pytest_cache/README.md +8 -0
- crackerjack/.pytest_cache/v/cache/nodeids +1 -0
- crackerjack/.pytest_cache/v/cache/stepwise +1 -0
- crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- crackerjack/__init__.py +2 -5
- crackerjack/__main__.py +101 -46
- crackerjack/crackerjack.py +340 -75
- crackerjack/pyproject.toml +5 -3
- crackerjack-0.12.0.dist-info/METADATA +225 -0
- {crackerjack-0.11.5.dist-info → crackerjack-0.12.0.dist-info}/RECORD +18 -11
- crackerjack-0.11.5.dist-info/METADATA +0 -164
- {crackerjack-0.11.5.dist-info → crackerjack-0.12.0.dist-info}/WHEEL +0 -0
- {crackerjack-0.11.5.dist-info → crackerjack-0.12.0.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.11.5.dist-info → crackerjack-0.12.0.dist-info}/licenses/LICENSE +0 -0
crackerjack/.coverage
ADDED
Binary file
|
crackerjack/.gitignore
CHANGED
@@ -0,0 +1,8 @@
|
|
1
|
+
# pytest cache directory #
|
2
|
+
|
3
|
+
This directory contains data from the pytest's cache plugin,
|
4
|
+
which provides the `--lf` and `--ff` options, as well as the `cache` fixture.
|
5
|
+
|
6
|
+
**Do not** commit this to version control.
|
7
|
+
|
8
|
+
See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information.
|
@@ -0,0 +1 @@
|
|
1
|
+
[]
|
@@ -0,0 +1 @@
|
|
1
|
+
[]
|
Binary file
|
Binary file
|
crackerjack/__init__.py
CHANGED
crackerjack/__main__.py
CHANGED
@@ -1,64 +1,119 @@
|
|
1
1
|
import typing as t
|
2
|
-
|
3
|
-
|
4
|
-
from pydantic import BaseModel
|
2
|
+
from enum import Enum
|
3
|
+
import typer
|
4
|
+
from pydantic import BaseModel, field_validator
|
5
|
+
from rich.console import Console
|
5
6
|
from crackerjack import crackerjack_it
|
6
7
|
|
8
|
+
console = Console(force_terminal=True)
|
9
|
+
app = typer.Typer(
|
10
|
+
help="Crackerjack: Your Python project setup and style enforcement tool."
|
11
|
+
)
|
12
|
+
|
13
|
+
|
14
|
+
class BumpOption(str, Enum):
|
15
|
+
micro = "micro"
|
16
|
+
minor = "minor"
|
17
|
+
major = "major"
|
18
|
+
|
19
|
+
def __str__(self) -> str:
|
20
|
+
return self.value
|
21
|
+
|
7
22
|
|
8
23
|
class Options(BaseModel):
|
9
24
|
commit: bool = False
|
10
25
|
interactive: bool = False
|
11
26
|
doc: bool = False
|
12
|
-
|
13
|
-
publish: t.
|
14
|
-
bump: t.
|
27
|
+
no_config_updates: bool = False
|
28
|
+
publish: t.Optional[BumpOption] = None
|
29
|
+
bump: t.Optional[BumpOption] = None
|
15
30
|
verbose: bool = False
|
16
31
|
update_precommit: bool = False
|
32
|
+
clean: bool = False
|
33
|
+
test: bool = False
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
@field_validator("publish", "bump", mode="before")
|
37
|
+
def validate_bump_options(cls, value: t.Optional[str]) -> t.Optional[BumpOption]:
|
38
|
+
if value is None:
|
39
|
+
return None
|
40
|
+
try:
|
41
|
+
return BumpOption(value.lower())
|
42
|
+
except ValueError:
|
43
|
+
valid_options = ", ".join([o.value for o in BumpOption])
|
44
|
+
raise ValueError(
|
45
|
+
f"Invalid bump option: {value}. Must be one of: {valid_options}"
|
46
|
+
)
|
47
|
+
|
48
|
+
|
49
|
+
cli_options = {
|
50
|
+
"commit": typer.Option(False, "-c", "--commit", help="Commit changes to Git."),
|
51
|
+
"interactive": typer.Option(
|
52
|
+
False, "-i", "--interactive", help="Run pre-commit hooks interactively."
|
53
|
+
),
|
54
|
+
"doc": typer.Option(False, "-d", "--doc", help="Generate documentation."),
|
55
|
+
"no_config_updates": typer.Option(
|
56
|
+
False, "-n", "--no-config-updates", help="Do not update configuration files."
|
57
|
+
),
|
58
|
+
"update_precommit": typer.Option(
|
59
|
+
False, "-u", "--update-precommit", help="Update pre-commit hooks."
|
60
|
+
),
|
61
|
+
"verbose": typer.Option(False, "-v", "--verbose", help="Enable verbose output."),
|
62
|
+
"publish": typer.Option(
|
63
|
+
None,
|
64
|
+
"-p",
|
65
|
+
"--publish",
|
66
|
+
help="Bump version and publish to PyPI (micro, minor, major).",
|
67
|
+
case_sensitive=False,
|
68
|
+
),
|
69
|
+
"bump": typer.Option(
|
70
|
+
None,
|
71
|
+
"-b",
|
72
|
+
"--bump",
|
73
|
+
help="Bump version (micro, minor, major).",
|
74
|
+
case_sensitive=False,
|
75
|
+
),
|
76
|
+
"clean": typer.Option(
|
77
|
+
False,
|
78
|
+
"-x",
|
79
|
+
"--clean",
|
80
|
+
help="Remove docstrings, line comments, and unnecessary whitespace.",
|
81
|
+
),
|
82
|
+
"test": typer.Option(False, "-t", "--test", help="Run tests."),
|
83
|
+
}
|
17
84
|
|
18
85
|
|
19
|
-
|
86
|
+
def create_options(**kwargs: t.Any) -> Options:
|
87
|
+
return Options(**kwargs)
|
20
88
|
|
21
89
|
|
22
|
-
@command()
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
c: bool = False,
|
35
|
-
i: bool = False,
|
36
|
-
d: bool = False,
|
37
|
-
u: bool = False,
|
38
|
-
v: bool = False,
|
39
|
-
x: bool = False,
|
40
|
-
p: str | bool = False,
|
41
|
-
b: str | bool = False,
|
90
|
+
@app.command()
|
91
|
+
def main(
|
92
|
+
commit: bool = cli_options["commit"],
|
93
|
+
interactive: bool = cli_options["interactive"],
|
94
|
+
doc: bool = cli_options["doc"],
|
95
|
+
no_config_updates: bool = cli_options["no_config_updates"],
|
96
|
+
update_precommit: bool = cli_options["update_precommit"],
|
97
|
+
verbose: bool = cli_options["verbose"],
|
98
|
+
publish: t.Optional[BumpOption] = cli_options["publish"],
|
99
|
+
bump: t.Optional[BumpOption] = cli_options["bump"],
|
100
|
+
clean: bool = cli_options["clean"],
|
101
|
+
test: bool = cli_options["test"],
|
42
102
|
) -> None:
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
options.bump = b
|
57
|
-
if v:
|
58
|
-
print("-v not currently implemented")
|
59
|
-
options.verbose = v
|
60
|
-
crackerjack_it(options=options)
|
103
|
+
options = create_options(
|
104
|
+
commit=commit,
|
105
|
+
interactive=interactive,
|
106
|
+
doc=doc,
|
107
|
+
no_config_updates=no_config_updates,
|
108
|
+
update_precommit=update_precommit,
|
109
|
+
verbose=verbose,
|
110
|
+
publish=publish,
|
111
|
+
bump=bump,
|
112
|
+
clean=clean,
|
113
|
+
test=test,
|
114
|
+
)
|
115
|
+
crackerjack_it(options)
|
61
116
|
|
62
117
|
|
63
118
|
if __name__ == "__main__":
|
64
|
-
|
119
|
+
app()
|
crackerjack/crackerjack.py
CHANGED
@@ -1,53 +1,189 @@
|
|
1
|
+
import ast
|
1
2
|
import re
|
3
|
+
import subprocess
|
2
4
|
import typing as t
|
5
|
+
from contextlib import suppress
|
3
6
|
from pathlib import Path
|
7
|
+
from subprocess import CompletedProcess
|
4
8
|
from subprocess import run as execute
|
5
9
|
from tomllib import loads
|
6
|
-
|
7
10
|
from pydantic import BaseModel
|
11
|
+
from rich.console import Console
|
8
12
|
from tomli_w import dumps
|
9
13
|
|
14
|
+
config_files = (".gitignore", ".pre-commit-config.yaml", ".libcst.codemod.yaml")
|
15
|
+
interactive_hooks = ("refurb", "bandit", "pyright")
|
16
|
+
default_python_version = "3.13"
|
10
17
|
|
11
|
-
class Crackerjack(BaseModel, arbitrary_types_allowed=True):
|
12
|
-
our_path: Path = Path(__file__).parent
|
13
|
-
pkg_path: Path = Path(Path.cwd())
|
14
|
-
pkg_dir: t.Optional[Path] = None
|
15
|
-
pkg_name: str = "crackerjack"
|
16
|
-
our_toml: t.Optional[dict[str, t.Any]] = None
|
17
|
-
pkg_toml: t.Optional[dict[str, t.Any]] = None
|
18
|
-
our_toml_path: t.Optional[Path] = None
|
19
|
-
pkg_toml_path: t.Optional[Path] = None
|
20
|
-
python_version: str = "3.13"
|
21
18
|
|
22
|
-
|
19
|
+
class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
|
20
|
+
console: Console
|
21
|
+
|
22
|
+
def clean_files(self, pkg_dir: Path | None) -> None:
|
23
|
+
if pkg_dir is None:
|
24
|
+
return
|
25
|
+
for file_path in pkg_dir.rglob("*.py"):
|
26
|
+
if not str(file_path.parent).startswith("__"):
|
27
|
+
self.clean_file(file_path)
|
28
|
+
if pkg_dir.parent.joinpath("__pycache__").exists():
|
29
|
+
pkg_dir.parent.joinpath("__pycache__").rmdir()
|
30
|
+
|
31
|
+
def clean_file(self, file_path: Path) -> None:
|
32
|
+
try:
|
33
|
+
code = file_path.read_text()
|
34
|
+
code = self.remove_docstrings(code)
|
35
|
+
code = self.remove_line_comments(code)
|
36
|
+
code = self.remove_extra_whitespace(code)
|
37
|
+
code = self.reformat_code(code)
|
38
|
+
file_path.write_text(code)
|
39
|
+
print(f"Cleaned: {file_path}")
|
40
|
+
except Exception as e:
|
41
|
+
print(f"Error cleaning {file_path}: {e}")
|
42
|
+
|
43
|
+
def remove_docstrings(self, code: str) -> str:
|
44
|
+
tree = ast.parse(code)
|
45
|
+
for node in ast.walk(tree):
|
46
|
+
if isinstance(
|
47
|
+
node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef, ast.Module)
|
48
|
+
):
|
49
|
+
if ast.get_docstring(node):
|
50
|
+
node.body = (
|
51
|
+
node.body[1:]
|
52
|
+
if isinstance(node.body[0], ast.Expr)
|
53
|
+
else node.body
|
54
|
+
)
|
55
|
+
return ast.unparse(tree)
|
56
|
+
|
57
|
+
def remove_line_comments(self, code: str) -> str:
|
58
|
+
lines = code.split("\n")
|
59
|
+
cleaned_lines = []
|
60
|
+
for line in lines:
|
61
|
+
comment_match = re.search("(?<!\\S)#(.*)", line)
|
62
|
+
if comment_match is None:
|
63
|
+
cleaned_lines.append(line)
|
64
|
+
continue
|
65
|
+
comment_start = comment_match.start()
|
66
|
+
code_part = line[:comment_start].rstrip()
|
67
|
+
comment = line[comment_start:].strip()
|
68
|
+
if code_part:
|
69
|
+
if re.match("^#(?: type: ignore| noqa)(.*)?$", comment):
|
70
|
+
cleaned_lines.append(line)
|
71
|
+
else:
|
72
|
+
cleaned_lines.append(code_part)
|
73
|
+
return "\n".join(cleaned_lines)
|
74
|
+
|
75
|
+
def remove_extra_whitespace(self, code: str) -> str:
|
76
|
+
lines = code.split("\n")
|
77
|
+
cleaned_lines = []
|
78
|
+
for i, line in enumerate(lines):
|
79
|
+
line = line.rstrip()
|
80
|
+
if i > 0 and (not line) and (not cleaned_lines[-1]):
|
81
|
+
continue
|
82
|
+
cleaned_lines.append(line)
|
83
|
+
return "\n".join(cleaned_lines)
|
84
|
+
|
85
|
+
def reformat_code(self, code: str) -> str:
|
86
|
+
try:
|
87
|
+
import tempfile
|
88
|
+
|
89
|
+
with tempfile.NamedTemporaryFile(
|
90
|
+
suffix=".py", mode="w+", delete=False
|
91
|
+
) as temp:
|
92
|
+
temp_path = Path(temp.name)
|
93
|
+
temp_path.write_text(code)
|
94
|
+
try:
|
95
|
+
result = subprocess.run(
|
96
|
+
["ruff", "format", str(temp_path)],
|
97
|
+
check=False,
|
98
|
+
capture_output=True,
|
99
|
+
text=True,
|
100
|
+
)
|
101
|
+
if result.returncode == 0:
|
102
|
+
formatted_code = temp_path.read_text()
|
103
|
+
else:
|
104
|
+
print(f"Ruff formatting failed: {result.stderr}")
|
105
|
+
formatted_code = code
|
106
|
+
except Exception as e:
|
107
|
+
print(f"Error running Ruff: {e}")
|
108
|
+
formatted_code = code
|
109
|
+
finally:
|
110
|
+
with suppress(FileNotFoundError):
|
111
|
+
temp_path.unlink()
|
112
|
+
return formatted_code
|
113
|
+
except Exception as e:
|
114
|
+
print(f"Error during reformatting: {e}")
|
115
|
+
return code
|
116
|
+
|
117
|
+
|
118
|
+
class ConfigManager(BaseModel, arbitrary_types_allowed=True):
|
119
|
+
our_path: Path
|
120
|
+
pkg_path: Path
|
121
|
+
pkg_name: str
|
122
|
+
console: Console
|
123
|
+
our_toml_path: Path | None = None
|
124
|
+
pkg_toml_path: Path | None = None
|
125
|
+
python_version: str = default_python_version
|
126
|
+
dry_run: bool = False
|
127
|
+
|
128
|
+
def swap_package_name(self, value: list[str] | str) -> list[str] | str:
|
23
129
|
if isinstance(value, list):
|
24
130
|
value.remove("crackerjack")
|
25
131
|
value.append(self.pkg_name)
|
26
|
-
|
132
|
+
else:
|
27
133
|
value = value.replace("crackerjack", self.pkg_name)
|
28
134
|
return value
|
29
135
|
|
30
136
|
def update_pyproject_configs(self) -> None:
|
137
|
+
self._setup_toml_paths()
|
138
|
+
if self._is_crackerjack_project():
|
139
|
+
self._handle_crackerjack_project()
|
140
|
+
return
|
141
|
+
our_toml_config = self._load_our_toml()
|
142
|
+
pkg_toml_config = self._load_pkg_toml()
|
143
|
+
self._ensure_required_sections(pkg_toml_config)
|
144
|
+
self._update_tool_settings(our_toml_config, pkg_toml_config)
|
145
|
+
self._update_python_version(our_toml_config, pkg_toml_config)
|
146
|
+
self._save_pkg_toml(pkg_toml_config)
|
147
|
+
|
148
|
+
def _setup_toml_paths(self) -> None:
|
31
149
|
toml_file = "pyproject.toml"
|
32
150
|
self.our_toml_path = self.our_path / toml_file
|
33
151
|
self.pkg_toml_path = self.pkg_path / toml_file
|
34
|
-
|
152
|
+
|
153
|
+
def _is_crackerjack_project(self) -> bool:
|
154
|
+
return self.pkg_path.stem == "crackerjack"
|
155
|
+
|
156
|
+
def _handle_crackerjack_project(self) -> None:
|
157
|
+
if self.our_toml_path and self.pkg_toml_path:
|
35
158
|
self.our_toml_path.write_text(self.pkg_toml_path.read_text())
|
36
|
-
|
37
|
-
|
38
|
-
|
159
|
+
|
160
|
+
def _load_our_toml(self) -> dict[str, t.Any]:
|
161
|
+
if self.our_toml_path:
|
162
|
+
return loads(self.our_toml_path.read_text())
|
163
|
+
return {}
|
164
|
+
|
165
|
+
def _load_pkg_toml(self) -> dict[str, t.Any]:
|
166
|
+
if self.pkg_toml_path:
|
167
|
+
return loads(self.pkg_toml_path.read_text())
|
168
|
+
return {}
|
169
|
+
|
170
|
+
def _ensure_required_sections(self, pkg_toml_config: dict[str, t.Any]) -> None:
|
39
171
|
pkg_toml_config.setdefault("tool", {})
|
40
172
|
pkg_toml_config.setdefault("project", {})
|
41
|
-
|
173
|
+
|
174
|
+
def _update_tool_settings(
|
175
|
+
self, our_toml_config: dict[str, t.Any], pkg_toml_config: dict[str, t.Any]
|
176
|
+
) -> None:
|
177
|
+
for tool, settings in our_toml_config.get("tool", {}).items():
|
42
178
|
for setting, value in settings.items():
|
43
179
|
if isinstance(value, dict):
|
44
180
|
for k, v in {
|
45
181
|
x: self.swap_package_name(y)
|
46
182
|
for x, y in value.items()
|
47
|
-
if isinstance(y, str
|
183
|
+
if isinstance(y, (str, list)) and "crackerjack" in str(y)
|
48
184
|
}.items():
|
49
185
|
settings[setting][k] = v
|
50
|
-
elif isinstance(value, str
|
186
|
+
elif isinstance(value, (str, list)) and "crackerjack" in str(value):
|
51
187
|
value = self.swap_package_name(value)
|
52
188
|
settings[setting] = value
|
53
189
|
if setting in (
|
@@ -60,20 +196,27 @@ class Crackerjack(BaseModel, arbitrary_types_allowed=True):
|
|
60
196
|
conf = pkg_toml_config["tool"].get(tool, {}).get(setting, [])
|
61
197
|
settings[setting] = list(set(conf + value))
|
62
198
|
pkg_toml_config["tool"][tool] = settings
|
63
|
-
|
64
|
-
|
199
|
+
|
200
|
+
def _update_python_version(
|
201
|
+
self, our_toml_config: dict[str, t.Any], pkg_toml_config: dict[str, t.Any]
|
202
|
+
) -> None:
|
203
|
+
python_version_pattern = "\\s*W*(\\d\\.\\d*)"
|
204
|
+
requires_python = our_toml_config.get("project", {}).get("requires-python", "")
|
65
205
|
classifiers = []
|
66
|
-
for classifier in pkg_toml_config
|
206
|
+
for classifier in pkg_toml_config.get("project", {}).get("classifiers", []):
|
67
207
|
classifier = re.sub(
|
68
208
|
python_version_pattern, f" {self.python_version}", classifier
|
69
209
|
)
|
70
210
|
classifiers.append(classifier)
|
71
211
|
pkg_toml_config["project"]["classifiers"] = classifiers
|
72
|
-
|
73
|
-
|
212
|
+
if requires_python:
|
213
|
+
pkg_toml_config["project"]["requires-python"] = requires_python
|
214
|
+
|
215
|
+
def _save_pkg_toml(self, pkg_toml_config: dict[str, t.Any]) -> None:
|
216
|
+
if self.pkg_toml_path:
|
217
|
+
self.pkg_toml_path.write_text(dumps(pkg_toml_config))
|
74
218
|
|
75
219
|
def copy_configs(self) -> None:
|
76
|
-
config_files = (".gitignore", ".pre-commit-config.yaml", ".libcst.codemod.yaml")
|
77
220
|
for config in config_files:
|
78
221
|
config_path = self.our_path / config
|
79
222
|
pkg_config_path = self.pkg_path / config
|
@@ -83,90 +226,212 @@ class Crackerjack(BaseModel, arbitrary_types_allowed=True):
|
|
83
226
|
continue
|
84
227
|
if config != ".gitignore":
|
85
228
|
pkg_config_path.write_text(
|
86
|
-
|
229
|
+
config_path.read_text().replace("crackerjack", self.pkg_name)
|
87
230
|
)
|
88
|
-
|
231
|
+
self.execute_command(["git", "add", config])
|
232
|
+
|
233
|
+
def execute_command(
|
234
|
+
self, cmd: list[str], **kwargs: t.Any
|
235
|
+
) -> subprocess.CompletedProcess[str]:
|
236
|
+
if self.dry_run:
|
237
|
+
self.console.print(f"[yellow]Would run: {' '.join(cmd)}[/yellow]")
|
238
|
+
return CompletedProcess(cmd, 0, "", "")
|
239
|
+
return execute(cmd, **kwargs)
|
240
|
+
|
241
|
+
|
242
|
+
class ProjectManager(BaseModel, arbitrary_types_allowed=True):
|
243
|
+
our_path: Path
|
244
|
+
pkg_path: Path
|
245
|
+
pkg_dir: Path | None = None
|
246
|
+
pkg_name: str = "crackerjack"
|
247
|
+
console: Console
|
248
|
+
code_cleaner: CodeCleaner
|
249
|
+
config_manager: ConfigManager
|
250
|
+
dry_run: bool = False
|
89
251
|
|
90
252
|
def run_interactive(self, hook: str) -> None:
|
91
253
|
success: bool = False
|
92
254
|
while not success:
|
93
|
-
fail =
|
255
|
+
fail = self.execute_command(
|
256
|
+
["pre-commit", "run", hook.lower(), "--all-files"]
|
257
|
+
)
|
94
258
|
if fail.returncode > 0:
|
95
259
|
retry = input(f"\n\n{hook.title()} failed. Retry? (y/N): ")
|
96
|
-
print()
|
260
|
+
self.console.print()
|
97
261
|
if retry.strip().lower() == "y":
|
98
262
|
continue
|
99
263
|
raise SystemExit(1)
|
100
264
|
success = True
|
101
265
|
|
102
266
|
def update_pkg_configs(self) -> None:
|
103
|
-
self.copy_configs()
|
104
|
-
installed_pkgs =
|
105
|
-
["pdm", "list", "--freeze"],
|
106
|
-
capture_output=True,
|
107
|
-
text=True,
|
267
|
+
self.config_manager.copy_configs()
|
268
|
+
installed_pkgs = self.execute_command(
|
269
|
+
["pdm", "list", "--freeze"], capture_output=True, text=True
|
108
270
|
).stdout.splitlines()
|
109
271
|
if not len([pkg for pkg in installed_pkgs if "pre-commit" in pkg]):
|
110
|
-
print("Initializing project...")
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
self.update_pyproject_configs()
|
272
|
+
self.console.print("Initializing project...")
|
273
|
+
self.execute_command(["pdm", "self", "add", "keyring"])
|
274
|
+
self.execute_command(["pdm", "config", "python.use_uv", "true"])
|
275
|
+
self.execute_command(["git", "init"])
|
276
|
+
self.execute_command(["git", "branch", "-m", "main"])
|
277
|
+
self.execute_command(["git", "add", "pyproject.toml"])
|
278
|
+
self.execute_command(["git", "add", "pdm.lock"])
|
279
|
+
self.execute_command(["pre-commit", "install"])
|
280
|
+
self.execute_command(["git", "config", "advice.addIgnoredFile", "false"])
|
281
|
+
self.config_manager.update_pyproject_configs()
|
120
282
|
|
121
283
|
def run_pre_commit(self) -> None:
|
122
|
-
|
284
|
+
self.console.print("\nRunning pre-commit hooks...\n")
|
285
|
+
check_all = self.execute_command(["pre-commit", "run", "--all-files"])
|
123
286
|
if check_all.returncode > 0:
|
124
|
-
check_all =
|
287
|
+
check_all = self.execute_command(["pre-commit", "run", "--all-files"])
|
125
288
|
if check_all.returncode > 0:
|
126
|
-
print("\n\nPre-commit failed. Please fix errors.\n")
|
289
|
+
self.console.print("\n\nPre-commit failed. Please fix errors.\n")
|
127
290
|
raise SystemExit(1)
|
128
291
|
|
129
|
-
def
|
292
|
+
def execute_command(
|
293
|
+
self, cmd: list[str], **kwargs: t.Any
|
294
|
+
) -> subprocess.CompletedProcess[str]:
|
295
|
+
if self.dry_run:
|
296
|
+
self.console.print(f"[yellow]Would run: {' '.join(cmd)}[/yellow]")
|
297
|
+
return CompletedProcess(cmd, 0, "", "")
|
298
|
+
return execute(cmd, **kwargs)
|
299
|
+
|
300
|
+
|
301
|
+
class Crackerjack(BaseModel, arbitrary_types_allowed=True):
|
302
|
+
our_path: Path = Path(__file__).parent
|
303
|
+
pkg_path: Path = Path(Path.cwd())
|
304
|
+
pkg_dir: Path | None = None
|
305
|
+
pkg_name: str = "crackerjack"
|
306
|
+
python_version: str = default_python_version
|
307
|
+
console: Console = Console(force_terminal=True)
|
308
|
+
dry_run: bool = False
|
309
|
+
code_cleaner: CodeCleaner | None = None
|
310
|
+
config_manager: ConfigManager | None = None
|
311
|
+
project_manager: ProjectManager | None = None
|
312
|
+
|
313
|
+
def __init__(self, **data: t.Any) -> None:
|
314
|
+
super().__init__(**data)
|
315
|
+
self.code_cleaner = CodeCleaner(console=self.console)
|
316
|
+
self.config_manager = ConfigManager(
|
317
|
+
our_path=self.our_path,
|
318
|
+
pkg_path=self.pkg_path,
|
319
|
+
pkg_name=self.pkg_name,
|
320
|
+
console=self.console,
|
321
|
+
python_version=self.python_version,
|
322
|
+
dry_run=self.dry_run,
|
323
|
+
)
|
324
|
+
self.project_manager = ProjectManager(
|
325
|
+
our_path=self.our_path,
|
326
|
+
pkg_path=self.pkg_path,
|
327
|
+
pkg_dir=self.pkg_dir,
|
328
|
+
pkg_name=self.pkg_name,
|
329
|
+
console=self.console,
|
330
|
+
code_cleaner=self.code_cleaner,
|
331
|
+
config_manager=self.config_manager,
|
332
|
+
dry_run=self.dry_run,
|
333
|
+
)
|
334
|
+
|
335
|
+
def _setup_package(self) -> None:
|
130
336
|
self.pkg_name = self.pkg_path.stem.lower().replace("-", "_")
|
131
337
|
self.pkg_dir = self.pkg_path / self.pkg_name
|
132
338
|
self.pkg_dir.mkdir(exist_ok=True)
|
133
|
-
print("\nCrackerjacking...\n")
|
134
|
-
|
135
|
-
|
136
|
-
|
339
|
+
self.console.print("\nCrackerjacking...\n")
|
340
|
+
self.config_manager.pkg_name = self.pkg_name
|
341
|
+
self.project_manager.pkg_name = self.pkg_name
|
342
|
+
self.project_manager.pkg_dir = self.pkg_dir
|
343
|
+
|
344
|
+
def _update_project(self, options: t.Any) -> None:
|
345
|
+
if not options.no_config_updates:
|
346
|
+
self.project_manager.update_pkg_configs()
|
347
|
+
result: CompletedProcess[str] = self.execute_command(
|
348
|
+
["pdm", "install"], capture_output=True, text=True
|
349
|
+
)
|
350
|
+
if result.returncode == 0:
|
351
|
+
self.console.print("PDM installed: ✅\n")
|
352
|
+
else:
|
353
|
+
self.console.print(
|
354
|
+
"\n\n❌ PDM installation failed. Is PDM is installed? Run `pipx install pdm` and try again.\n\n"
|
355
|
+
)
|
356
|
+
|
357
|
+
def _update_precommit(self, options: t.Any) -> None:
|
137
358
|
if self.pkg_path.stem == "crackerjack" and options.update_precommit:
|
138
|
-
|
359
|
+
self.execute_command(["pre-commit", "autoupdate"])
|
360
|
+
|
361
|
+
def _run_interactive_hooks(self, options: t.Any) -> None:
|
139
362
|
if options.interactive:
|
140
|
-
for hook in
|
141
|
-
self.run_interactive(hook)
|
142
|
-
|
363
|
+
for hook in interactive_hooks:
|
364
|
+
self.project_manager.run_interactive(hook)
|
365
|
+
|
366
|
+
def _clean_project(self, options: t.Any) -> None:
|
367
|
+
if options.clean:
|
368
|
+
if self.pkg_dir:
|
369
|
+
self.code_cleaner.clean_files(self.pkg_dir)
|
370
|
+
if self.pkg_path.stem == "crackerjack":
|
371
|
+
tests_dir = self.pkg_path / "tests"
|
372
|
+
if tests_dir.exists() and tests_dir.is_dir():
|
373
|
+
self.console.print("\nCleaning tests directory...\n")
|
374
|
+
self.code_cleaner.clean_files(tests_dir)
|
375
|
+
|
376
|
+
def _run_tests(self, options: t.Any) -> None:
|
377
|
+
if options.test:
|
378
|
+
self.console.print("\n\nRunning tests...\n")
|
379
|
+
result = self.execute_command(["pytest"], capture_output=True, text=True)
|
380
|
+
if result.stdout:
|
381
|
+
self.console.print(result.stdout)
|
382
|
+
if result.returncode > 0:
|
383
|
+
if result.stderr:
|
384
|
+
self.console.print(result.stderr)
|
385
|
+
self.console.print("\n\n❌ Tests failed. Please fix errors.\n")
|
386
|
+
return
|
387
|
+
self.console.print("\n\n✅ Tests passed successfully!\n")
|
388
|
+
|
389
|
+
def _bump_version(self, options: t.Any) -> None:
|
143
390
|
for option in (options.publish, options.bump):
|
144
391
|
if option:
|
145
|
-
|
392
|
+
self.execute_command(["pdm", "bump", option])
|
146
393
|
break
|
394
|
+
|
395
|
+
def _publish_project(self, options: t.Any) -> None:
|
147
396
|
if options.publish:
|
148
|
-
build =
|
149
|
-
|
397
|
+
build = self.execute_command(
|
398
|
+
["pdm", "build"], capture_output=True, text=True
|
399
|
+
)
|
400
|
+
self.console.print(build.stdout)
|
150
401
|
if build.returncode > 0:
|
151
|
-
print(build.stderr)
|
152
|
-
print("\n\nBuild failed. Please fix errors.\n")
|
402
|
+
self.console.print(build.stderr)
|
403
|
+
self.console.print("\n\nBuild failed. Please fix errors.\n")
|
153
404
|
raise SystemExit(1)
|
154
|
-
|
405
|
+
self.execute_command(["pdm", "publish", "--no-build"])
|
406
|
+
|
407
|
+
def _commit_and_push(self, options: t.Any) -> None:
|
155
408
|
if options.commit:
|
156
409
|
commit_msg = input("\nCommit message: ")
|
157
|
-
|
158
|
-
[
|
159
|
-
"git",
|
160
|
-
"commit",
|
161
|
-
"-m",
|
162
|
-
commit_msg,
|
163
|
-
"--no-verify",
|
164
|
-
"--",
|
165
|
-
".",
|
166
|
-
]
|
410
|
+
self.execute_command(
|
411
|
+
["git", "commit", "-m", commit_msg, "--no-verify", "--", "."]
|
167
412
|
)
|
168
|
-
|
169
|
-
|
413
|
+
self.execute_command(["git", "push", "origin", "main"])
|
414
|
+
|
415
|
+
def execute_command(
|
416
|
+
self, cmd: list[str], **kwargs: t.Any
|
417
|
+
) -> subprocess.CompletedProcess[str]:
|
418
|
+
if self.dry_run:
|
419
|
+
self.console.print(f"[yellow]Would run: {' '.join(cmd)}[/yellow]")
|
420
|
+
return CompletedProcess(cmd, 0, "", "")
|
421
|
+
return execute(cmd, **kwargs)
|
422
|
+
|
423
|
+
def process(self, options: t.Any) -> None:
|
424
|
+
self._setup_package()
|
425
|
+
self._update_project(options)
|
426
|
+
self._update_precommit(options)
|
427
|
+
self._run_interactive_hooks(options)
|
428
|
+
self._clean_project(options)
|
429
|
+
self.project_manager.run_pre_commit()
|
430
|
+
self._run_tests(options)
|
431
|
+
self._bump_version(options)
|
432
|
+
self._publish_project(options)
|
433
|
+
self._commit_and_push(options)
|
434
|
+
self.console.print("\nCrackerjack complete!\n")
|
170
435
|
|
171
436
|
|
172
437
|
crackerjack_it = Crackerjack().process
|
crackerjack/pyproject.toml
CHANGED
@@ -49,8 +49,9 @@ max-complexity = 12
|
|
49
49
|
convention = "google"
|
50
50
|
|
51
51
|
[tool.vulture]
|
52
|
-
min_confidence =
|
52
|
+
min_confidence = 86
|
53
53
|
paths = ["crackerjack",]
|
54
|
+
ignore_names = ["cls"]
|
54
55
|
|
55
56
|
[tool.creosote]
|
56
57
|
paths = [
|
@@ -119,7 +120,7 @@ pythonPlatform = "Darwin"
|
|
119
120
|
|
120
121
|
[project]
|
121
122
|
name = "crackerjack"
|
122
|
-
version = "0.11.
|
123
|
+
version = "0.11.5"
|
123
124
|
description = "Default template for PDM package"
|
124
125
|
requires-python = ">=3.13"
|
125
126
|
readme = "README.md"
|
@@ -145,7 +146,6 @@ classifiers = [
|
|
145
146
|
"Typing :: Typed",
|
146
147
|
]
|
147
148
|
dependencies = [
|
148
|
-
"click>=8.1.8",
|
149
149
|
"autotyping>=24.9.0",
|
150
150
|
"pre-commit>=4.1.0",
|
151
151
|
"pytest>=8.3.5",
|
@@ -157,6 +157,8 @@ dependencies = [
|
|
157
157
|
"pytest-mock>=3.14.0",
|
158
158
|
"tomli-w>=1.2.0",
|
159
159
|
"pytest-asyncio>=0.25.3",
|
160
|
+
"rich>=13.9.4",
|
161
|
+
"typer>=0.15.2",
|
160
162
|
]
|
161
163
|
authors = [
|
162
164
|
{ name = "lesleslie", email = "les@wedgwoodwebworks.com" },
|
@@ -0,0 +1,225 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: crackerjack
|
3
|
+
Version: 0.12.0
|
4
|
+
Summary: Default template for PDM package
|
5
|
+
Keywords: black,ruff,mypy,creosote,refurb
|
6
|
+
Author-Email: lesleslie <les@wedgwoodwebworks.com>
|
7
|
+
Maintainer-Email: lesleslie <les@wedgwoodwebworks.com>
|
8
|
+
License: BSD-3-CLAUSE
|
9
|
+
Classifier: Environment :: Console
|
10
|
+
Classifier: Operating System :: POSIX
|
11
|
+
Classifier: Programming Language :: Python
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
15
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
16
|
+
Classifier: Topic :: Software Development :: Testing
|
17
|
+
Classifier: Topic :: Utilities
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
19
|
+
Classifier: License :: OSI Approved :: BSD License
|
20
|
+
Classifier: Typing :: Typed
|
21
|
+
Project-URL: homepage, https://github.com/lesleslie/crackerjack
|
22
|
+
Project-URL: documentation, https://github.com/lesleslie/crackerjack
|
23
|
+
Project-URL: repository, https://github.com/lesleslie/crackerjack
|
24
|
+
Requires-Python: >=3.13
|
25
|
+
Requires-Dist: autotyping>=24.9.0
|
26
|
+
Requires-Dist: pre-commit>=4.1.0
|
27
|
+
Requires-Dist: pytest>=8.3.5
|
28
|
+
Requires-Dist: pydantic>=2.10.6
|
29
|
+
Requires-Dist: pdm-bump>=0.9.10
|
30
|
+
Requires-Dist: pdm>=2.22.4
|
31
|
+
Requires-Dist: uv>=0.6.5
|
32
|
+
Requires-Dist: pytest-cov>=6.0.0
|
33
|
+
Requires-Dist: pytest-mock>=3.14.0
|
34
|
+
Requires-Dist: tomli-w>=1.2.0
|
35
|
+
Requires-Dist: pytest-asyncio>=0.25.3
|
36
|
+
Requires-Dist: rich>=13.9.4
|
37
|
+
Requires-Dist: typer>=0.15.2
|
38
|
+
Description-Content-Type: text/markdown
|
39
|
+
|
40
|
+
# Crackerjack: Elevate Your Python Development
|
41
|
+
|
42
|
+
[](https://github.com/lesleslie/crackerjack)
|
43
|
+
[](https://www.python.org/downloads/)
|
44
|
+
[](https://github.com/astral-sh/ruff)
|
45
|
+
[](https://github.com/astral-sh/uv)
|
46
|
+
[](https://microsoft.github.io/pyright/)
|
47
|
+
[](https://pdm.fming.dev)
|
48
|
+
[](https://github.com/PyCQA/bandit)
|
49
|
+
[](https://opensource.org/licenses/BSD-3-Clause)
|
50
|
+
|
51
|
+
**Crackerjack** (`ˈkra-kər-ˌjak`): *a person or thing of marked excellence.*
|
52
|
+
|
53
|
+
Crackerjack is an opinionated Python project management tool designed to help you create, maintain, and publish high-quality Python projects with ease. It combines best-in-class tools and a streamlined workflow to ensure code quality, consistency, and reliability.
|
54
|
+
|
55
|
+
## The Crackerjack Philosophy
|
56
|
+
|
57
|
+
Crackerjack is built on the following core principles:
|
58
|
+
|
59
|
+
- **Code Clarity:** Code should be easy to read, understand, and maintain.
|
60
|
+
- **Automation:** Tedious tasks should be automated, allowing developers to focus on solving problems.
|
61
|
+
- **Consistency:** Code style, formatting, and project structure should be consistent across projects.
|
62
|
+
- **Reliability:** Tests are essential, and code should be checked rigorously.
|
63
|
+
- **Tool Integration:** Leverage powerful existing tools instead of reinventing the wheel.
|
64
|
+
- **Static Typing:** Static typing is essential for all development.
|
65
|
+
|
66
|
+
## Key Features
|
67
|
+
|
68
|
+
Crackerjack provides:
|
69
|
+
|
70
|
+
- **Effortless Project Setup:** Initializes new Python projects with a standard directory structure, `pyproject.toml`, and essential configuration files.
|
71
|
+
- **PDM Integration:** Manages dependencies and virtual environments using [PDM](https://pdm.fming.dev/) (with [uv](https://github.com/astral-sh/uv) enabled for speed).
|
72
|
+
- **Automated Code Cleaning:** Removes unnecessary docstrings, line comments, and trailing whitespace.
|
73
|
+
- **Consistent Code Formatting:** Enforces a consistent style using [Ruff](https://github.com/astral-sh/ruff), the lightning-fast Python linter and formatter.
|
74
|
+
- **Comprehensive Pre-commit Hooks:** Installs and manages a robust suite of pre-commit hooks to ensure code quality (see the "Pre-commit Hooks" section below).
|
75
|
+
- **Interactive Checks:** Supports interactive pre-commit hooks (like `refurb`, `bandit`, and `pyright`) to allow you to fix issues in real-time.
|
76
|
+
- **Built-in Testing:** Automatically runs tests using `pytest`.
|
77
|
+
- **Easy Version Bumping:** Provides commands to bump the project version (micro, minor, or major).
|
78
|
+
- **Simplified Publishing:** Automates publishing to PyPI via PDM.
|
79
|
+
- **Commit and Push:** Commits and pushes your changes.
|
80
|
+
|
81
|
+
## Pre-commit Hooks
|
82
|
+
|
83
|
+
Crackerjack automatically installs and manages these pre-commit hooks:
|
84
|
+
|
85
|
+
1. **pdm-lock-check:** Ensures the `pdm.lock` file is up to date.
|
86
|
+
2. **Core pre-commit-hooks:** Essential hooks from [pre-commit-hooks](https://github.com/pre-commit/pre-commit-hooks) (e.g., `trailing-whitespace`, `end-of-file-fixer`).
|
87
|
+
3. **Ruff:** [Ruff](https://github.com/astral-sh/ruff) for linting, code formatting, and general code style enforcement.
|
88
|
+
4. **Vulture:** [Vulture](https://github.com/jendrikseipp/vulture) to identify dead code.
|
89
|
+
5. **Creosote:** [Creosote](https://github.com/fredrikaverpil/creosote) to detect unused dependencies.
|
90
|
+
6. **Flynt:** [Flynt](https://github.com/ikamensh/flynt/) for converting string formatting to f-strings.
|
91
|
+
7. **Codespell:** [Codespell](https://github.com/codespell-project/codespell) for correcting typos in the code.
|
92
|
+
8. **Autotyping:** [Autotyping](https://github.com/JelleZijlstra/autotyping) for adding type hints.
|
93
|
+
9. **Refurb:** [Refurb](https://github.com/dosisod/refurb) to suggest code improvements.
|
94
|
+
10. **Bandit:** [Bandit](https://github.com/PyCQA/bandit) to identify potential security vulnerabilities.
|
95
|
+
11. **Pyright:** [Pyright](https://github.com/RobertCraigie/pyright-python) for static type checking.
|
96
|
+
12. **Ruff (again):** A final Ruff pass to ensure all changes comply with the enforced style.
|
97
|
+
|
98
|
+
## The Crackerjack Style Guide
|
99
|
+
|
100
|
+
Crackerjack projects adhere to these guidelines:
|
101
|
+
|
102
|
+
- **Static Typing:** Use type hints consistently throughout your code.
|
103
|
+
- **Explicit Naming:** Choose clear, descriptive names for classes, functions, variables, and other identifiers.
|
104
|
+
- **Markdown for Documentation:** Use Markdown (`.md`) for all documentation, including docstrings, READMEs, etc.
|
105
|
+
- **Pathlib:** Use `pathlib.Path` for handling file and directory paths instead of `os.path`.
|
106
|
+
- **Consistent Imports:** Use `import typing as t` for type hinting.
|
107
|
+
- **Constants and Config:** Do not use all-caps for constants or configuration settings.
|
108
|
+
- **Path Parameters:** Functions that handle file operations should accept `pathlib.Path` objects as parameters.
|
109
|
+
- **Dependency Management:** Use PDM for dependency management, package building, and publishing.
|
110
|
+
- **Testing:** Use pytest as your testing framework.
|
111
|
+
- **Python Version:** Crackerjack projects support the latest Python versions.
|
112
|
+
- **Clear Code:** Avoid overly complex code.
|
113
|
+
- **Modular:** Functions should do one thing well.
|
114
|
+
|
115
|
+
## Installation
|
116
|
+
|
117
|
+
1. **Python:** Ensure you have Python 3.13 installed.
|
118
|
+
2. **PDM:** Install [PDM](https://pdm.fming.dev/) using `pipx`:
|
119
|
+
|
120
|
+
```
|
121
|
+
pipx install pdm
|
122
|
+
```
|
123
|
+
|
124
|
+
3. **Crackerjack:** Install Crackerjack and initialize in your project root using:
|
125
|
+
```
|
126
|
+
pip install crackerjack
|
127
|
+
cd your_project_root
|
128
|
+
python -m crackerjack
|
129
|
+
```
|
130
|
+
|
131
|
+
## Usage
|
132
|
+
|
133
|
+
Run Crackerjack from the root of your Python project using:
|
134
|
+
|
135
|
+
python -m crackerjack
|
136
|
+
|
137
|
+
|
138
|
+
### Command-Line Options
|
139
|
+
|
140
|
+
- `-c`, `--commit`: Commit changes to Git.
|
141
|
+
- `-i`, `--interactive`: Run pre-commit hooks interactively when possible.
|
142
|
+
- `-n`, `--no-config-updates`: Skip updating configuration files (e.g., `pyproject.toml`).
|
143
|
+
- `-u`, `--update-precommit`: Update pre-commit hooks to the latest versions.
|
144
|
+
- `-v`, `--verbose`: Enable verbose output.
|
145
|
+
- `-p`, `--publish <micro|minor|major>`: Bump the project version and publish to PyPI using PDM.
|
146
|
+
- `-b`, `--bump <micro|minor|major>`: Bump the project version without publishing.
|
147
|
+
- `-x`, `--clean`: Clean code by removing docstrings, line comments, and extra whitespace.
|
148
|
+
- `-t`, `--test`: Run tests using `pytest`.
|
149
|
+
- `-h`, `--help`: Display help.
|
150
|
+
|
151
|
+
### Example Workflows
|
152
|
+
|
153
|
+
- **Run checks, bump version, publish, then commit:**
|
154
|
+
```
|
155
|
+
python -m crackerjack -p minor -c
|
156
|
+
```
|
157
|
+
|
158
|
+
- **Clean code, run checks, run tests, then commit:**
|
159
|
+
````
|
160
|
+
python -m crackerjack -c -x -t
|
161
|
+
```
|
162
|
+
|
163
|
+
- **Run checks skipping config updates:**
|
164
|
+
```
|
165
|
+
python -m crackerjack -n
|
166
|
+
```
|
167
|
+
|
168
|
+
- **Bump the version and publish to PyPI:**
|
169
|
+
```
|
170
|
+
python -m crackerjack -p micro
|
171
|
+
```
|
172
|
+
|
173
|
+
- **Bump the version without publishing:**
|
174
|
+
```
|
175
|
+
python -m crackerjack -b major
|
176
|
+
```
|
177
|
+
- **Update pre-commit hooks:**
|
178
|
+
```
|
179
|
+
python -m crackerjack -u
|
180
|
+
```
|
181
|
+
- **Get help:**
|
182
|
+
```
|
183
|
+
python -m crackerjack -h
|
184
|
+
```
|
185
|
+
|
186
|
+
## Contributing
|
187
|
+
|
188
|
+
Crackerjack is an evolving project. Contributions are welcome! Please open a pull request or issue.
|
189
|
+
|
190
|
+
To contribute:
|
191
|
+
|
192
|
+
1. Add Crackerjack as a development dependency to your project:
|
193
|
+
```
|
194
|
+
pdm add -G dev crackerjack
|
195
|
+
```
|
196
|
+
|
197
|
+
2. Run checks and tests before submitting:
|
198
|
+
```
|
199
|
+
python -m crackerjack -x -t
|
200
|
+
```
|
201
|
+
|
202
|
+
This ensures your code meets all quality standards before submission.
|
203
|
+
|
204
|
+
## License
|
205
|
+
|
206
|
+
This project is licensed under the terms of the BSD 3-Clause license.
|
207
|
+
|
208
|
+
## Acknowledgments
|
209
|
+
|
210
|
+
- **PDM:** For excellent dependency and virtual environment management.
|
211
|
+
- **Ruff:** For lightning-fast linting and code formatting.
|
212
|
+
- **pre-commit:** For the robust hook management system.
|
213
|
+
- **pytest:** For the flexible and powerful testing framework.
|
214
|
+
- **uv:** For greatly improving PDM speeds.
|
215
|
+
- **bandit:** For finding security vulnerabilities.
|
216
|
+
- **vulture:** for dead code detection.
|
217
|
+
- **creosote:** For unused dependency detection.
|
218
|
+
- **flynt:** For f-string conversion.
|
219
|
+
- **codespell:** For spelling correction.
|
220
|
+
- **autotyping:** For automatically adding type hints.
|
221
|
+
- **refurb:** For code improvement suggestions.
|
222
|
+
- **pyright:** For static type checking.
|
223
|
+
- **Typer:** For the creation of the CLI.
|
224
|
+
|
225
|
+
---
|
@@ -1,11 +1,17 @@
|
|
1
|
-
crackerjack-0.
|
2
|
-
crackerjack-0.
|
3
|
-
crackerjack-0.
|
4
|
-
crackerjack-0.
|
5
|
-
crackerjack/.
|
1
|
+
crackerjack-0.12.0.dist-info/METADATA,sha256=WvBhT8kVf9gxBhKuM_yH4EwNdnuYGEpBEBKYIlptc8A,10289
|
2
|
+
crackerjack-0.12.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
3
|
+
crackerjack-0.12.0.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
4
|
+
crackerjack-0.12.0.dist-info/licenses/LICENSE,sha256=fDt371P6_6sCu7RyqiZH_AhT1LdN3sN1zjBtqEhDYCk,1531
|
5
|
+
crackerjack/.coverage,sha256=dLzPzp72qZEXohNfxnOAlRwvM9dqF06-HoFqfvXZd1U,53248
|
6
|
+
crackerjack/.gitignore,sha256=l8ErBAypC3rI6N9lhc7ZMdOw87t0Tz69ZW5C6uj15Wg,214
|
6
7
|
crackerjack/.libcst.codemod.yaml,sha256=a8DlErRAIPV1nE6QlyXPAzTOgkB24_spl2E9hphuf5s,772
|
7
8
|
crackerjack/.pdm.toml,sha256=dZe44HRcuxxCFESGG8SZIjmc-cGzSoyK3Hs6t4NYA8w,23
|
8
9
|
crackerjack/.pre-commit-config.yaml,sha256=cLIzulHerHmCHjo5UWA2JAMm5d5PjwGpIp5Lx-nL3FQ,2267
|
10
|
+
crackerjack/.pytest_cache/.gitignore,sha256=Ptcxtl0GFQwTji2tsL4Gl1UIiKa0frjEXsya26i46b0,37
|
11
|
+
crackerjack/.pytest_cache/CACHEDIR.TAG,sha256=N9yI75oKvt2-gQU6bdj9-xOvthMEXqHrSlyBWnSjveQ,191
|
12
|
+
crackerjack/.pytest_cache/README.md,sha256=c_1vzN2ALEGaay2YPWwxc7fal1WKxLWJ7ewt_kQ9ua0,302
|
13
|
+
crackerjack/.pytest_cache/v/cache/nodeids,sha256=T1PNoYwrqgwDVLtfmj7L5e0Sq02OEbqHPC8RFhICuUU,2
|
14
|
+
crackerjack/.pytest_cache/v/cache/stepwise,sha256=T1PNoYwrqgwDVLtfmj7L5e0Sq02OEbqHPC8RFhICuUU,2
|
9
15
|
crackerjack/.ruff_cache/.gitignore,sha256=aEiIwOuxfzdCmLZe4oB1JsBmCUxwG8x-u-HBCV9JT8E,1
|
10
16
|
crackerjack/.ruff_cache/0.1.11/3256171999636029978,sha256=-RLDsRf5uj09SyFQVzjwQ1HkTxjIRxNLLE24SEJxD4g,248
|
11
17
|
crackerjack/.ruff_cache/0.1.14/602324811142551221,sha256=HIYvldb69IHdMzquAA8JpzU2RDT9shEB_dPvzyeFZ_g,248
|
@@ -34,12 +40,13 @@ crackerjack/.ruff_cache/0.7.1/1024065805990144819,sha256=3Sww592NB0PWBNHU_UIqvqg
|
|
34
40
|
crackerjack/.ruff_cache/0.7.1/285614542852677309,sha256=mOHKRzKoSvW-1sHtqI_LHWRt-mBinJ4rQRtp9Yqzv5I,224
|
35
41
|
crackerjack/.ruff_cache/0.7.3/16061516852537040135,sha256=AWJR9gmaO7-wpv8mY1homuwI8CrMPI3VrnbXH-wRPlg,224
|
36
42
|
crackerjack/.ruff_cache/0.8.4/16354268377385700367,sha256=Ksz4X8N6Z1i83N0vV1PxmBRlqgjrtzmDCOg7VBF4baQ,224
|
37
|
-
crackerjack/.ruff_cache/0.9.10/12813592349865671909,sha256=
|
43
|
+
crackerjack/.ruff_cache/0.9.10/12813592349865671909,sha256=c68BYIC0rNDH3TrWX-RKYI1gb7KGKAck8MOpwzySX7U,224
|
38
44
|
crackerjack/.ruff_cache/0.9.3/13948373885254993391,sha256=kGhtIkzPUtKAgvlKs3D8j4QM4qG8RhsHrmQJI69Sv3o,224
|
39
45
|
crackerjack/.ruff_cache/0.9.9/12813592349865671909,sha256=tmr8_vhRD2OxsVuMfbJPdT9fDFX-d5tfC5U9jgziyho,224
|
46
|
+
crackerjack/.ruff_cache/0.9.9/8843823720003377982,sha256=e4ymkXfQsUg5e_mtO34xTsaTvs1uA3_fI216Qq9qCAM,136
|
40
47
|
crackerjack/.ruff_cache/CACHEDIR.TAG,sha256=WVMVbX4MVkpCclExbq8m-IcOZIOuIZf5FrYw5Pk-Ma4,43
|
41
|
-
crackerjack/__init__.py,sha256=
|
42
|
-
crackerjack/__main__.py,sha256=
|
43
|
-
crackerjack/crackerjack.py,sha256=
|
44
|
-
crackerjack/pyproject.toml,sha256=
|
45
|
-
crackerjack-0.
|
48
|
+
crackerjack/__init__.py,sha256=EOKnIXfBAvxS55uPkpk5DbcNqVS29wja_IcCPaGwyus,141
|
49
|
+
crackerjack/__main__.py,sha256=nraD_3T7XokPEniztNRxfh6hMCRS7ihRucQWswb7j9I,3485
|
50
|
+
crackerjack/crackerjack.py,sha256=W7azMMGcZ5xZjthU6vIqHhXm0UAmENFSPJvb4pFaxjY,17401
|
51
|
+
crackerjack/pyproject.toml,sha256=z5FPRNjNaa9_6cb4gnEOdRdjqAOKI57Lunk-E3F3ZeM,3442
|
52
|
+
crackerjack-0.12.0.dist-info/RECORD,,
|
@@ -1,164 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: crackerjack
|
3
|
-
Version: 0.11.5
|
4
|
-
Summary: Default template for PDM package
|
5
|
-
Keywords: black,ruff,mypy,creosote,refurb
|
6
|
-
Author-Email: lesleslie <les@wedgwoodwebworks.com>
|
7
|
-
Maintainer-Email: lesleslie <les@wedgwoodwebworks.com>
|
8
|
-
License: BSD-3-CLAUSE
|
9
|
-
Classifier: Environment :: Console
|
10
|
-
Classifier: Operating System :: POSIX
|
11
|
-
Classifier: Programming Language :: Python
|
12
|
-
Classifier: Programming Language :: Python :: 3.13
|
13
|
-
Classifier: Development Status :: 4 - Beta
|
14
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
15
|
-
Classifier: Topic :: Software Development :: Quality Assurance
|
16
|
-
Classifier: Topic :: Software Development :: Testing
|
17
|
-
Classifier: Topic :: Utilities
|
18
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
19
|
-
Classifier: License :: OSI Approved :: BSD License
|
20
|
-
Classifier: Typing :: Typed
|
21
|
-
Project-URL: homepage, https://github.com/lesleslie/crackerjack
|
22
|
-
Project-URL: documentation, https://github.com/lesleslie/crackerjack
|
23
|
-
Project-URL: repository, https://github.com/lesleslie/crackerjack
|
24
|
-
Requires-Python: >=3.13
|
25
|
-
Requires-Dist: click>=8.1.8
|
26
|
-
Requires-Dist: autotyping>=24.9.0
|
27
|
-
Requires-Dist: pre-commit>=4.1.0
|
28
|
-
Requires-Dist: pytest>=8.3.5
|
29
|
-
Requires-Dist: pydantic>=2.10.6
|
30
|
-
Requires-Dist: pdm-bump>=0.9.10
|
31
|
-
Requires-Dist: pdm>=2.22.4
|
32
|
-
Requires-Dist: uv>=0.6.5
|
33
|
-
Requires-Dist: pytest-cov>=6.0.0
|
34
|
-
Requires-Dist: pytest-mock>=3.14.0
|
35
|
-
Requires-Dist: tomli-w>=1.2.0
|
36
|
-
Requires-Dist: pytest-asyncio>=0.25.3
|
37
|
-
Description-Content-Type: text/markdown
|
38
|
-
|
39
|
-
# Crackerjack Python
|
40
|
-
|
41
|
-
[](https://docs.python.org/3/)
|
42
|
-
[](https://github.com/astral-sh/ruff)
|
43
|
-
[](https://github.com/ambv/black)
|
44
|
-
[](https://microsoft.github.io/pyright/)
|
45
|
-
[](https://pdm.fming.dev)
|
46
|
-
[](https://github.com/astral-sh/uv)
|
47
|
-
[](https://github.com/PyCQA/bandit)
|
48
|
-
[](https://github.com/lesleslie/crackerjack)
|
49
|
-
|
50
|
-
Crackerjack is a python coding style which uses a minimalist approach to produce elegant, easy to read, code.
|
51
|
-
|
52
|
-
crack·er·jack ˈkra-kər-ˌjak
|
53
|
-
: a person or thing of marked excellence
|
54
|
-
|
55
|
-
### **Why Crackerjack?**
|
56
|
-
|
57
|
-
Crackerjack works on the theory that with static typing and explicit class,
|
58
|
-
function, variable, and other object names - the code should be
|
59
|
-
straight forward to read. Documentation and tests should be able to write themselves using a generative ai.
|
60
|
-
Crackerjack provides a set of guidelines and utilities to keep the codebase clean, elegant, standardized, and
|
61
|
-
easily readable.
|
62
|
-
|
63
|
-
### **What does this package do?**
|
64
|
-
|
65
|
-
This package:
|
66
|
-
|
67
|
-
- streamlines and standardizes code style across numerous packages
|
68
|
-
|
69
|
-
- installs, or updates, a project's pre-commit tools as well as .gitignore & other config files
|
70
|
-
to comply with evolving crackerjack standards
|
71
|
-
|
72
|
-
- runs the following pre-commit hooks (in order):
|
73
|
-
* [pdm-lock-check](https://github.com/pdm-project/pdm)
|
74
|
-
* various core [pre-commit-hooks](https://github.com/pre-commit/pre-commit-hooks)
|
75
|
-
* [ruff](https://github.com/charliermarsh/ruff-pre-commit)
|
76
|
-
* [vulture](https://github.com/jendrikseipp/vulture)
|
77
|
-
* [creosote](https://github.com/fredrikaverpil/creosote)
|
78
|
-
* [flynt](https://github.com/ikamensh/flynt/)
|
79
|
-
* [codespell](https://github.com/codespell-project/codespell)
|
80
|
-
* [autotyping](https://github.com/JelleZijlstra/autotyping)
|
81
|
-
* [refurb](https://github.com/dosisod/refurb)
|
82
|
-
* [bandit](https://github.com/PyCQA/bandit)
|
83
|
-
* [pyright](https://github.com/RobertCraigie/pyright-python)
|
84
|
-
* [ruff](https://github.com/charliermarsh/ruff-pre-commit) (again for sanity checking)
|
85
|
-
|
86
|
-
- converts/creates documentation in Markdown (md) (work in progress)
|
87
|
-
|
88
|
-
- runs tests and generates pytest mock stubs if needed (work in progress)
|
89
|
-
|
90
|
-
- bumps the project version and publishes it to PyPI
|
91
|
-
|
92
|
-
- commits changes to git repositories
|
93
|
-
|
94
|
-
### **What are the rules?**
|
95
|
-
|
96
|
-
(...more what you'd call "guidelines" than actual rules. -Captain Barbossa )
|
97
|
-
|
98
|
-
- code is statically typed
|
99
|
-
|
100
|
-
- all docstrings, README's, and other documentation is to be done in Markdown (md)
|
101
|
-
|
102
|
-
- use aiopath.AsyncPath or pathlib.Path not os.path
|
103
|
-
|
104
|
-
- import typing as t
|
105
|
-
|
106
|
-
- do not capitalize all letters in configuration settings or constants (we diverge from PEP-8 here
|
107
|
-
for not other reason than it looks ugly)
|
108
|
-
|
109
|
-
- functions that deal with path operations should get passed AsyncPaths or Paths - not strings
|
110
|
-
|
111
|
-
- use PDM (uv support enabled) for dependency management and package building/publishing
|
112
|
-
|
113
|
-
- use pdoc and mkdocs for producing documentation
|
114
|
-
|
115
|
-
- use pytest for testing
|
116
|
-
|
117
|
-
- be compliant with, and only support, the latest python version within 2 months after release
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
[//]: # (- variable docstrings are supported as outlined in)
|
122
|
-
|
123
|
-
[//]: # ( [PEP-224](https://www.python.org/dev/peps/pep-0224/) as well as the module-level)
|
124
|
-
|
125
|
-
[//]: # ( __pdoc__ dictionary (see [pdoc docs]()
|
126
|
-
|
127
|
-
[//]: # ( https://pdoc3.github.io/pdoc/doc/pdoc/#overriding-docstrings-with-__pdoc__)))
|
128
|
-
|
129
|
-
|
130
|
-
### **Installation**
|
131
|
-
|
132
|
-
From your projects root directory:
|
133
|
-
|
134
|
-
```pdm add -d crackerjack```
|
135
|
-
|
136
|
-
### **Usage**
|
137
|
-
|
138
|
-
From your projects root directory:
|
139
|
-
|
140
|
-
```python -m crackerjack```
|
141
|
-
|
142
|
-
For a full list of options:
|
143
|
-
|
144
|
-
```python -m crackerjack -h```
|
145
|
-
|
146
|
-
When you ready to publish your project:
|
147
|
-
|
148
|
-
``python -m crackerjack -p micro``
|
149
|
-
|
150
|
-
The -p option not only publishes your project but will bump your
|
151
|
-
project version for you. The options are 'micro', 'minor', and 'major'.
|
152
|
-
Put the -c option at the end and commit the bumped version to your git
|
153
|
-
repository at the same time:
|
154
|
-
|
155
|
-
``python -m crackerjack -p micro -c``
|
156
|
-
|
157
|
-
### **Contributing**
|
158
|
-
|
159
|
-
Crackerjack is currently an evolving standard. If you like the idea, but don't like certain things about it, or
|
160
|
-
would like new features added, let me know in Discussions, Issues, or email me.
|
161
|
-
|
162
|
-
### **License**
|
163
|
-
|
164
|
-
BSD-3-Clause
|
File without changes
|
File without changes
|
File without changes
|