Skip to content

Commit 5917679

Browse files
committed
Merge branch 'main' into main-github
2 parents 25f3e19 + 7c37f09 commit 5917679

12 files changed

Lines changed: 185 additions & 47 deletions

File tree

.vscode/terms.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pypa
3030
pyproject
3131
pytest
3232
sampleproject
33+
secho
3334
sendline
3435
setuptools
3536
SPHINXBUILD

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
prune template
22
prune */__pycache__
33
exclude copier.yaml
4+
graft src/init_python_project/template

Makefile

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,30 @@ DOC_EXAMPLES = docs/examples/mkdocs docs/examples/sphinx docs/examples/default d
1010
examples: ## build all published examples
1111
examples: $(PUBLISHED_EXAMPLES)
1212

13-
COPIER_ARGS?=--trust --vcs-ref=HEAD
14-
COPIER_DEFAULT_VALUES=-d "project_name=Sample Project" -d "package_name=sample_project"
15-
build/examples/%: COPIER_DEFAULT_VALUES += --defaults
13+
INIT_PYTHON_PROJECT_ARGS=--project-name="Sample Project"
1614
build/examples/%: EXAMPLE_DIR:=$@
17-
build/examples/github: COPIER_DEFAULT_VALUES+=-d user_name=jannismain -d remote=github -d remote_url=git@github.com:jannismain/python-project-template-example.git
18-
build/examples/gitlab%: COPIER_DEFAULT_VALUES+=-d user_name=mkj
19-
build/examples/gitlab_fhg: COPIER_DEFAULT_VALUES+= -d remote=gitlab-fhg -d remote_url=git@gitlab.cc-asp.fraunhofer.de:mkj/sample-project.git
20-
build/examples/gitlab_iis: COPIER_DEFAULT_VALUES+= -d remote=gitlab-iis -d remote_url=git@git01.iis.fhg.de:mkj/sample-project.git
21-
build/examples/gitlab_iis_sphinx: COPIER_DEFAULT_VALUES+= -d remote=gitlab-iis -d remote_url=git@git01.iis.fhg.de:mkj/sample-project-sphinx.git -d docs=sphinx
15+
build/examples/github: INIT_PYTHON_PROJECT_ARGS+=--user-name=jannismain --remote=github --remote-url=git@github.com:jannismain/python-project-template-example.git
16+
build/examples/gitlab%: INIT_PYTHON_PROJECT_ARGS+=--user-name mkj
17+
build/examples/gitlab_fhg: INIT_PYTHON_PROJECT_ARGS+=--remote=gitlab-fhg --remote-url=git@gitlab.cc-asp.fraunhofer.de:mkj/sample-project.git
18+
build/examples/gitlab_iis: INIT_PYTHON_PROJECT_ARGS+=--remote=gitlab-iis --remote-url=git@git01.iis.fhg.de:mkj/sample-project.git
19+
build/examples/gitlab_iis_sphinx: INIT_PYTHON_PROJECT_ARGS+=--remote=gitlab-iis --remote-url=git@git01.iis.fhg.de:mkj/sample-project-sphinx.git --docs=sphinx
2220

23-
$(PUBLISHED_EXAMPLES):
21+
$(PUBLISHED_EXAMPLES): uncopy-template copy-template
2422
@echo "Recreating '$@'..."
2523
@rm -rf "$@" && mkdir -p "$@"
26-
@copier copy ${COPIER_ARGS} ${COPIER_DEFAULT_VALUES} . "$@"
24+
init-python-project "$@" ${INIT_PYTHON_PROJECT_ARGS} --defaults --yes --verbose
2725
$(MAKE) example-setup EXAMPLE_DIR="$@"
2826

