Skip to content

Commit 8b84c83

Browse files
authored
Merge pull request #96 from csdms/mcflugen/replace-cookiecutter
Replace cookiecutter
2 parents 791e0b2 + cfad50e commit 8b84c83

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+925
-1185
lines changed

.gitignore

Lines changed: 5 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,8 @@
1-
# Byte-compiled / optimized / DLL files
2-
__pycache__/
3-
*.py[cod]
4-
*$py.class
5-
6-
# C extensions
7-
*.so
8-
9-
# Distribution / packaging
10-
.Python
11-
env/
12-
build/
13-
develop-eggs/
14-
dist/
15-
downloads/
16-
eggs/
17-
.eggs/
18-
lib/
19-
lib64/
20-
parts/
21-
sdist/
22-
var/
23-
wheels/
241
*.egg-info/
25-
.installed.cfg
26-
*.egg
27-
28-
# PyInstaller
29-
# Usually these files are written by a python script from a template
30-
# before PyInstaller builds the exe, so as to inject date/other infos into it.
31-
*.manifest
32-
*.spec
33-
34-
# Installer logs
35-
pip-log.txt
36-
pip-delete-this-directory.txt
37-
38-
# Unit test / coverage reports
39-
htmlcov/
40-
.tox/
2+
*.py[cod]
413
.coverage
42-
.coverage.*
43-
.cache
44-
nosetests.xml
45-
coverage.xml
46-
*.cover
47-
.hypothesis/
48-
49-
# Translations
50-
*.mo
51-
*.pot
52-
53-
# Django stuff:
54-
*.log
55-
local_settings.py
56-
57-
# Flask stuff:
58-
instance/
59-
.webassets-cache
60-
61-
# Scrapy stuff:
62-
.scrapy
63-
64-
# Sphinx documentation
65-
docs/_build/
66-
67-
# PyBuilder
68-
target/
69-
70-
# Jupyter Notebook
71-
.ipynb_checkpoints
72-
73-
# pyenv
74-
.python-version
75-
76-
# celery beat schedule file
77-
celerybeat-schedule
78-
79-
# SageMath parsed files
80-
*.sage.py
81-
82-
# dotenv
83-
.env
84-
85-
# virtualenv
86-
.venv
87-
venv/
88-
ENV/
89-
90-
# Spyder project settings
91-
.spyderproject
92-
.spyproject
93-
94-
# Rope project settings
95-
.ropeproject
96-
97-
# mkdocs documentation
98-
/site
99-
100-
# mypy
101-
.mypy_cache/
102-
103-
# nox virtual envs
1044
.nox/
5+
__pycache__/
6+
build/
7+
dist/
8+
docs/source/api/babelizer*rst

.pre-commit-config.yaml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/psf/black
3-
rev: 24.2.0
3+
rev: 24.3.0
44
hooks:
55
- id: black
66
name: black
@@ -35,7 +35,7 @@ repos:
3535
exclude: ^babelizer/data
3636

3737
- repo: https://github.com/asottile/pyupgrade
38-
rev: v3.15.1
38+
rev: v3.15.2
3939
hooks:
4040
- id: pyupgrade
4141
args: [--py310-plus]
@@ -65,9 +65,18 @@ repos:
6565
- id: end-of-file-fixer
6666
- id: forbid-new-submodules
6767
- id: trailing-whitespace
68+
- id: name-tests-test
69+
exclude: ^external
70+
- id: file-contents-sorter
71+
files: |
72+
(?x)^(
73+
requirements(-\w+)?.(in|txt)|
74+
external/requirements(-\w+)?.(in|txt)|
75+
.gitignore
76+
)
6877
6978
- repo: https://github.com/pre-commit/mirrors-mypy
70-
rev: v1.8.0
79+
rev: v1.9.0
7180
hooks:
7281
- id: mypy
7382
additional_dependencies: [types-all]

MANIFEST.in

Lines changed: 0 additions & 19 deletions
This file was deleted.

