Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace cookiecutter #96

Merged
merged 38 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b295751
add _cookiecutter module to replace cookiecutter functionality
mcflugen Mar 23, 2024
34fdc2d
replace cookiecutter's now extension for the current date
mcflugen Mar 23, 2024
026de99
fix an annotation
mcflugen Mar 23, 2024
48453de
move as_cwd to utils module
mcflugen Mar 23, 2024
205c14e
adjust location of template down on folder
mcflugen Mar 23, 2024
6e23709
use replacement cookiecutter to render files
mcflugen Mar 23, 2024
4918faf
add a post-hook function to run after rendering files
mcflugen Mar 23, 2024
260ae7e
add some verbosity
mcflugen Mar 24, 2024
2502c3b
remove .jinja extension from templates
mcflugen Mar 24, 2024
5a248b2
add .jinja extension to python template files
mcflugen Mar 24, 2024
54b354d
add package_version to context
mcflugen Mar 24, 2024
3c86843
remove cookiecutter files from the data dir; rename package data dir
mcflugen Mar 31, 2024
d22da6a
remove cookiecutter context from jinja templates
mcflugen Mar 31, 2024
f061df8
remove cookiecutter context from metadata
mcflugen Mar 31, 2024
d9b6aee
remove calls to cookiecutter package
mcflugen Mar 31, 2024
9bdceef
remove cookiecutter dependency
mcflugen Mar 31, 2024
f6727e5
remove prettify_python and black, isort dependencies
mcflugen Mar 31, 2024
5c4f3b2
add get_template_dir function
mcflugen Apr 1, 2024
73fc90c
raise error for undefined variables in templates
mcflugen Apr 1, 2024
a2232ab
clean up variable names in templates
mcflugen Apr 1, 2024
57a9353
better error message for existing output dir
mcflugen Apr 1, 2024
3b10450
make utils module private
mcflugen Apr 2, 2024
1eb959a
clean up cookiecutter; add babelizer_environment function
mcflugen Apr 2, 2024
2b63d93
add news fragment
mcflugen Apr 2, 2024
768dd09
remove the manifest from the templates
mcflugen Apr 3, 2024
70347d9
add setuptools package-data and version for python projects
mcflugen Apr 3, 2024
ffa804c
remove the babelizer's manifest
mcflugen Apr 3, 2024
cfc62d7
recusively include babelizer package data
mcflugen Apr 3, 2024
36a1218
remove the docs Makefile
mcflugen Apr 3, 2024
f7f8af2
more clean up; move parse_entry_point into _utils.py
mcflugen Apr 4, 2024
c4a8100
update requirements files
mcflugen Apr 4, 2024
2824e9e
clean up .gitignore; add a couple linters
mcflugen Apr 4, 2024
b669578
fix parse_entry_point doctest
mcflugen Apr 4, 2024
4f11dd1
update the template pre-commit config file
mcflugen Apr 4, 2024
c10fa1b
remove unused _norm_os function
mcflugen Apr 4, 2024
efbcc9c
update pre-commit config file
mcflugen Apr 4, 2024
be33883
move validate_dict into _utils as validate_dict_keys
mcflugen Apr 4, 2024
cfad50e
changed BabelMetadata to BabelConfig
mcflugen Apr 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 5 additions & 101 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,104 +1,8 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
*.py[cod]
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# dotenv
.env

# virtualenv
.venv
venv/
ENV/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

# nox virtual envs
.nox/
__pycache__/
build/
dist/
docs/source/api/babelizer*rst
15 changes: 12 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/psf/black
rev: 24.2.0
rev: 24.3.0
hooks:
- id: black
name: black
Expand Down Expand Up @@ -35,7 +35,7 @@ repos:
exclude: ^babelizer/data

- repo: https://github.com/asottile/pyupgrade
rev: v3.15.1
rev: v3.15.2
hooks:
- id: pyupgrade
args: [--py310-plus]
Expand Down Expand Up @@ -65,9 +65,18 @@ repos:
- id: end-of-file-fixer
- id: forbid-new-submodules
- id: trailing-whitespace
- id: name-tests-test
exclude: ^external
- id: file-contents-sorter
files: |
(?x)^(
requirements(-\w+)?.(in|txt)|
external/requirements(-\w+)?.(in|txt)|
.gitignore
)

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
rev: v1.9.0
hooks:
- id: mypy
additional_dependencies: [types-all]
Expand Down
19 changes: 0 additions & 19 deletions MANIFEST.in