29-
docs/examples/mkdocs: COPIER_DEFAULT_VALUES+=-d docs=mkdocs
30-
docs/examples/sphinx: COPIER_DEFAULT_VALUES+=-d docs=sphinx
31-
docs/examples/minimal: COPIER_DEFAULT_VALUES+=-d docs=none -d use_precommit=False -d use_bumpversion=False
32-
docs/examples/full: COPIER_DEFAULT_VALUES+=-d docs=mkdocs -d use_precommit=True -d use_bumpversion=True
33-
docs/examples/gitlab: COPIER_DEFAULT_VALUES+=-d remote=gitlab-iis
34-
35-
$(DOC_EXAMPLES):
27+
docs/examples/mkdocs: INIT_PYTHON_PROJECT_ARGS+=--docs mkdocs
28+
docs/examples/sphinx: INIT_PYTHON_PROJECT_ARGS+=--docs sphinx
29+
docs/examples/minimal: INIT_PYTHON_PROJECT_ARGS+=--docs none --no-precommit --no-bumpversion
30+
docs/examples/full: INIT_PYTHON_PROJECT_ARGS+=--docs mkdocs --precommit --bumpversion
31+
docs/examples/gitlab: INIT_PYTHON_PROJECT_ARGS+=--docs mkdocs --precommit --bumpversion --remote gitlab-iis
32+
doc-examples: $(DOC_EXAMPLES)
33+
$(DOC_EXAMPLES): uncopy-template copy-template
3634
@echo "Recreating '$@'..."
3735
@rm -rf "$@" && mkdir -p "$@"
38-
@copier copy ${COPIER_ARGS} --defaults -d user_name=mkj ${COPIER_DEFAULT_VALUES} . "$@"
36+
init-python-project "$@" --user-name mkj ${INIT_PYTHON_PROJECT_ARGS} --defaults --yes --verbose
3937
@cd $@ &&\
4038
python -m venv .venv || echo "Couldn't setup virtual environment" &&\
4139
. .venv/bin/activate &&\
@@ -66,7 +64,7 @@ examples-clean: ## remove all published examples
6664

6765
build/example: ## build individual example for manual testing (will prompt for values!)
6866
rm -rf "$@"
69-
copier copy ${COPIER_ARGS} ${COPIER_DEFAULT_VALUES} . "$@"
67+
init-python-project "$@" ${INIT_PYTHON_PROJECT_ARGS}
7068
$(MAKE) example-setup EXAMPLE_DIR="$@"
7169

7270

@@ -124,8 +122,10 @@ install-build: build
124122
copy-template:
125123
@cp -r ${TEMPLATE_SRC} ${TEMPLATE_DEST}
126124
@cp copier.yaml ${PKGDIR}/.
127-
build-clean: ## remove build artifacts
128-
@rm -rf ${BUILDDIR} ${PKGDIR}/template ${PKGDIR}/copier.yaml
125+
uncopy-template:
126+
@rm -rf ${TEMPLATE_DEST} ${PKGDIR}/copier.yaml
127+
build-clean: uncopy-template ## remove build artifacts
128+
@rm -rf ${BUILDDIR}
129129

130130
.PHONY: release release-test release-tag release-pypi release-github
131131
release: release-test release-tag build release-pypi release-github

copier.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ package_name:
5151
5252
Finally, the package name is repeated across multiple configuration and documentation files.
5353
54-
use_precommit:
54+
precommit:
5555
type: bool
5656
default: true
5757
help: Use pre-commit to run checks on each commit?
@@ -64,7 +64,7 @@ use_precommit:
6464
Most formatters and some linters are able to fix issues automatically.
6565
So you can simply review the changes those tools made, stage them and commit again.
6666
67-
use_bumpversion:
67+
bumpversion:
6868
type: bool
6969
default: false
7070
help: Use bumpversion to manage semantic version across multiple files?
@@ -178,4 +178,4 @@ _tasks:
178178
- "rm -rf context"
179179
- "git init --initial-branch={{default_branch}}"
180180
- "git remote add origin {{remote_url}} || true"
181-
- "{% if use_precommit %}pre-commit install || echo 'Error during installation of pre-commit hooks. Is pre-commit installed?'{% endif %}"
181+
- "{% if precommit %}pre-commit install || echo 'Error during installation of pre-commit hooks. Is pre-commit installed?'{% endif %}"

docs/user-guide/getting-started.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
{{ includex('README.md', start_match='Prerequisites', end_match='<!-- usage-end -->')}}
22

3-
## Using [copier] directly
3+
??? note "Using [pipx]"
44

5-
```console
6-
copier copy --trust https://git01.iis.fhg.de/mkj/project-template.git my_new_project
7-
```
5+
```{.sh .copy}
6+
pipx run init-python-project
7+
```
88

9-
*Note: `--trust` is required because the template uses [tasks][] to setup your git repository for you.*
9+
[pipx]: https://pypa.github.io/pipx/
1010

