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.
@@ -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,5 @@
1
+ Template for making copier repos.
2
+
3
+ MIT License available [here](./LICENSE)
4
+
5
+
@@ -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,3 @@
1
+ from .main import app
2
+
3
+ __all__ = ["app"]
@@ -0,0 +1,3 @@
1
+ from .main import app
2
+
3
+ app()
@@ -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)