copier_template 0.1.0__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.
- copier_template-0.1.0/PKG-INFO +20 -0
- copier_template-0.1.0/README.md +5 -0
- copier_template-0.1.0/pyproject.toml +49 -0
- copier_template-0.1.0/pyproject.toml.orig +44 -0
- copier_template-0.1.0/src/copier_template/__init__.py +3 -0
- copier_template-0.1.0/src/copier_template/__main__.py +3 -0
- copier_template-0.1.0/src/copier_template/config.py +16 -0
- copier_template-0.1.0/src/copier_template/main.py +200 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: copier_template
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Meta-template for creating copier projects
|
|
5
|
+
Requires-Dist: copier>=9.15.1
|
|
6
|
+
Requires-Dist: inflection>=0.5.1
|
|
7
|
+
Requires-Dist: pytest>=9.1.1
|
|
8
|
+
Requires-Dist: tomlkit>=0.15.0
|
|
9
|
+
Requires-Dist: typer>=0.26.6
|
|
10
|
+
Requires-Python: >=3.14
|
|
11
|
+
Project-URL: Homepage, https://github.com/Phazebreak-Coatings-Inc/copier-template
|
|
12
|
+
Project-URL: Documentation, https://phazebreak-coatings-inc.github.io/copier-template
|
|
13
|
+
Project-URL: Repository, https://github.com/Phazebreak-Coatings-Inc/copier-template/
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
Template for making copier repos.
|
|
17
|
+
|
|
18
|
+
MIT License available [here](./LICENSE)
|
|
19
|
+
|
|
20
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "copier_template"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Meta-template for creating copier projects"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"copier>=9.15.1",
|
|
9
|
+
"inflection>=0.5.1",
|
|
10
|
+
"pytest>=9.1.1",
|
|
11
|
+
"tomlkit>=0.15.0",
|
|
12
|
+
"typer>=0.26.6",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Homepage = "https://github.com/Phazebreak-Coatings-Inc/copier-template"
|
|
17
|
+
Documentation = "https://phazebreak-coatings-inc.github.io/copier-template"
|
|
18
|
+
Repository = "https://github.com/Phazebreak-Coatings-Inc/copier-template/"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["uv_build>=0.11.18,<0.12"]
|
|
22
|
+
build-backend = "uv_build"
|
|
23
|
+
|
|
24
|
+
[tool.pytest.ini_options]
|
|
25
|
+
norecursedirs = [
|
|
26
|
+
"*.egg",
|
|
27
|
+
".*",
|
|
28
|
+
"_darcs",
|
|
29
|
+
"build",
|
|
30
|
+
"CVS",
|
|
31
|
+
"dist",
|
|
32
|
+
"node_modules",
|
|
33
|
+
"venv",
|
|
34
|
+
"{arch}",
|
|
35
|
+
"example",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[tool.ruff]
|
|
39
|
+
exclude = [
|
|
40
|
+
"example",
|
|
41
|
+
"template",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.uv.workspace]
|
|
45
|
+
members = []
|
|
46
|
+
exclude = [
|
|
47
|
+
"example",
|
|
48
|
+
"template",
|
|
49
|
+
]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "copier_template"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Meta-template for creating copier projects"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"copier>=9.15.1",
|
|
9
|
+
"inflection>=0.5.1",
|
|
10
|
+
"pytest>=9.1.1",
|
|
11
|
+
"tomlkit>=0.15.0",
|
|
12
|
+
"typer>=0.26.6",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Homepage = "https://github.com/Phazebreak-Coatings-Inc/copier-template"
|
|
17
|
+
Documentation = "https://phazebreak-coatings-inc.github.io/copier-template"
|
|
18
|
+
Repository = "https://github.com/Phazebreak-Coatings-Inc/copier-template/"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["uv_build>=0.11.18,<0.12"]
|
|
22
|
+
build-backend = "uv_build"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
[tool.pytest.ini_options]
|
|
26
|
+
norecursedirs = [
|
|
27
|
+
"*.egg",
|
|
28
|
+
".*",
|
|
29
|
+
"_darcs",
|
|
30
|
+
"build",
|
|
31
|
+
"CVS",
|
|
32
|
+
"dist",
|
|
33
|
+
"node_modules",
|
|
34
|
+
"venv",
|
|
35
|
+
"{arch}",
|
|
36
|
+
"example",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
exclude = ["example", "template"]
|
|
41
|
+
|
|
42
|
+
[tool.uv.workspace]
|
|
43
|
+
members = []
|
|
44
|
+
exclude = ["example", "template"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
COPIER_REPO = "gh:Phazebreak-Coatings-Inc/alembic-environment"
|
|
2
|
+
ANSWERS_FILE = ".copier-answers.yml"
|
|
3
|
+
EXAMPLE_NAME = "example"
|
|
4
|
+
EXAMPLE_PROJECT_NAME = "example_project"
|
|
5
|
+
|
|
6
|
+
WORKSPACE = {}
|
|
7
|
+
## ENTER IN THE STYLE OF
|
|
8
|
+
# { "SOME DEPENDENCY NAME": "SOME DEPENDENDENCY FILE LOCATION"}
|
|
9
|
+
|
|
10
|
+
SCRIPTS = {}
|
|
11
|
+
## ENTER IN THE STYLE OF
|
|
12
|
+
# { "SOME SCRIPT NAME": "SOME PATH TO THE SCRIPT VIA PYTHON MODULE"}
|
|
13
|
+
|
|
14
|
+
PACKAGES = []
|
|
15
|
+
|
|
16
|
+
EXAMPLE_PRESENT = []
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import inflection
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
|
|
6
|
+
import copier
|
|
7
|
+
import tomlkit
|
|
8
|
+
import typer
|
|
9
|
+
from typer import Typer
|
|
10
|
+
|
|
11
|
+
type PyProject = tomlkit.TOMLDocument
|
|
12
|
+
|
|
13
|
+
type WorkspaceMembers = list[str]
|
|
14
|
+
|
|
15
|
+
import shutil
|
|
16
|
+
|
|
17
|
+
from .config import (
|
|
18
|
+
ANSWERS_FILE,
|
|
19
|
+
COPIER_REPO,
|
|
20
|
+
EXAMPLE_NAME,
|
|
21
|
+
EXAMPLE_PROJECT_NAME,
|
|
22
|
+
PACKAGES,
|
|
23
|
+
SCRIPTS,
|
|
24
|
+
WORKSPACE,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def sh(cmd: str, silent=False, check=True, **kwargs) -> subprocess.CompletedProcess:
|
|
29
|
+
if silent:
|
|
30
|
+
kwargs.setdefault("stdout", subprocess.PIPE)
|
|
31
|
+
kwargs.setdefault("stderr", subprocess.PIPE)
|
|
32
|
+
kwargs.setdefault("text", True)
|
|
33
|
+
try:
|
|
34
|
+
return subprocess.run(cmd, shell=True, check=check, **kwargs)
|
|
35
|
+
except subprocess.CalledProcessError as e:
|
|
36
|
+
if not silent:
|
|
37
|
+
typer.secho(f"\nFailed: {cmd}", fg=typer.colors.BRIGHT_RED, err=True)
|
|
38
|
+
if output := (e.stderr or e.stdout):
|
|
39
|
+
typer.secho(output.rstrip(), fg=typer.colors.RED, err=True)
|
|
40
|
+
raise typer.Exit(e.returncode) from None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
PYPROJECT_TEMPLATE = """\
|
|
44
|
+
[project]
|
|
45
|
+
name = "{PROJECT_NAME}"
|
|
46
|
+
version = "0.1.0"
|
|
47
|
+
requires-python = ">=3.14"
|
|
48
|
+
dependencies = []
|
|
49
|
+
|
|
50
|
+
[build-system]
|
|
51
|
+
requires = ["uv_build>=0.11.18,<0.12"]
|
|
52
|
+
build-backend = "uv_build"
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_pyproject(cwd: Path, fallback_name: str | None = None) -> tomlkit.TOMLDocument:
|
|
57
|
+
p = cwd / "pyproject.toml"
|
|
58
|
+
if not p.exists():
|
|
59
|
+
print(f"'pyproject'.toml not found at {p}")
|
|
60
|
+
p.touch()
|
|
61
|
+
n = fallback_name or inflection.underscore(
|
|
62
|
+
typer.prompt("What would you like to name your pyproject?")
|
|
63
|
+
)
|
|
64
|
+
p.write_text(PYPROJECT_TEMPLATE.format(PROJECT_NAME=n))
|
|
65
|
+
b = cwd / "src" / n
|
|
66
|
+
b.mkdir(parents=True)
|
|
67
|
+
(b / "__init__.py").touch()
|
|
68
|
+
|
|
69
|
+
return tomlkit.parse(p.read_text())
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def add_workspaces(p: PyProject, workspace: dict[str, str]) -> PyProject:
|
|
73
|
+
def sd(t, name):
|
|
74
|
+
return t.setdefault(name, tomlkit.table())
|
|
75
|
+
|
|
76
|
+
if len(workspace) == 0:
|
|
77
|
+
return p
|
|
78
|
+
|
|
79
|
+
uv = sd(sd(p, "tool"), "uv")
|
|
80
|
+
ws = sd(uv, "workspace")
|
|
81
|
+
ext = list(ws.get("members", []))
|
|
82
|
+
|
|
83
|
+
arr = tomlkit.array()
|
|
84
|
+
for m in ext + [m for m in workspace.values() if m not in ext]:
|
|
85
|
+
arr.append(m)
|
|
86
|
+
ws["members"] = arr
|
|
87
|
+
|
|
88
|
+
sources = sd(uv, "sources")
|
|
89
|
+
for name in workspace:
|
|
90
|
+
if name not in sources:
|
|
91
|
+
it = tomlkit.inline_table()
|
|
92
|
+
it["workspace"] = True
|
|
93
|
+
sources[name] = it
|
|
94
|
+
return p
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def add_scripts(p: PyProject, scripts: dict[str, str]) -> PyProject:
|
|
98
|
+
def sd(t, name):
|
|
99
|
+
return t.setdefault(name, tomlkit.table())
|
|
100
|
+
|
|
101
|
+
if len(scripts) == 0:
|
|
102
|
+
return p
|
|
103
|
+
|
|
104
|
+
table = sd(sd(p, "project"), "scripts")
|
|
105
|
+
for name, target in scripts.items():
|
|
106
|
+
table[name] = target
|
|
107
|
+
return p
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def write_pyproject(cwd: Path, p: PyProject) -> None:
|
|
111
|
+
(cwd / "pyproject.toml").write_text(tomlkit.dumps(p))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def prepare_pyproject(p: Path, project_name: str | None = None):
|
|
115
|
+
write_pyproject(
|
|
116
|
+
p,
|
|
117
|
+
add_scripts(add_workspaces(get_pyproject(p, project_name), WORKSPACE), SCRIPTS),
|
|
118
|
+
)
|
|
119
|
+
if len(WORKSPACE) > 0:
|
|
120
|
+
sh(f"uv add --workspace {' '.join(WORKSPACE)}", cwd=p)
|
|
121
|
+
if len(PACKAGES) > 0:
|
|
122
|
+
sh(f"uv add --dev {' '.join(PACKAGES)}", cwd=p)
|
|
123
|
+
sh("uv sync", cwd=p)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
app = Typer(pretty_exceptions_show_locals=False)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@app.command(help="Initialize a new copier-template project.")
|
|
130
|
+
def init(
|
|
131
|
+
dest: Annotated[
|
|
132
|
+
str, typer.Argument(help="Directory to initialize project in.")
|
|
133
|
+
] = ".",
|
|
134
|
+
):
|
|
135
|
+
get_pyproject(Path(dest).resolve())
|
|
136
|
+
copier.run_copy(COPIER_REPO, dest)
|
|
137
|
+
repair(dest)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@app.command(help="Update your existing project.")
|
|
141
|
+
def update(
|
|
142
|
+
abort: Annotated[
|
|
143
|
+
bool,
|
|
144
|
+
typer.Option(
|
|
145
|
+
"--abort",
|
|
146
|
+
"-a",
|
|
147
|
+
help="If the abort flag is triggered, this will reset the attempt to update the copier project. Prudent when the changes are too much to resolve.",
|
|
148
|
+
),
|
|
149
|
+
] = False,
|
|
150
|
+
):
|
|
151
|
+
match abort:
|
|
152
|
+
case False:
|
|
153
|
+
typer.confirm(
|
|
154
|
+
"Are you sure you want to update? If you need to abort mid-update, it will trigger a 'git reset.' Make sure to save all uncommitted changes.",
|
|
155
|
+
abort=True,
|
|
156
|
+
)
|
|
157
|
+
sh(f"copier update -a {ANSWERS_FILE} --conflict inline")
|
|
158
|
+
case True:
|
|
159
|
+
typer.confirm(
|
|
160
|
+
"Are you sure you want to abort? This will trigger a 'git reset.'",
|
|
161
|
+
abort=True,
|
|
162
|
+
)
|
|
163
|
+
sh("git reset")
|
|
164
|
+
sh("git checkout .")
|
|
165
|
+
sh("git clean -d -i")
|
|
166
|
+
repair()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@app.command(help="Hook up dependencies and workspaces correctly.")
|
|
170
|
+
def repair(
|
|
171
|
+
cwd: Annotated[
|
|
172
|
+
str, typer.Argument(help="Directory to initialize project in.")
|
|
173
|
+
] = ".",
|
|
174
|
+
):
|
|
175
|
+
p = Path(cwd).resolve()
|
|
176
|
+
prepare_pyproject(p)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@app.command(help="Destroy and regenerate the committed example project.")
|
|
180
|
+
def example():
|
|
181
|
+
root = Path.cwd().resolve()
|
|
182
|
+
if not (root / "copier.yml").exists():
|
|
183
|
+
typer.secho("Run from the template repo root.", fg=typer.colors.RED, err=True)
|
|
184
|
+
raise typer.Exit(1)
|
|
185
|
+
|
|
186
|
+
dst = root / EXAMPLE_NAME
|
|
187
|
+
if dst.exists():
|
|
188
|
+
shutil.rmtree(dst)
|
|
189
|
+
dst.mkdir()
|
|
190
|
+
|
|
191
|
+
sh(
|
|
192
|
+
f"uv run python -m copier copy {str(root)} {str(dst)} --trust -d project_name={EXAMPLE_PROJECT_NAME} --skip-tasks"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
(dst / ANSWERS_FILE).unlink(missing_ok=True)
|
|
196
|
+
prepare_pyproject(dst, EXAMPLE_PROJECT_NAME)
|
|
197
|
+
sh("uv build --all-packages", cwd=dst)
|
|
198
|
+
sh('uv run pytest tests/test_example.py -m "not slow"', cwd=root)
|
|
199
|
+
sh("uv run pytest", cwd=dst)
|
|
200
|
+
typer.secho(f"Regenerated {dst}", fg=typer.colors.GREEN)
|