11-
[tasks]: https://git01.iis.fhg.de/mkj/project-template/-/blob/main/copier.yaml
11+
??? note "Using [copier]"
12+
13+
The underlying template is built using [copier]. This means you can also use the copier template directly like this:
1214

13-
*Note: If you have [pipx][] installed (you should, it is good), you can simply use `pipx run copier` out of the box.*
15+
```{.sh .copy}
16+
copier copy --trust https://git01.iis.fhg.de/mkj/project-template.git my_new_project
17+
```
1418

19+
*Note: `--trust` is required because the template uses [tasks] to setup your git repository for you.*
20+
21+
[tasks]: https://git01.iis.fhg.de/mkj/project-template/-/blob/main/copier.yaml
1522
[copier]: https://github.com/copier-org/copier
16-
[pipx]: https://pypa.github.io/pipx/

src/init_python_project/cli.py

Lines changed: 132 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
import logging
12
import sys
3+
from enum import StrEnum
24
from pathlib import Path
5+
from subprocess import check_output
36
from typing import Annotated, Optional
47

8+
import typer
59
from copier import run_copy
610
from typer import Argument, Option, Typer, colors, confirm, style
711

@@ -16,18 +20,124 @@ def version_callback(value: bool) -> None:
1620
sys.exit(0)
1721

1822