babelizer/_cookiecutter.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from __future__ import annotations
2+
3+
import os
4+
from collections.abc import Iterable
5+
from datetime import datetime
6+
from typing import Any
7+
8+
from jinja2 import Environment
9+
from jinja2 import FileSystemLoader
10+
from jinja2 import StrictUndefined
11+
from jinja2 import Template
12+
13+
from babelizer._datadir import get_template_dir
14+
from babelizer._post_hook import run
15+
from babelizer._utils import as_cwd
16+
17+
18+
def cookiecutter(
19+
template: str,
20+
context: dict[str, Any] | None = None,
21+
output_dir: str = ".",
22+
) -> None:
23+
if context is None:
24+
context = {}
25+
env = babelizer_environment(template)
26+
27+
def datetime_format(value: datetime, format_: str = "%Y-%M-%D") -> str:
28+
return value.strftime(format_)
29+
30+
env.filters["datetimeformat"] = datetime_format
31+
32+
for dirpath, _dirnames, filenames in os.walk(template):
33+
rel_path = os.path.relpath(dirpath, template)
34+
target_dir = os.path.join(output_dir, render_path(rel_path, context))
35+
36+
if not os.path.exists(target_dir):
37+
os.makedirs(target_dir)
38+
39+
for filename in filenames:
40+
target_path = os.path.join(target_dir, render_path(filename, context))
41+
42+
with open(target_path, "w") as fp:
43+
fp.write(
44+
env.get_template(os.path.join(rel_path, filename)).render(**context)
45+
)
46+
47+
with as_cwd(output_dir):
48+
run(context)
49+
50+
51+
def babelizer_environment(template: str | None = None) -> Environment:
52+
if template is None:
53+
template = get_template_dir()
54+
55+
return Environment(loader=FileSystemLoader(template), undefined=StrictUndefined)
56+
57+
58+
def render_path(
59+
path: str,
60+
context: dict[str, Any],
61+
remove_extension: Iterable[str] = (".jinja", ".jinja2", ".j2"),
62+
) -> str:
63+
"""Render a path as though it were a jinja template.
64+
65+
Parameters
66+
----------
67+
path : str
68+
A path.
69+
context : dict
70+
Context to use for substitution.
71+
remove_extension : iterable of str, optional
72+
If the provided path ends with one of these exensions,
73+
the extension will be removed from the rendered path.
74+
75+
Examples
76+
--------
77+
>>> from babelizer._cookiecutter import render_path
78+
>>> render_path("{{foo}}.py", {"foo": "bar"})
79+
'bar.py'
80+
>>> render_path("{{foo}}.py.jinja", {"foo": "bar"})
81+
'bar.py'
82+
>>> render_path("bar.py.j2", {"foo": "bar"})
83+
'bar.py'
84+
>>> render_path("{{bar}}.py.jinja", {"foo": "bar"})
85+
Traceback (most recent call last):
86+
...
87+
jinja2.exceptions.UndefinedError: 'bar' is undefined
88+
"""
89+
rendered_path = Template(path, undefined=StrictUndefined).render(**context)
90+
91+
root, ext = os.path.splitext(rendered_path)
92+
return rendered_path if ext not in remove_extension else root

babelizer/_datadir.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@
1010

1111
def get_datadir() -> str:
1212
return str(importlib_resources.files("babelizer") / "data")
13+
14+
15+
def get_template_dir() -> str:
16+
return str(importlib_resources.files("babelizer") / "data" / "templates")

babelizer/_files/bmi_py.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,30 @@
55
from typing import Any
66

77

8-
def render(plugin_metadata: Mapping[str, Any]) -> str:
8+
def render(context: Mapping[str, Any]) -> str:
99
"""Render _bmi.py."""
10-
languages = {library["language"] for library in plugin_metadata["library"].values()}
10+
languages = {library["language"] for library in context["library"].values()}
1111
assert len(languages) == 1
1212
language = languages.pop()
1313

1414
if language == "python":
15-
return _render_bmi_py(plugin_metadata)
15+
return _render_bmi_py(context)
1616
else:
17-
return _render_bmi_c(plugin_metadata)
17+
return _render_bmi_c(context)
1818

1919

20-
def _render_bmi_c(plugin_metadata: Mapping[str, Any]) -> str:
20+
def _render_bmi_c(context: Mapping[str, Any]) -> str:
2121
"""Render _bmi.py for a non-python library."""
22-
languages = [library["language"] for library in plugin_metadata["library"].values()]
22+
languages = [library["language"] for library in context["library"].values()]
2323
language = languages[0]
2424
assert language in ("c", "c++", "fortran")
2525