This file was deleted.

92 changes: 92 additions & 0 deletions babelizer/_cookiecutter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from __future__ import annotations

import os
from collections.abc import Iterable
from datetime import datetime
from typing import Any

from jinja2 import Environment
from jinja2 import FileSystemLoader
from jinja2 import StrictUndefined
from jinja2 import Template

from babelizer._datadir import get_template_dir
from babelizer._post_hook import run
from babelizer._utils import as_cwd


def cookiecutter(
template: str,
context: dict[str, Any] | None = None,
output_dir: str = ".",
) -> None:
if context is None:
context = {}
env = babelizer_environment(template)

def datetime_format(value: datetime, format_: str = "%Y-%M-%D") -> str:
return value.strftime(format_)

env.filters["datetimeformat"] = datetime_format

for dirpath, _dirnames, filenames in os.walk(template):
rel_path = os.path.relpath(dirpath, template)
target_dir = os.path.join(output_dir, render_path(rel_path, context))

if not os.path.exists(target_dir):
os.makedirs(target_dir)

for filename in filenames:
target_path = os.path.join(target_dir, render_path(filename, context))

with open(target_path, "w") as fp:
fp.write(
env.get_template(os.path.join(rel_path, filename)).render(**context)
)

with as_cwd(output_dir):
run(context)


def babelizer_environment(template: str | None = None) -> Environment:
if template is None:
template = get_template_dir()

return Environment(loader=FileSystemLoader(template), undefined=StrictUndefined)


def render_path(
path: str,
context: dict[str, Any],
remove_extension: Iterable[str] = (".jinja", ".jinja2", ".j2"),
) -> str:
"""Render a path as though it were a jinja template.

Parameters
----------
path : str
A path.
context : dict
Context to use for substitution.
remove_extension : iterable of str, optional
If the provided path ends with one of these exensions,
the extension will be removed from the rendered path.

Examples
--------
>>> from babelizer._cookiecutter import render_path
>>> render_path("{{foo}}.py", {"foo": "bar"})
'bar.py'
>>> render_path("{{foo}}.py.jinja", {"foo": "bar"})
'bar.py'
>>> render_path("bar.py.j2", {"foo": "bar"})
'bar.py'
>>> render_path("{{bar}}.py.jinja", {"foo": "bar"})
Traceback (most recent call last):
...
jinja2.exceptions.UndefinedError: 'bar' is undefined
"""
rendered_path = Template(path, undefined=StrictUndefined).render(**context)

root, ext = os.path.splitext(rendered_path)
return rendered_path if ext not in remove_extension else root
4 changes: 4 additions & 0 deletions babelizer/_datadir.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@

def get_datadir() -> str:
return str(importlib_resources.files("babelizer") / "data")


def get_template_dir() -> str:
return str(importlib_resources.files("babelizer") / "data" / "templates")
28 changes: 14 additions & 14 deletions babelizer/_files/bmi_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@
from typing import Any


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

if language == "python":
return _render_bmi_py(plugin_metadata)
return _render_bmi_py(context)
else:
return _render_bmi_c(plugin_metadata)
return _render_bmi_c(context)


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

imports = [
f"from {plugin_metadata['package']['name']}.lib import {cls}"
for cls in plugin_metadata["library"]
f"from {context['package']['name']}.lib import {cls}"
for cls in context["library"]
]

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

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


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

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

imports = [
f"from {component['library']} import {component['entry_point']} as {cls}"
for cls, component in plugin_metadata["library"].items()
for cls, component in context["library"].items()
]

rename = [
Expand All @@ -66,10 +66,10 @@ def _render_bmi_py(plugin_metadata: Mapping[str, Any]) -> str:
""".replace(
"'", '"'
)
for cls in plugin_metadata["library"]
for cls in context["library"]
]

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

return f"""\
{header}
Expand Down
8 changes: 4 additions & 4 deletions babelizer/_files/gitignore.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
from typing import Any


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

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

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

if "fortran" in languages:
Expand Down
10 changes: 4 additions & 6 deletions babelizer/_files/init_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@
from typing import Any


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

imports = [f"from {package_name}._version import __version__"]
imports += [
f"from {package_name}._bmi import {cls}" for cls in plugin_metadata["library"]
]
imports += [f"from {package_name}._bmi import {cls}" for cls in context["library"]]

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

return f"""\
{os.linesep.join(sorted(imports))}
Expand Down
Loading
Loading