23+
class DocumentationTool(StrEnum):
24+
"which documentation tool to use"
25+
mkdocs = "mkdocs"
26+
sphinx = "sphinx"
27+
none = "none"
28+
29+
30+
class DocumentationTemplate(StrEnum):
31+
"which documentation template to use"
32+
sphinx_fhg_iis = "sphinx-fhg-iis"
33+
builtin = "none"
34+
35+
36+
class RemotePlatform(StrEnum):
37+
"which remote platform to configure"
38+
github = "github"
39+
gitlab_fhg = "gitlab-fhg"
40+
gitlab_iis = "gitlab-iis"
41+
42+
43+
def CustomOptional(_type=bool, help="", custom_flag: str | list = None, **kwargs):
44+
if issubclass(_type, StrEnum):
45+
kwargs = {"case_sensitive": False, **kwargs}
46+
if not help:
47+
help = _type.__doc__
48+
49+
kwargs = {"show_default": False, "help": help, **kwargs}
50+
51+
if custom_flag is None:
52+
return Annotated[Optional[_type], Option(**kwargs)]
53+
54+
if isinstance(custom_flag, str):
55+
custom_flag = [custom_flag]
56+
return Annotated[Optional[_type], Option(*custom_flag, **kwargs)]
57+
58+
1959
@app.command(name="init-python-project")
2060
def cli(
61+
# data passed to the underlying copier template
2162
target_path: Path = Argument("new-project"),
63+
project_name: CustomOptional(str, "project name (title case with spaces)") = None,
64+
package_name: CustomOptional(str, "Python package name (lowercase with underscores)") = None,
65+
user_name: CustomOptional(str, "your user name") = None,
66+
docs: CustomOptional(DocumentationTool) = None,
67+
docs_template: CustomOptional(DocumentationTemplate) = None,
68+
remote: CustomOptional(RemotePlatform) = None,
69+
remote_url: CustomOptional(str, "ssh url where your repository will be hosted on") = None,
70+
precommit: CustomOptional(bool, "include pre-commit hooks") = None,
71+
bumpversion: CustomOptional(bool, "include bumpversion configuration") = None,
72+
# arguments that affect project creation
73+
defaults: Annotated[
74+
bool, Option("--defaults", "-d", help="automatically accept all default options")
75+
] = False,
76+
dry_run: Annotated[bool, Option("--dry-run", help="do not actually create project")] = False,
77+
always_confirm: Annotated[
78+
bool, Option("--yes", "-y", help="answer any confirmation request with yes")
79+
] = False,
2280
version: Annotated[
23-
Optional[bool], Option("--version", callback=version_callback, is_eager=True)
81+
Optional[bool],
82+
Option("--version", callback=version_callback, is_eager=True, help="show version and exit"),
83+
] = None,
84+
verbose: Annotated[
85+
Optional[bool],
86+
typer.Option(
87+
"--verbose",
88+
"-v",
89+
callback=lambda x: logging.basicConfig(
90+
level=logging.INFO if x else logging.WARN, format="%(message)s"
91+
),
92+
is_eager=True,
93+
help="show more information",
94+
),
95+
] = False,
96+
copier_args: Annotated[
97+
Optional[list[str]],
98+
typer.Option("--copier-arg", help="anything you want to pass to copier"),
2499
] = None,
25100
) -> None:
26-
"""Executes the CLI command to create a new project."""
27-
target_path.mkdir(exist_ok=True)
101+
"""Executes the CLI command to create a new project.
102+
103+
For a list of supported copier arguments, see
104+
https://copier.readthedocs.io/en/stable/reference/main/#copier.main.Worker.
105+
106+
Note that `src_path`, `dest_path`, `vcs_ref`, `data`, `defaults`, `user_defaults` and `unsafe`
107+
are already set by this command. Further, `--dry-run` corresponds to copier's `--pretend` and
108+
`--yes` implies copier's `--overwrite`.
109+
"""
110+
111+
if docs_template not in [None, "none"] and (
112+
docs is None or (docs is not None and not docs_template.value.startswith(docs.value))
113+
):
114+
typer.secho(
115+
f"Error: selected template ({docs_template}) not compatible "
116+
f"with documentation tool ({docs})",
117+
fg=colors.RED,
118+
err=True,
119+
)
120+
raise typer.Exit(1)
121+
122+
# cast enums to their values
123+
for option in "docs remote".split():
124+
if locals()[option] is not None:
125+
locals()[option] = locals()[option].value
126+
127+
# assemble values provided by the user
128+
data = {}
129+
for (
130+
option
131+
) in "project_name package_name user_name docs remote remote_url precommit bumpversion".split():
132+
value = locals()[option]
133+
if value is not None:
134+
logging.info("%s: %s", option, value)
135+
data[option] = value
136+
28137
if (
29138
target_path.is_dir()
30139
and any(target_path.iterdir())
140+
and not always_confirm
31141
and not confirm(
32142
style(
33143
f"Target directory '{target_path}' is not empty! Continue?",
@@ -37,10 +147,29 @@ def cli(
37147
):
38148
sys.exit(1)
39149

150+
# parse copier args
151+
copier_args = {
152+
k.replace("--", "").replace("-", "_"): v
153+
for k, v in (
154+
arg.split("=") if "=" in arg else arg.split() if " " in arg else (arg, True)
155+
for arg in (copier_args or [])
156+
)
157+
}
158+
40159
run_copy(
41160
src_path=str(Path(__file__).parent.absolute()),
42161
dst_path=target_path,
43162
unsafe=True,
163+
data=data,
164+
user_defaults=dict(
165+
user_name=check_output(["whoami"]).decode().strip() if user_name is None else user_name,
166+
project_name=target_path.name.replace("-", " ").replace("_", " ").title(),
167+
),
168+
defaults=defaults,
169+
overwrite=always_confirm,
170+
pretend=dry_run or copier_args.pop("pretend", False),
171+
quiet=True,
172+
**copier_args,
44173
)
45174

46175

template/Makefile.jinja

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.PHONY: install-dev
22
install-dev: ## install project including all development dependencies
3-
pip install -e .[test,dev]
4-
pip install -r docs/requirements.txt
3+
pip install -e .[test,dev]{% if docs != 'none' %}
4+
pip install -r docs/requirements.txt{% endif %}
55

66
.PHONY: maintainability
77
maintainability: ## run maintainability checks

template/pyproject.toml.jinja

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ dependencies = ["click"]
7070
# Similar to `dependencies` above, these must be valid existing
7171
# projects.
7272
[project.optional-dependencies]
73-
dev = ["black", "radon", "ruff"{% if use_bumpversion %}, "bump2version"{% endif %}]
73+
dev = ["black", "radon", "ruff"{% if bumpversion %}, "bump2version"{% endif %}]
7474
test = ["pytest", "pytest-cov", "coverage[toml]"]
7575

7676
# The following would provide a command line executable which executes

template/{% if use_bumpversion %}.bumpversion.cfg{% endif %}.jinja renamed to template/{% if bumpversion %}.bumpversion.cfg{% endif %}.jinja

File renamed without changes.

template/{% if docs == 'mkdocs' %}docs{% endif %}/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mkdocs
12
mkdocs-material
23
mkdocstrings[python]
34
mkdocs-git-revision-date-localized-plugin

0 commit comments

Comments
 (0)