2626
imports = [
27-
f"from {plugin_metadata['package']['name']}.lib import {cls}"
28-
for cls in plugin_metadata["library"]
27+
f"from {context['package']['name']}.lib import {cls}"
28+
for cls in context["library"]
2929
]
3030

31-
names = [f" {cls!r},".replace("'", '"') for cls in plugin_metadata["library"]]
31+
names = [f" {cls!r},".replace("'", '"') for cls in context["library"]]
3232

3333
return f"""\
3434
{os.linesep.join(sorted(imports))}
@@ -39,9 +39,9 @@ def _render_bmi_c(plugin_metadata: Mapping[str, Any]) -> str:
3939
"""
4040

4141

42-
def _render_bmi_py(plugin_metadata: Mapping[str, Any]) -> str:
42+
def _render_bmi_py(context: Mapping[str, Any]) -> str:
4343
"""Render _bmi.py for a python library."""
44-
languages = [library["language"] for library in plugin_metadata["library"].values()]
44+
languages = [library["language"] for library in context["library"].values()]
4545
language = languages[0]
4646
assert language == "python"
4747

@@ -56,7 +56,7 @@ def _render_bmi_py(plugin_metadata: Mapping[str, Any]) -> str:
5656

5757
imports = [
5858
f"from {component['library']} import {component['entry_point']} as {cls}"
59-
for cls, component in plugin_metadata["library"].items()
59+
for cls, component in context["library"].items()
6060
]
6161

6262
rename = [
@@ -66,10 +66,10 @@ def _render_bmi_py(plugin_metadata: Mapping[str, Any]) -> str:
6666
""".replace(
6767
"'", '"'
6868
)
69-
for cls in plugin_metadata["library"]
69+
for cls in context["library"]
7070
]
7171

72-
names = [f" {cls!r},".replace("'", '"') for cls in plugin_metadata["library"]]
72+
names = [f" {cls!r},".replace("'", '"') for cls in context["library"]]
7373

7474
return f"""\
7575
{header}

babelizer/_files/gitignore.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
from typing import Any
66

77

8-
def render(plugin_metadata: Mapping[str, Any]) -> str:
8+
def render(context: Mapping[str, Any]) -> str:
99
"""Render a .gitignore file."""
10-
package_name = plugin_metadata["package"]["name"]
10+
package_name = context["package"]["name"]
1111

12-
languages = {library["language"] for library in plugin_metadata["library"].values()}
12+
languages = {library["language"] for library in context["library"].values()}
1313
ignore = {
1414
"*.egg-info/",
1515
"*.py[cod]",
@@ -22,7 +22,7 @@ def render(plugin_metadata: Mapping[str, Any]) -> str:
2222

2323
if "python" not in languages:
2424
ignore |= {"*.o", "*.so"} | {
25-
f"{package_name}/lib/{cls.lower()}.c" for cls in plugin_metadata["library"]
25+
f"{package_name}/lib/{cls.lower()}.c" for cls in context["library"]
2626
}
2727

2828
if "fortran" in languages:

babelizer/_files/init_py.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,14 @@
55
from typing import Any
66

77

8-
def render(plugin_metadata: Mapping[str, Any]) -> str:
8+
def render(context: Mapping[str, Any]) -> str:
99
"""Render __init__.py."""
10-
package_name = plugin_metadata["package"]["name"]
10+
package_name = context["package"]["name"]
1111

1212
imports = [f"from {package_name}._version import __version__"]
13-
imports += [
14-
f"from {package_name}._bmi import {cls}" for cls in plugin_metadata["library"]
15-
]
13+
imports += [f"from {package_name}._bmi import {cls}" for cls in context["library"]]
1614

17-
names = [f" {cls!r},".replace("'", '"') for cls in plugin_metadata["library"]]
15+
names = [f" {cls!r},".replace("'", '"') for cls in context["library"]]
1816

1917
return f"""\
2018
{os.linesep.join(sorted(imports))}

0 commit comments

Comments
 (0)