From b2957515552205d8e55c142d8d17e2c9df1a9b66 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 22 Mar 2024 20:41:41 -0600 Subject: [PATCH 01/38] add _cookiecutter module to replace cookiecutter functionality --- babelizer/_cookiecutter.py | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 babelizer/_cookiecutter.py diff --git a/babelizer/_cookiecutter.py b/babelizer/_cookiecutter.py new file mode 100644 index 0000000..b1dd462 --- /dev/null +++ b/babelizer/_cookiecutter.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +import os +from datetime import datetime +from typing import Any + +from jinja2 import Environment +from jinja2 import FileSystemLoader +from jinja2 import Template + +from babelizer._post_hook import run +from babelizer.utils import as_cwd + + +def cookiecutter( + template: str, + extra_context: dict[str, Any] | None = None, + output_dir: str = ".", + no_input: bool = True, + overwrite_if_exists: bool = False, +) -> None: + if extra_context is None: + extra_context = {} + env = Environment(loader=FileSystemLoader(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, extra_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, extra_context)) + + with open(target_path, "w") as fp: + fp.write( + env.get_template(os.path.join(rel_path, filename)).render( + **extra_context + ) + ) + + with as_cwd(output_dir): + run(extra_context) + + +def render_path(path: str, context: dict[str, Any]) -> str: + return Template(path).render(**context) From 34fdc2dcda86eb6926ed9f453dcfb9a53dca5ebe Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 22 Mar 2024 20:42:55 -0600 Subject: [PATCH 02/38] replace cookiecutter's now extension for the current date --- babelizer/data/{{cookiecutter.package_name}}/CHANGES.rst | 2 +- babelizer/data/{{cookiecutter.package_name}}/docs/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/babelizer/data/{{cookiecutter.package_name}}/CHANGES.rst b/babelizer/data/{{cookiecutter.package_name}}/CHANGES.rst index e6e128b..0f28d3d 100644 --- a/babelizer/data/{{cookiecutter.package_name}}/CHANGES.rst +++ b/babelizer/data/{{cookiecutter.package_name}}/CHANGES.rst @@ -6,7 +6,7 @@ Release Notes .. towncrier release notes start -0.1.0 ({% now 'local', '%Y-%m-%d' %}) +0.1.0 ({{ cookiecutter.now|datetimeformat('%Y-%m-%d') }}) ------------------ - Initial release diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/conf.py b/babelizer/data/{{cookiecutter.package_name}}/docs/conf.py index 57c39b2..5679bab 100644 --- a/babelizer/data/{{cookiecutter.package_name}}/docs/conf.py +++ b/babelizer/data/{{cookiecutter.package_name}}/docs/conf.py @@ -59,7 +59,7 @@ # General information about the project. project = "{{ cookiecutter.package_name }}" -copyright = "{% now 'local', '%Y' %}, {{ cookiecutter.info.full_name }}" +copyright = "{{ cookiecutter.now|datetimeformat('%Y') }}, {{ cookiecutter.info.full_name }}" author = "{{ cookiecutter.info.full_name }}" # The version info for the project you're documenting, acts as replacement From 026de996de51accdb98b9bac3415d3b52f898db9 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 22 Mar 2024 20:43:41 -0600 Subject: [PATCH 03/38] fix an annotation --- babelizer/metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/babelizer/metadata.py b/babelizer/metadata.py index c407cae..659f9ef 100644 --- a/babelizer/metadata.py +++ b/babelizer/metadata.py @@ -131,7 +131,7 @@ def __init__( self._meta = BabelMetadata.norm(config) - def __getitem__(self, key: str) -> str: + def __getitem__(self, key: str) -> dict[str, Any]: return self._meta[key] def __iter__(self) -> Generator[str, None, None]: From 48453dea020498893877ad756a4a71bbffc7d6ac Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 22 Mar 2024 20:44:12 -0600 Subject: [PATCH 04/38] move as_cwd to utils module --- babelizer/utils.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/babelizer/utils.py b/babelizer/utils.py index 887c038..2bfbfff 100644 --- a/babelizer/utils.py +++ b/babelizer/utils.py @@ -2,6 +2,7 @@ from __future__ import annotations +import os import pathlib import subprocess import sys @@ -90,3 +91,18 @@ def save_files(files: Iterable[str]) -> Generator[dict[str, str], None, None]: for file_ in contents: with open(file_, "w") as fp: fp.write(contents[file_]) + + +@contextmanager +def as_cwd(path: str) -> Generator[None, None, None]: + """Change directory context. + + Parameters + ---------- + path : str + Path-like object to a directory. + """ + prev_cwd = os.getcwd() + os.chdir(path) + yield + os.chdir(prev_cwd) From 205c14ebfd826aefb3e08b659562275ffa6c3289 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 22 Mar 2024 20:44:45 -0600 Subject: [PATCH 05/38] adjust location of template down on folder --- babelizer/cli.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/babelizer/cli.py b/babelizer/cli.py index 42b49ac..24fc0be 100644 --- a/babelizer/cli.py +++ b/babelizer/cli.py @@ -83,8 +83,7 @@ def init( META is babelizer configuration information, usually saved to a file. """ - output = "." - template = template or get_datadir() + template = template or os.path.join(get_datadir(), "{{cookiecutter.package_name}}") if not quiet: out(f"reading template from {template}") @@ -95,6 +94,8 @@ def init( except (ScanError, ValidationError) as error: raise BabelizerAbort(str(error)) + output = babel_metadata["package"]["name"] + try: new_folder = render( babel_metadata, @@ -111,6 +112,7 @@ def init( "Don't forget to drop model metadata files into" f" {os.path.join(new_folder, 'meta')}" ) + repo = git.Repo(new_folder) repo.git.add("--all") repo.index.commit("Initial commit") From 6e23709f8dc6a60ac984f8ee515e4452efda1d15 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 22 Mar 2024 20:45:44 -0600 Subject: [PATCH 06/38] use replacement cookiecutter to render files --- babelizer/render.py | 63 +++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/babelizer/render.py b/babelizer/render.py index bd2e8d5..0873b20 100644 --- a/babelizer/render.py +++ b/babelizer/render.py @@ -2,10 +2,9 @@ from __future__ import annotations -import contextlib +import datetime import os import sys -from collections.abc import Generator from typing import Any import git @@ -44,6 +43,51 @@ def render( clobber: bool = False, version: str = "0.1", make_pretty: bool = False, +) -> str: + from babelizer._cookiecutter import cookiecutter + + if template is None: + template = get_datadir() + + context = { + "cookiecutter": { + "files": { + "_bmi.py": render_bmi(plugin_metadata), + "__init__.py": render_init(plugin_metadata), + "lib/__init__.py": render_lib_init(plugin_metadata), + ".gitignore": render_gitignore(plugin_metadata), + "LICENSE.rst": render_license(plugin_metadata), + }, + "now": datetime.datetime.now(), + } + | plugin_metadata.as_cookiecutter_context() + } + + cookiecutter( + template, + extra_context=context, + output_dir=output, + no_input=True, + overwrite_if_exists=clobber, + ) + + path = os.path.realpath(output) + + with open(os.path.join(path, "babel.toml"), "w") as fp: + plugin_metadata.dump(fp, fmt="toml") + + git.Repo.init(path) + + return path + + +def render_with_cookiecutter( + plugin_metadata: BabelMetadata, + output: str, + template: str | None = None, + clobber: bool = False, + version: str = "0.1", + make_pretty: bool = False, ) -> str: """Generate a babelized library. @@ -153,21 +197,6 @@ def render_plugin_repo( return path -@contextlib.contextmanager -def as_cwd(path: str) -> Generator[None, None, None]: - """Change directory context. - - Parameters - ---------- - path : str - Path-like object to a directory. - """ - prev_cwd = os.getcwd() - os.chdir(path) - yield - os.chdir(prev_cwd) - - def blacken_file(filepath: str) -> None: """Format a Python file with ``black``. From 4918fafe23781ba69fba34f7d7e953f312196b62 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 22 Mar 2024 20:50:25 -0600 Subject: [PATCH 07/38] add a post-hook function to run after rendering files --- babelizer/_post_hook.py | 158 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 babelizer/_post_hook.py diff --git a/babelizer/_post_hook.py b/babelizer/_post_hook.py new file mode 100644 index 0000000..11f9d07 --- /dev/null +++ b/babelizer/_post_hook.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +import errno +import os +import re +from collections import defaultdict +from collections.abc import Iterable +from pathlib import Path +from typing import Any + +from logoizer import logoize + +# PROJECT_DIRECTORY = Path.cwd().resolve() + + +# def remove_file(filepath: Path) -> None: +# filepath.unlink(filepath) +# # (PROJECT_DIRECTORY / filepath).unlink(filepath) + + +# def remove_folder(folderpath): +# shutil.rmtree(PROJECT_DIRECTORY / folderpath) + + +# def make_folder(folderpath): +# try: +# # (PROJECT_DIRECTORY / folderpath).mkdir(parents=True, exist_ok=True) +# folderpath.mkdir(parents=True, exist_ok=True) +# except OSError: +# pass + + +def clean_folder(folderpath: Path, keep: Iterable[str | Path] = ()) -> None: + keep = {str((folderpath / path).resolve()) for path in keep} + # if keep: + # keep = set([str((folderpath / path).resolve()) for path in keep]) + # else: + # keep = set() + + # folderpath = PROJECT_DIRECTORY / folderpath + for fname in folderpath.glob("*"): + if not fname.is_dir() and str(fname.resolve()) not in keep: + fname.unlink() + + try: + folderpath.rmdir() + except OSError as err: + if err.errno != errno.ENOTEMPTY: + raise + + +def split_file(filepath: Path, include_preamble: bool = False) -> set[str]: + filepath = Path(filepath) + SPLIT_START_REGEX = re.compile(r"\s*#\s*start:\s*(?P\S+)\s*") + + files = defaultdict(list) + fname = "preamble" + with open(filepath) as fp: + for line in fp: + m = SPLIT_START_REGEX.match(line) + if m: + fname = m["fname"] + files[fname].append(line) + + preamble = files.pop("preamble") + folderpath = filepath.parent + for name, contents in files.items(): + with open(folderpath / name, "w") as fp: + if include_preamble: + fp.write("".join(preamble)) + print("".join(contents).strip(), file=fp) + # fp.write("".join(contents).strip()) + + return set(files) + + +def write_api_yaml(folderpath: Path, **kwds: str) -> Path: + # make_folder(folderpath) + os.makedirs(folderpath, exist_ok=True) + + # api_yaml = PROJECT_DIRECTORY / folderpath / "api.yaml" + api_yaml = folderpath / "api.yaml" + contents = """\ +name: {package_name} +language: {language} +package: {package_name} +class: {plugin_class} +""".format( + **kwds + ) + with open(api_yaml, "w") as fp: + fp.write(contents) + + return api_yaml + + +def remove_trailing_whitespace(path: str | Path) -> None: + with open(path) as fp: + lines = [line.rstrip() for line in fp] + with open(path, "w") as fp: + print(os.linesep.join(lines), file=fp) + + +def run(context: dict[str, Any]) -> None: + PROJECT_DIRECTORY = Path.cwd().resolve() + + package_name = context["cookiecutter"]["package_name"] + language = context["cookiecutter"]["language"] + + LIB_DIRECTORY = PROJECT_DIRECTORY / Path(package_name, "lib") + + keep = set() + + static_dir = PROJECT_DIRECTORY / "docs" / "_static" + # make_folder(PROJECT_DIRECTORY / static_dir) + os.makedirs(PROJECT_DIRECTORY / static_dir, exist_ok=True) + + logoize(package_name, static_dir / "logo-light.svg", light=True) + logoize(package_name, static_dir / "logo-dark.svg", light=False) + + remove_trailing_whitespace(static_dir / "logo-dark.svg") + remove_trailing_whitespace(static_dir / "logo-light.svg") + + if language == "c": + keep |= {"__init__.py", "bmi.c", "bmi.h"} + keep |= split_file(LIB_DIRECTORY / "_c.pyx", include_preamble=True) + elif language == "c++": + keep |= {"__init__.py", "bmi.hxx"} + keep |= split_file(LIB_DIRECTORY / "_cxx.pyx", include_preamble=True) + elif language == "fortran": + keep |= { + "__init__.py", + "bmi.f90", + "bmi_interoperability.f90", + "bmi_interoperability.h", + } + keep |= split_file(LIB_DIRECTORY / "_fortran.pyx", include_preamble=True) + + clean_folder(LIB_DIRECTORY, keep=keep) + + # if "Not open source" == "{{ cookiecutter.open_source_license }}": + # remove_file("LICENSE") + + if language == "python": + os.remove(PROJECT_DIRECTORY / "meson.build") + + datadir = Path("meta") + package_datadir = Path(package_name) / "data" + if not package_datadir.exists(): + package_datadir.symlink_to(".." / datadir, target_is_directory=True) + + for babelized_class in context["cookiecutter"]["components"]: + write_api_yaml( + PROJECT_DIRECTORY / datadir / babelized_class, + language=language, + plugin_class=babelized_class, + package_name=package_name, + ) From 260ae7eb41d3c4f16ebf6cc4c565f94f6b3e48e8 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sun, 24 Mar 2024 09:42:43 -0600 Subject: [PATCH 08/38] add some verbosity --- babelizer/_cookiecutter.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/babelizer/_cookiecutter.py b/babelizer/_cookiecutter.py index b1dd462..8c9ead0 100644 --- a/babelizer/_cookiecutter.py +++ b/babelizer/_cookiecutter.py @@ -39,11 +39,15 @@ def datetime_format(value: datetime, format_: str = "%Y-%M-%D") -> str: target_path = os.path.join(target_dir, render_path(filename, extra_context)) with open(target_path, "w") as fp: - fp.write( - env.get_template(os.path.join(rel_path, filename)).render( - **extra_context - ) - ) + print(os.path.join(rel_path, filename)) + _template = env.get_template(os.path.join(rel_path, filename)) + fp.write(_template.render(**extra_context)) + + # fp.write( + # env.get_template(os.path.join(rel_path, filename)).render( + # **extra_context + # ) + # ) with as_cwd(output_dir): run(extra_context) From 2502c3bc16c797e2fcf58b1c7a6b40cb95036242 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sun, 24 Mar 2024 10:05:18 -0600 Subject: [PATCH 09/38] remove .jinja extension from templates --- babelizer/_cookiecutter.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/babelizer/_cookiecutter.py b/babelizer/_cookiecutter.py index 8c9ead0..a34bb77 100644 --- a/babelizer/_cookiecutter.py +++ b/babelizer/_cookiecutter.py @@ -39,19 +39,21 @@ def datetime_format(value: datetime, format_: str = "%Y-%M-%D") -> str: target_path = os.path.join(target_dir, render_path(filename, extra_context)) with open(target_path, "w") as fp: - print(os.path.join(rel_path, filename)) - _template = env.get_template(os.path.join(rel_path, filename)) - fp.write(_template.render(**extra_context)) - - # fp.write( - # env.get_template(os.path.join(rel_path, filename)).render( - # **extra_context - # ) - # ) + fp.write( + env.get_template(os.path.join(rel_path, filename)).render( + **extra_context + ) + ) with as_cwd(output_dir): run(extra_context) def render_path(path: str, context: dict[str, Any]) -> str: - return Template(path).render(**context) + rendered_path = Template(path).render(**context) + + root, ext = os.path.splitext(rendered_path) + if ext == ".jinja": + rendered_path = root + + return rendered_path From 5a248b2b29fc8a438e5b7c06f2347f8dd284d5a7 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sun, 24 Mar 2024 10:05:34 -0600 Subject: [PATCH 10/38] add .jinja extension to python template files --- .../{{cookiecutter.package_name}}/docs/{conf.py => conf.py.jinja} | 0 .../{noxfile.py => noxfile.py.jinja} | 0 .../{__init__.py => __init__.py.jinja} | 0 .../{{cookiecutter.package_name}}/{_bmi.py => _bmi.py.jinja} | 0 .../{_version.py => _version.py.jinja} | 0 .../lib/{__init__.py => __init__.py.jinja} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename babelizer/data/{{cookiecutter.package_name}}/docs/{conf.py => conf.py.jinja} (100%) rename babelizer/data/{{cookiecutter.package_name}}/{noxfile.py => noxfile.py.jinja} (100%) rename babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/{__init__.py => __init__.py.jinja} (100%) rename babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/{_bmi.py => _bmi.py.jinja} (100%) rename babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/{_version.py => _version.py.jinja} (100%) rename babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/{__init__.py => __init__.py.jinja} (100%) diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/conf.py b/babelizer/data/{{cookiecutter.package_name}}/docs/conf.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/conf.py rename to babelizer/data/{{cookiecutter.package_name}}/docs/conf.py.jinja diff --git a/babelizer/data/{{cookiecutter.package_name}}/noxfile.py b/babelizer/data/{{cookiecutter.package_name}}/noxfile.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/noxfile.py rename to babelizer/data/{{cookiecutter.package_name}}/noxfile.py.jinja diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py b/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py rename to babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py.jinja diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_bmi.py b/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_bmi.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_bmi.py rename to babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_bmi.py.jinja diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_version.py b/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_version.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_version.py rename to babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_version.py.jinja diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/__init__.py b/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/__init__.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/__init__.py rename to babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/__init__.py.jinja From 54b354d5067491795cb08ee39666272ffa9d11c5 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sun, 24 Mar 2024 10:19:21 -0600 Subject: [PATCH 11/38] add package_version to context --- babelizer/render.py | 1 + 1 file changed, 1 insertion(+) diff --git a/babelizer/render.py b/babelizer/render.py index 0873b20..779fee5 100644 --- a/babelizer/render.py +++ b/babelizer/render.py @@ -59,6 +59,7 @@ def render( "LICENSE.rst": render_license(plugin_metadata), }, "now": datetime.datetime.now(), + "package_version": version, } | plugin_metadata.as_cookiecutter_context() } From 3c86843d6b6c718ab1385ab9a7aac0b4b8dea628 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sun, 31 Mar 2024 15:22:11 -0400 Subject: [PATCH 12/38] remove cookiecutter files from the data dir; rename package data dir --- babelizer/cli.py | 2 +- babelizer/data/cookiecutter.json | 27 ---- babelizer/data/hooks/post_gen_project.py | 147 ------------------ babelizer/data/hooks/pre_gen_project.py | 45 ------ .../.github/workflows/lint.yml | 0 .../.github/workflows/test.yml | 0 .../.gitignore | 0 .../.pre-commit-config.yaml | 0 .../CHANGES.rst | 0 .../CREDITS.rst | 0 .../LICENSE.rst | 0 .../MANIFEST.in | 0 .../README.rst | 0 .../docs/Makefile | 0 .../docs/api/.gitignore | 0 .../docs/authors.rst | 0 .../docs/babel.rst | 0 .../docs/changelog.rst | 0 .../docs/conf.py.jinja | 0 .../docs/developer_install.rst | 0 .../docs/environments.rst | 0 .../docs/index.rst | 0 .../docs/license.rst | 0 .../docs/quickstart.rst | 0 .../docs/updating.rst | 0 .../docs/usage.rst | 0 .../meson.build | 0 .../noxfile.py.jinja | 0 .../pyproject.toml | 0 .../requirements-build.txt | 0 .../requirements-library.txt | 0 .../requirements-testing.txt | 0 .../requirements.txt | 0 .../setup.cfg | 0 .../__init__.py.jinja | 0 .../_bmi.py.jinja | 0 .../_version.py.jinja | 0 .../lib/__init__.py.jinja | 0 .../{{cookiecutter.package_name}}/lib/_c.pyx | 0 .../lib/_cxx.pyx | 0 .../lib/_fortran.pyx | 0 .../lib/bmi_interoperability.f90 | 0 .../lib/bmi_interoperability.h | 0 babelizer/metadata.py | 3 +- 44 files changed, 3 insertions(+), 221 deletions(-) delete mode 100644 babelizer/data/cookiecutter.json delete mode 100644 babelizer/data/hooks/post_gen_project.py delete mode 100644 babelizer/data/hooks/pre_gen_project.py rename babelizer/data/{{{cookiecutter.package_name}} => templates}/.github/workflows/lint.yml (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/.github/workflows/test.yml (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/.gitignore (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/.pre-commit-config.yaml (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/CHANGES.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/CREDITS.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/LICENSE.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/MANIFEST.in (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/README.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/Makefile (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/api/.gitignore (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/authors.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/babel.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/changelog.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/conf.py.jinja (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/developer_install.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/environments.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/index.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/license.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/quickstart.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/updating.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/docs/usage.rst (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/meson.build (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/noxfile.py.jinja (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/pyproject.toml (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/requirements-build.txt (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/requirements-library.txt (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/requirements-testing.txt (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/requirements.txt (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/setup.cfg (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/{{cookiecutter.package_name}}/__init__.py.jinja (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/{{cookiecutter.package_name}}/_bmi.py.jinja (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/{{cookiecutter.package_name}}/_version.py.jinja (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/{{cookiecutter.package_name}}/lib/__init__.py.jinja (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/{{cookiecutter.package_name}}/lib/_c.pyx (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/{{cookiecutter.package_name}}/lib/_cxx.pyx (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/{{cookiecutter.package_name}}/lib/_fortran.pyx (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/{{cookiecutter.package_name}}/lib/bmi_interoperability.f90 (100%) rename babelizer/data/{{{cookiecutter.package_name}} => templates}/{{cookiecutter.package_name}}/lib/bmi_interoperability.h (100%) diff --git a/babelizer/cli.py b/babelizer/cli.py index 24fc0be..8d828b7 100644 --- a/babelizer/cli.py +++ b/babelizer/cli.py @@ -83,7 +83,7 @@ def init( META is babelizer configuration information, usually saved to a file. """ - template = template or os.path.join(get_datadir(), "{{cookiecutter.package_name}}") + template = template or os.path.join(get_datadir(), "templates") if not quiet: out(f"reading template from {template}") diff --git a/babelizer/data/cookiecutter.json b/babelizer/data/cookiecutter.json deleted file mode 100644 index f64b3dc..0000000 --- a/babelizer/data/cookiecutter.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "components": {}, - "build": { - "undef_macros": [], - "define_macros": [], - "libraries": [], - "library_dirs": [], - "include_dirs": [], - "extra_compile_args": [] - }, - "info": { - "full_name": "CSDMS", - "email": "csdms@colorado.edu", - "github_username": "csdms", - "project_short_description": "PyMT plugin for {{cookiecutter.package_name}}" - }, - "ci": { - "os": ["ubuntu", "macos", "windows"], - "python_version": ["3.10", "3.11", "3.12"] - }, - "package_name": "package", - "package_requirements": "", - "package_version": "0.1", - "language": ["c", "c++", "fortran", "python"], - "open_source_license": ["MIT License", "BSD License", "ISC License", "Apache Software License 2.0", "GNU General Public License v3", "Not open source"], - "files": {} -} diff --git a/babelizer/data/hooks/post_gen_project.py b/babelizer/data/hooks/post_gen_project.py deleted file mode 100644 index 0be9b26..0000000 --- a/babelizer/data/hooks/post_gen_project.py +++ /dev/null @@ -1,147 +0,0 @@ -#! /usr/bin/env python -import errno -import os -import re -from collections import defaultdict -from pathlib import Path - -from logoizer import logoize - - -PROJECT_DIRECTORY = Path.cwd().resolve() -LIB_DIRECTORY = Path("{{ cookiecutter.package_name }}", "lib") - - -def remove_file(filepath): - (PROJECT_DIRECTORY / filepath).unlink(filepath) - - -def remove_folder(folderpath): - shutil.rmtree(PROJECT_DIRECTORY / folderpath) - - -def make_folder(folderpath): - try: - (PROJECT_DIRECTORY / folderpath).mkdir(parents=True, exist_ok=True) - except OSError: - pass - - -def clean_folder(folderpath, keep=None): - if keep: - keep = set([str((folderpath / path).resolve()) for path in keep]) - else: - keep = set() - - folderpath = PROJECT_DIRECTORY / folderpath - for fname in folderpath.glob("*"): - if not fname.is_dir() and str(fname.resolve()) not in keep: - fname.unlink() - - try: - folderpath.rmdir() - except OSError as err: - if err.errno != errno.ENOTEMPTY: - raise - - -def split_file(filepath, include_preamble=False): - filepath = Path(filepath) - SPLIT_START_REGEX = re.compile(r"\s*#\s*start:\s*(?P\S+)\s*") - - files = defaultdict(list) - fname = "preamble" - with open(filepath, "r") as fp: - for line in fp: - m = SPLIT_START_REGEX.match(line) - if m: - fname = m["fname"] - files[fname].append(line) - - preamble = files.pop("preamble") - folderpath = filepath.parent - for name, contents in files.items(): - with open(folderpath / name, "w") as fp: - if include_preamble: - fp.write("".join(preamble)) - print("".join(contents).strip(), file=fp) - # fp.write("".join(contents).strip()) - - return set(files) - - -def write_api_yaml(folderpath, **kwds): - make_folder(folderpath) - - api_yaml = PROJECT_DIRECTORY / folderpath / "api.yaml" - contents = """\ -name: {package_name} -language: {language} -package: {package_name} -class: {plugin_class} -""".format(**kwds) - with open(api_yaml, "w") as fp: - fp.write(contents) - - return api_yaml - - -def remove_trailing_whitespace(path): - with open(path) as fp: - lines = [line.rstrip() for line in fp] - with open(path, "w") as fp: - print(os.linesep.join(lines), file=fp) - - -if __name__ == "__main__": - keep = set() - - static_dir = PROJECT_DIRECTORY / "docs" / "_static" - make_folder(static_dir) - - logoize("{{ cookiecutter.package_name }}", static_dir / "logo-light.svg", light=True) - logoize("{{ cookiecutter.package_name }}", static_dir / "logo-dark.svg", light=False) - - remove_trailing_whitespace(static_dir / "logo-dark.svg") - remove_trailing_whitespace(static_dir / "logo-light.svg") - - {%- if cookiecutter.language == 'c' %} - - keep |= set(["__init__.py", "bmi.c", "bmi.h"]) - keep |= split_file(LIB_DIRECTORY / "_c.pyx", include_preamble=True) - - {%- elif cookiecutter.language == 'c++' %} - - keep |= set(["__init__.py", "bmi.hxx"]) - keep |= split_file(LIB_DIRECTORY / "_cxx.pyx", include_preamble=True) - - {%- elif cookiecutter.language == 'fortran' %} - - keep |= set(["__init__.py", "bmi.f90", "bmi_interoperability.f90", - "bmi_interoperability.h"]) - keep |= split_file(LIB_DIRECTORY / "_fortran.pyx", include_preamble=True) - - {%- endif %} - - clean_folder(LIB_DIRECTORY, keep=keep) - - if "Not open source" == "{{ cookiecutter.open_source_license }}": - remove_file("LICENSE") - - {%- if cookiecutter.language == 'python' %} - remove_file("meson.build") - {%- endif %} - - datadir = Path("meta") - package_datadir = Path("{{ cookiecutter.package_name }}") / "data" - if not package_datadir.exists(): - package_datadir.symlink_to(".." / datadir, target_is_directory=True) - -{%- for babelized_class, component in cookiecutter.components|dictsort %} - write_api_yaml( - datadir / "{{ babelized_class }}", - language="{{ component.language }}", - plugin_class="{{ babelized_class }}", - package_name="{{ cookiecutter.package_name }}", - ) -{% endfor %} diff --git a/babelizer/data/hooks/pre_gen_project.py b/babelizer/data/hooks/pre_gen_project.py deleted file mode 100644 index e03b932..0000000 --- a/babelizer/data/hooks/pre_gen_project.py +++ /dev/null @@ -1,45 +0,0 @@ -import re -import sys - -MODULE_REGEX = r"^[_a-zA-Z][_a-zA-Z0-9]+$" - -module_name = "{{ cookiecutter.package_name }}" - - -if not re.match(MODULE_REGEX, module_name): - print( - "ERROR: The project slug (%s) is not a valid Python module name. Please do not use a - and use _ instead" - % module_name - ) - - # Exit to cancel project - sys.exit(1) - - -def is_valid_entry_point(entry_point): - try: - babelized_class, plugin_entry_point = entry_point.split("=") - except ValueError: - return False - try: - plugin_module, plugin_class = plugin_entry_point.split(":") - except ValueError: - return False - - if not re.match(MODULE_REGEX, babelized_class): - return False - for module_name in plugin_module.split("."): - if not re.match(MODULE_REGEX, module_name): - return False - if not re.match(MODULE_REGEX, plugin_class): - return False - - return True - - -{%- for babelized_class, component in cookiecutter.components|dictsort %} -if not is_valid_entry_point(entry_point := "{{ babelized_class }}={{ component.library }}:{{ component.entry_point}}"): - print(f"ERROR: The entry point ({entry_point}) is not a valid Python entry point.") - - sys.exit(2) -{% endfor %} diff --git a/babelizer/data/{{cookiecutter.package_name}}/.github/workflows/lint.yml b/babelizer/data/templates/.github/workflows/lint.yml similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/.github/workflows/lint.yml rename to babelizer/data/templates/.github/workflows/lint.yml diff --git a/babelizer/data/{{cookiecutter.package_name}}/.github/workflows/test.yml b/babelizer/data/templates/.github/workflows/test.yml similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/.github/workflows/test.yml rename to babelizer/data/templates/.github/workflows/test.yml diff --git a/babelizer/data/{{cookiecutter.package_name}}/.gitignore b/babelizer/data/templates/.gitignore similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/.gitignore rename to babelizer/data/templates/.gitignore diff --git a/babelizer/data/{{cookiecutter.package_name}}/.pre-commit-config.yaml b/babelizer/data/templates/.pre-commit-config.yaml similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/.pre-commit-config.yaml rename to babelizer/data/templates/.pre-commit-config.yaml diff --git a/babelizer/data/{{cookiecutter.package_name}}/CHANGES.rst b/babelizer/data/templates/CHANGES.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/CHANGES.rst rename to babelizer/data/templates/CHANGES.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/CREDITS.rst b/babelizer/data/templates/CREDITS.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/CREDITS.rst rename to babelizer/data/templates/CREDITS.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/LICENSE.rst b/babelizer/data/templates/LICENSE.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/LICENSE.rst rename to babelizer/data/templates/LICENSE.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/MANIFEST.in b/babelizer/data/templates/MANIFEST.in similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/MANIFEST.in rename to babelizer/data/templates/MANIFEST.in diff --git a/babelizer/data/{{cookiecutter.package_name}}/README.rst b/babelizer/data/templates/README.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/README.rst rename to babelizer/data/templates/README.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/Makefile b/babelizer/data/templates/docs/Makefile similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/Makefile rename to babelizer/data/templates/docs/Makefile diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/api/.gitignore b/babelizer/data/templates/docs/api/.gitignore similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/api/.gitignore rename to babelizer/data/templates/docs/api/.gitignore diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/authors.rst b/babelizer/data/templates/docs/authors.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/authors.rst rename to babelizer/data/templates/docs/authors.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/babel.rst b/babelizer/data/templates/docs/babel.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/babel.rst rename to babelizer/data/templates/docs/babel.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/changelog.rst b/babelizer/data/templates/docs/changelog.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/changelog.rst rename to babelizer/data/templates/docs/changelog.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/conf.py.jinja b/babelizer/data/templates/docs/conf.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/conf.py.jinja rename to babelizer/data/templates/docs/conf.py.jinja diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/developer_install.rst b/babelizer/data/templates/docs/developer_install.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/developer_install.rst rename to babelizer/data/templates/docs/developer_install.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/environments.rst b/babelizer/data/templates/docs/environments.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/environments.rst rename to babelizer/data/templates/docs/environments.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/index.rst b/babelizer/data/templates/docs/index.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/index.rst rename to babelizer/data/templates/docs/index.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/license.rst b/babelizer/data/templates/docs/license.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/license.rst rename to babelizer/data/templates/docs/license.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/quickstart.rst b/babelizer/data/templates/docs/quickstart.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/quickstart.rst rename to babelizer/data/templates/docs/quickstart.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/updating.rst b/babelizer/data/templates/docs/updating.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/updating.rst rename to babelizer/data/templates/docs/updating.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/docs/usage.rst b/babelizer/data/templates/docs/usage.rst similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/docs/usage.rst rename to babelizer/data/templates/docs/usage.rst diff --git a/babelizer/data/{{cookiecutter.package_name}}/meson.build b/babelizer/data/templates/meson.build similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/meson.build rename to babelizer/data/templates/meson.build diff --git a/babelizer/data/{{cookiecutter.package_name}}/noxfile.py.jinja b/babelizer/data/templates/noxfile.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/noxfile.py.jinja rename to babelizer/data/templates/noxfile.py.jinja diff --git a/babelizer/data/{{cookiecutter.package_name}}/pyproject.toml b/babelizer/data/templates/pyproject.toml similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/pyproject.toml rename to babelizer/data/templates/pyproject.toml diff --git a/babelizer/data/{{cookiecutter.package_name}}/requirements-build.txt b/babelizer/data/templates/requirements-build.txt similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/requirements-build.txt rename to babelizer/data/templates/requirements-build.txt diff --git a/babelizer/data/{{cookiecutter.package_name}}/requirements-library.txt b/babelizer/data/templates/requirements-library.txt similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/requirements-library.txt rename to babelizer/data/templates/requirements-library.txt diff --git a/babelizer/data/{{cookiecutter.package_name}}/requirements-testing.txt b/babelizer/data/templates/requirements-testing.txt similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/requirements-testing.txt rename to babelizer/data/templates/requirements-testing.txt diff --git a/babelizer/data/{{cookiecutter.package_name}}/requirements.txt b/babelizer/data/templates/requirements.txt similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/requirements.txt rename to babelizer/data/templates/requirements.txt diff --git a/babelizer/data/{{cookiecutter.package_name}}/setup.cfg b/babelizer/data/templates/setup.cfg similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/setup.cfg rename to babelizer/data/templates/setup.cfg diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py.jinja b/babelizer/data/templates/{{cookiecutter.package_name}}/__init__.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py.jinja rename to babelizer/data/templates/{{cookiecutter.package_name}}/__init__.py.jinja diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_bmi.py.jinja b/babelizer/data/templates/{{cookiecutter.package_name}}/_bmi.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_bmi.py.jinja rename to babelizer/data/templates/{{cookiecutter.package_name}}/_bmi.py.jinja diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_version.py.jinja b/babelizer/data/templates/{{cookiecutter.package_name}}/_version.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/_version.py.jinja rename to babelizer/data/templates/{{cookiecutter.package_name}}/_version.py.jinja diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/__init__.py.jinja b/babelizer/data/templates/{{cookiecutter.package_name}}/lib/__init__.py.jinja similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/__init__.py.jinja rename to babelizer/data/templates/{{cookiecutter.package_name}}/lib/__init__.py.jinja diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/_c.pyx b/babelizer/data/templates/{{cookiecutter.package_name}}/lib/_c.pyx similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/_c.pyx rename to babelizer/data/templates/{{cookiecutter.package_name}}/lib/_c.pyx diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/_cxx.pyx b/babelizer/data/templates/{{cookiecutter.package_name}}/lib/_cxx.pyx similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/_cxx.pyx rename to babelizer/data/templates/{{cookiecutter.package_name}}/lib/_cxx.pyx diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/_fortran.pyx b/babelizer/data/templates/{{cookiecutter.package_name}}/lib/_fortran.pyx similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/_fortran.pyx rename to babelizer/data/templates/{{cookiecutter.package_name}}/lib/_fortran.pyx diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/bmi_interoperability.f90 b/babelizer/data/templates/{{cookiecutter.package_name}}/lib/bmi_interoperability.f90 similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/bmi_interoperability.f90 rename to babelizer/data/templates/{{cookiecutter.package_name}}/lib/bmi_interoperability.f90 diff --git a/babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/bmi_interoperability.h b/babelizer/data/templates/{{cookiecutter.package_name}}/lib/bmi_interoperability.h similarity index 100% rename from babelizer/data/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/lib/bmi_interoperability.h rename to babelizer/data/templates/{{cookiecutter.package_name}}/lib/bmi_interoperability.h diff --git a/babelizer/metadata.py b/babelizer/metadata.py index 659f9ef..f66d1a2 100644 --- a/babelizer/metadata.py +++ b/babelizer/metadata.py @@ -423,7 +423,8 @@ def parse_entry_point(specifier: str) -> tuple[str, str, str]: module, obj = (item.strip() for item in value.split(":")) except ValueError: raise ValidationError( - f"bad entry point specifier ({specifier}). specifier must be of the form name=module:class" + f"bad entry point specifier ({specifier}). specifier must be of" + " the form name=module:class" ) from None return name, module, obj From d22da6ab710579b3e5f8cc4e546bdea0c80e1f89 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sun, 31 Mar 2024 15:52:20 -0400 Subject: [PATCH 13/38] remove cookiecutter context from jinja templates --- babelizer/_post_hook.py | 9 ++-- .../data/templates/.github/workflows/test.yml | 10 ++-- babelizer/data/templates/.gitignore | 2 +- babelizer/data/templates/CHANGES.rst | 2 +- babelizer/data/templates/CREDITS.rst | 2 +- babelizer/data/templates/LICENSE.rst | 2 +- babelizer/data/templates/MANIFEST.in | 8 +-- babelizer/data/templates/README.rst | 54 +++++++++---------- babelizer/data/templates/docs/Makefile | 2 +- babelizer/data/templates/docs/api/.gitignore | 2 +- babelizer/data/templates/docs/conf.py.jinja | 20 +++---- .../data/templates/docs/developer_install.rst | 22 ++++---- .../data/templates/docs/environments.rst | 8 +-- babelizer/data/templates/docs/index.rst | 10 ++-- babelizer/data/templates/docs/quickstart.rst | 10 ++-- babelizer/data/templates/meson.build | 52 +++++++++--------- babelizer/data/templates/noxfile.py.jinja | 12 ++--- babelizer/data/templates/pyproject.toml | 34 ++++++------ .../data/templates/requirements-build.txt | 6 +-- .../data/templates/requirements-library.txt | 2 +- .../__init__.py.jinja | 1 - .../_bmi.py.jinja | 1 - .../_version.py.jinja | 1 - .../lib/__init__.py.jinja | 1 - .../{{package_name}}/__init__.py.jinja | 1 + .../templates/{{package_name}}/_bmi.py.jinja | 1 + .../{{package_name}}/_version.py.jinja | 1 + .../{{package_name}}/lib/__init__.py.jinja | 1 + .../lib/_c.pyx | 2 +- .../lib/_cxx.pyx | 2 +- .../lib/_fortran.pyx | 2 +- .../lib/bmi_interoperability.f90 | 6 +-- .../lib/bmi_interoperability.h | 0 33 files changed, 143 insertions(+), 146 deletions(-) delete mode 100644 babelizer/data/templates/{{cookiecutter.package_name}}/__init__.py.jinja delete mode 100644 babelizer/data/templates/{{cookiecutter.package_name}}/_bmi.py.jinja delete mode 100644 babelizer/data/templates/{{cookiecutter.package_name}}/_version.py.jinja delete mode 100644 babelizer/data/templates/{{cookiecutter.package_name}}/lib/__init__.py.jinja create mode 100644 babelizer/data/templates/{{package_name}}/__init__.py.jinja create mode 100644 babelizer/data/templates/{{package_name}}/_bmi.py.jinja create mode 100644 babelizer/data/templates/{{package_name}}/_version.py.jinja create mode 100644 babelizer/data/templates/{{package_name}}/lib/__init__.py.jinja rename babelizer/data/templates/{{{cookiecutter.package_name}} => {{package_name}}}/lib/_c.pyx (99%) rename babelizer/data/templates/{{{cookiecutter.package_name}} => {{package_name}}}/lib/_cxx.pyx (98%) rename babelizer/data/templates/{{{cookiecutter.package_name}} => {{package_name}}}/lib/_fortran.pyx (99%) rename babelizer/data/templates/{{{cookiecutter.package_name}} => {{package_name}}}/lib/bmi_interoperability.f90 (99%) rename babelizer/data/templates/{{{cookiecutter.package_name}} => {{package_name}}}/lib/bmi_interoperability.h (100%) diff --git a/babelizer/_post_hook.py b/babelizer/_post_hook.py index 11f9d07..98d69ea 100644 --- a/babelizer/_post_hook.py +++ b/babelizer/_post_hook.py @@ -104,8 +104,8 @@ def remove_trailing_whitespace(path: str | Path) -> None: def run(context: dict[str, Any]) -> None: PROJECT_DIRECTORY = Path.cwd().resolve() - package_name = context["cookiecutter"]["package_name"] - language = context["cookiecutter"]["language"] + package_name = context["package_name"] + language = context["language"] LIB_DIRECTORY = PROJECT_DIRECTORY / Path(package_name, "lib") @@ -138,9 +138,6 @@ def run(context: dict[str, Any]) -> None: clean_folder(LIB_DIRECTORY, keep=keep) - # if "Not open source" == "{{ cookiecutter.open_source_license }}": - # remove_file("LICENSE") - if language == "python": os.remove(PROJECT_DIRECTORY / "meson.build") @@ -149,7 +146,7 @@ def run(context: dict[str, Any]) -> None: if not package_datadir.exists(): package_datadir.symlink_to(".." / datadir, target_is_directory=True) - for babelized_class in context["cookiecutter"]["components"]: + for babelized_class in context["components"]: write_api_yaml( PROJECT_DIRECTORY / datadir / babelized_class, language=language, diff --git a/babelizer/data/templates/.github/workflows/test.yml b/babelizer/data/templates/.github/workflows/test.yml index 3541623..973f34d 100644 --- a/babelizer/data/templates/.github/workflows/test.yml +++ b/babelizer/data/templates/.github/workflows/test.yml @@ -19,8 +19,8 @@ jobs: strategy: matrix: - os: [{{ cookiecutter.ci.os | join(", ") }}] - python-version: [{{ cookiecutter.ci.python_version | join(", ") }}] + os: [{{ ci.os | join(", ") }}] + python-version: [{{ ci.python_version | join(", ") }}] steps: - uses: actions/checkout@v4 @@ -51,7 +51,7 @@ jobs: - name: Test run: | - python -c 'import {{ cookiecutter.package_name }}' - {%- for babelized_class in cookiecutter.components %} - bmi-test {{ cookiecutter.package_name }}.bmi:{{ babelized_class }} -vvv + python -c 'import {{ package_name }}' + {%- for babelized_class in components %} + bmi-test {{ package_name }}.bmi:{{ babelized_class }} -vvv {%- endfor %} diff --git a/babelizer/data/templates/.gitignore b/babelizer/data/templates/.gitignore index 0d0df21..2ef470b 100644 --- a/babelizer/data/templates/.gitignore +++ b/babelizer/data/templates/.gitignore @@ -1 +1 @@ -{{cookiecutter.files['.gitignore']}} +{{ files['.gitignore'] }} diff --git a/babelizer/data/templates/CHANGES.rst b/babelizer/data/templates/CHANGES.rst index 0f28d3d..6d925e8 100644 --- a/babelizer/data/templates/CHANGES.rst +++ b/babelizer/data/templates/CHANGES.rst @@ -6,7 +6,7 @@ Release Notes .. towncrier release notes start -0.1.0 ({{ cookiecutter.now|datetimeformat('%Y-%m-%d') }}) +0.1.0 ({{ now|datetimeformat('%Y-%m-%d') }}) ------------------ - Initial release diff --git a/babelizer/data/templates/CREDITS.rst b/babelizer/data/templates/CREDITS.rst index c8314f9..1ae8d38 100644 --- a/babelizer/data/templates/CREDITS.rst +++ b/babelizer/data/templates/CREDITS.rst @@ -1,4 +1,4 @@ Credits ======= -* {{ cookiecutter.info.full_name }} <{{ cookiecutter.info.email }}> +* {{ info.full_name }} <{{ info.email }}> diff --git a/babelizer/data/templates/LICENSE.rst b/babelizer/data/templates/LICENSE.rst index 1058335..79121ef 100644 --- a/babelizer/data/templates/LICENSE.rst +++ b/babelizer/data/templates/LICENSE.rst @@ -1 +1 @@ -{{cookiecutter.files['LICENSE.rst']}} +{{ files['LICENSE.rst'] }} diff --git a/babelizer/data/templates/MANIFEST.in b/babelizer/data/templates/MANIFEST.in index fb1c8ab..52020e2 100644 --- a/babelizer/data/templates/MANIFEST.in +++ b/babelizer/data/templates/MANIFEST.in @@ -1,15 +1,15 @@ -recursive-include {{ cookiecutter.package_name }}/data * +recursive-include {{ package_name }}/data * include LICENSE include requirements.txt include *.rst include *.txt include Makefile include babel.toml -recursive-include {{ cookiecutter.package_name }} *.pyx +recursive-include {{ package_name }} *.pyx recursive-include docs *.py recursive-include docs *.rst recursive-include docs Makefile recursive-exclude meta * recursive-exclude recipe * -recursive-exclude {{ cookiecutter.package_name }} *.cpp -recursive-exclude {{ cookiecutter.package_name }} *.c +recursive-exclude {{ package_name }} *.cpp +recursive-exclude {{ package_name }} *.c diff --git a/babelizer/data/templates/README.rst b/babelizer/data/templates/README.rst index 6335302..e2d991d 100644 --- a/babelizer/data/templates/README.rst +++ b/babelizer/data/templates/README.rst @@ -1,29 +1,29 @@ -{{ '=' * cookiecutter.package_name | length }} -{{ cookiecutter.package_name }} -{{ '=' * cookiecutter.package_name | length }} +{{ '=' * package_name | length }} +{{ package_name }} +{{ '=' * package_name | length }} -{% set is_open_source = cookiecutter.open_source_license != 'Not open source' -%} +{% set is_open_source = open_source_license != 'Not open source' -%} {% if is_open_source %} .. image:: https://img.shields.io/badge/CSDMS-Basic%20Model%20Interface-green.svg :target: https://bmi.readthedocs.io/ :alt: Basic Model Interface -.. image:: https://img.shields.io/badge/recipe-{{ cookiecutter.package_name }}-green.svg - :target: https://anaconda.org/conda-forge/{{ cookiecutter.package_name }} +.. image:: https://img.shields.io/badge/recipe-{{ package_name }}-green.svg + :target: https://anaconda.org/conda-forge/{{ package_name }} -.. image:: https://readthedocs.org/projects/{{ cookiecutter.package_name | replace("_", "-") }}/badge/?version=latest - :target: https://{{ cookiecutter.package_name | replace("_", "-") }}.readthedocs.io/en/latest/?badge=latest +.. image:: https://readthedocs.org/projects/{{ package_name | replace("_", "-") }}/badge/?version=latest + :target: https://{{ package_name | replace("_", "-") }}.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status -.. image:: https://github.com/{{ cookiecutter.info.github_username }}/{{ cookiecutter.package_name }}/actions/workflows/test.yml/badge.svg - :target: https://github.com/{{ cookiecutter.info.github_username }}/{{ cookiecutter.package_name }}/actions/workflows/test.yml +.. image:: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/test.yml/badge.svg + :target: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/test.yml -.. image:: https://github.com/{{ cookiecutter.info.github_username }}/{{ cookiecutter.package_name }}/actions/workflows/flake8.yml/badge.svg - :target: https://github.com/{{ cookiecutter.info.github_username }}/{{ cookiecutter.package_name }}/actions/workflows/flake8.yml +.. image:: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/flake8.yml/badge.svg + :target: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/flake8.yml -.. image:: https://github.com/{{ cookiecutter.info.github_username }}/{{ cookiecutter.package_name }}/actions/workflows/black.yml/badge.svg - :target: https://github.com/{{ cookiecutter.info.github_username }}/{{ cookiecutter.package_name }}/actions/workflows/black.yml +.. image:: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/black.yml/badge.svg + :target: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/black.yml {%- endif %} @@ -42,9 +42,9 @@ Python and the Python Modeling Toolkit, PyMT. * - Library - Component - PyMT - {% for babelized_class, component in cookiecutter.components|dictsort -%} + {% for babelized_class, component in components|dictsort -%} * - {{ component.library }} - - :class:`~{{ cookiecutter.package_name }}.{{ babelized_class }}` + - :class:`~{{ package_name }}.{{ babelized_class }}` - .. code-block:: pycon @@ -55,8 +55,8 @@ Python and the Python Modeling Toolkit, PyMT. {% if is_open_source %} -* Free software: {{ cookiecutter.open_source_license }} -* Documentation: https://{{ cookiecutter.package_name | replace("_", "-") }}.readthedocs.io. +* Free software: {{ open_source_license }} +* Documentation: https://{{ package_name | replace("_", "-") }}.readthedocs.io. {% endif %} @@ -65,22 +65,22 @@ Quickstart .. start-quickstart -To get started you will need to install the *{{ cookiecutter.package_name }}* package. +To get started you will need to install the *{{ package_name }}* package. Here are two ways to do so. Install from conda-forge ------------------------ -If the *{{ cookiecutter.package_name }}* package is distributed on *conda-forge*, install it into your current environment with *conda*. +If the *{{ package_name }}* package is distributed on *conda-forge*, install it into your current environment with *conda*. .. code:: bash - conda install -c conda-forge {{ cookiecutter.package_name }} + conda install -c conda-forge {{ package_name }} Install from source ------------------- -You can build and install the *{{ cookiecutter.package_name }}* package from source using *conda* and *pip*. +You can build and install the *{{ package_name }}* package from source using *conda* and *pip*. First, from the source directory, install package dependencies into your current environment with *conda*. @@ -89,7 +89,7 @@ First, from the source directory, install package dependencies into your current conda install -c conda-forge --file requirements.txt --file requirements-build.txt --file requirements-library.txt Then install the package itself with *pip*. -{%- if cookiecutter.language == 'python' %} +{%- if language == 'python' %} .. code:: bash @@ -115,16 +115,16 @@ There are two ways to use the components provided by this package: directly thro Model Interface (BMI), or as a PyMT plugin. A BMI is provided by each component in this package: -{%- for babelized_class, component in cookiecutter.components|dictsort -%} -:class:`~{{ cookiecutter.package_name}}.{{ babelized_class }}` +{%- for babelized_class, component in components|dictsort -%} +:class:`~{{ package_name}}.{{ babelized_class }}` {% endfor %}. -{% for babelized_class, component in cookiecutter.components|dictsort -%} +{% for babelized_class, component in components|dictsort -%} .. code-block:: pycon - >>> from {{ cookiecutter.package_name}} import {{ babelized_class }} + >>> from {{ package_name}} import {{ babelized_class }} >>> model = {{ babelized_class }}() >>> model.get_component_name() # Get the name of the component >>> model.get_output_var_names() # Get a list of the component's output variables diff --git a/babelizer/data/templates/docs/Makefile b/babelizer/data/templates/docs/Makefile index ed1a43d..d5748f0 100644 --- a/babelizer/data/templates/docs/Makefile +++ b/babelizer/data/templates/docs/Makefile @@ -4,7 +4,7 @@ # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = python -msphinx -SPHINXPROJ = {{ cookiecutter.package_name }} +SPHINXPROJ = {{ package_name }} SOURCEDIR = . BUILDDIR = _build diff --git a/babelizer/data/templates/docs/api/.gitignore b/babelizer/data/templates/docs/api/.gitignore index 4935535..745c9c8 100644 --- a/babelizer/data/templates/docs/api/.gitignore +++ b/babelizer/data/templates/docs/api/.gitignore @@ -1,2 +1,2 @@ # auto-generated with sphinx-apidoc -{{ cookiecutter.package_name }}*.rst +{{ package_name }}*.rst diff --git a/babelizer/data/templates/docs/conf.py.jinja b/babelizer/data/templates/docs/conf.py.jinja index 5679bab..9400196 100644 --- a/babelizer/data/templates/docs/conf.py.jinja +++ b/babelizer/data/templates/docs/conf.py.jinja @@ -1,4 +1,4 @@ -# {{ cookiecutter.package_name }} documentation build configuration file, created by +# {{ package_name }} documentation build configuration file, created by # sphinx-quickstart on Fri Jun 9 13:47:02 2017. # # This file is execfile()d with the current directory set to its @@ -18,7 +18,7 @@ import os import pathlib -import {{ cookiecutter.package_name }} +import {{ package_name }} docs_dir = os.path.dirname(__file__) @@ -58,18 +58,18 @@ source_suffix = ".rst" master_doc = "index" # General information about the project. -project = "{{ cookiecutter.package_name }}" -copyright = "{{ cookiecutter.now|datetimeformat('%Y') }}, {{ cookiecutter.info.full_name }}" -author = "{{ cookiecutter.info.full_name }}" +project = "{{ package_name }}" +copyright = "{{ now|datetimeformat('%Y') }}, {{ info.full_name }}" +author = "{{ info.full_name }}" # The version info for the project you're documenting, acts as replacement # for |version| and |release|, also used in various other places throughout # the built documents. # # The short X.Y version. -version = {{ cookiecutter.package_name }}.__version__ +version = {{ package_name }}.__version__ # The full version, including alpha/beta/rc tags. -release = {{ cookiecutter.package_name }}.__version__ +release = {{ package_name }}.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -96,7 +96,7 @@ todo_include_todos = False # a list of builtin themes. # html_theme = "furo" -html_title = "{{ cookiecutter.package_name }}" +html_title = "{{ package_name }}" # Theme options are theme-specific and customize the look and feel of a @@ -105,7 +105,7 @@ html_title = "{{ cookiecutter.package_name }}" # html_theme_options = { "announcement": None, - "source_repository": "https://github.com/{{ cookiecutter.info.github_username }}/{{ cookiecutter.package_name }}/", + "source_repository": "https://github.com/{{ info.github_username }}/{{ package_name }}/", "source_branch": "main", "source_directory": "docs", "sidebar_hide_name": False, @@ -132,7 +132,7 @@ html_static_path = ["_static"] # -- Options for HTMLHelp output --------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = "{{ cookiecutter.package_name }}doc" +htmlhelp_basename = "{{ package_name }}doc" # -- Options for intersphinx extension --------------------------------------- diff --git a/babelizer/data/templates/docs/developer_install.rst b/babelizer/data/templates/docs/developer_install.rst index a6dcc70..dfb47d1 100644 --- a/babelizer/data/templates/docs/developer_install.rst +++ b/babelizer/data/templates/docs/developer_install.rst @@ -6,30 +6,30 @@ Developer Install .. important:: - The following commands will install *{{ cookiecutter.package_name }}* into your current environment. Although - not necessary, we **highly recommend** you install *{{ cookiecutter.package_name }}* into its own + The following commands will install *{{ package_name }}* into your current environment. Although + not necessary, we **highly recommend** you install *{{ package_name }}* into its own :ref:`virtual environment `. -If you will be modifying code or contributing new code to *{{ cookiecutter.package_name }}*, you will first -need to get *{{ cookiecutter.package_name }}*'s source code and then install *{{ cookiecutter.package_name }}* from that code. +If you will be modifying code or contributing new code to *{{ package_name }}*, you will first +need to get *{{ package_name }}*'s source code and then install *{{ package_name }}* from that code. Source Install -------------- -*{{ cookiecutter.package_name }}* is actively being developed on GitHub, where the code is freely available. +*{{ package_name }}* is actively being developed on GitHub, where the code is freely available. If you would like to modify or contribute code, you can either clone our repository .. code-block:: bash - git clone git://github.com/pymt-lab/{{ cookiecutter.package_name }}.git + git clone git://github.com/pymt-lab/{{ package_name }}.git -or download the `tarball `_ +or download the `tarball `_ (a zip file is available for Windows users): .. code-block:: bash - curl -OL https://github.com/{{ cookiecutter.info.github_username }}/{{ cookiecutter.package_name }}/tarball/master + curl -OL https://github.com/{{ info.github_username }}/{{ package_name }}/tarball/master Once you have a copy of the source code, you can install it into your current Python environment, @@ -38,7 +38,7 @@ Python environment, .. code-block:: bash - cd {{ cookiecutter.package_name }} + cd {{ package_name }} mamba install --file=requirements.txt pip install -e . @@ -46,7 +46,7 @@ Python environment, .. code-block:: bash - cd {{ cookiecutter.package_name }} + cd {{ package_name }} conda install --file=requirements.txt pip install -e . @@ -54,5 +54,5 @@ Python environment, .. code-block:: bash - cd {{ cookiecutter.package_name }} + cd {{ package_name }} pip install -e . diff --git a/babelizer/data/templates/docs/environments.rst b/babelizer/data/templates/docs/environments.rst index 7b1f4c0..ec8dfca 100644 --- a/babelizer/data/templates/docs/environments.rst +++ b/babelizer/data/templates/docs/environments.rst @@ -18,15 +18,15 @@ should stick with *pip*. .. code-block:: bash conda install mamba -c conda-forge - mamba create -n {{ cookiecutter.package_name }} - mamba activate {{ cookiecutter.package_name }} + mamba create -n {{ package_name }} + mamba activate {{ package_name }} .. tab:: conda .. code-block:: bash - conda create -n {{ cookiecutter.package_name }} - conda activate {{ cookiecutter.package_name }} + conda create -n {{ package_name }} + conda activate {{ package_name }} .. tab:: venv diff --git a/babelizer/data/templates/docs/index.rst b/babelizer/data/templates/docs/index.rst index b3c2c42..2fe0fe0 100644 --- a/babelizer/data/templates/docs/index.rst +++ b/babelizer/data/templates/docs/index.rst @@ -1,15 +1,15 @@ .. image:: _static/logo-light.svg :align: center :scale: 15% - :alt: {{ cookiecutter.package_name }} - :target: https://{{ cookiecutter.package_name }}.readthedocs.org/ + :alt: {{ package_name }} + :target: https://{{ package_name }}.readthedocs.org/ :class: only-light .. image:: _static/logo-dark.svg :align: center :scale: 15% - :alt: {{ cookiecutter.package_name }} - :target: https://{{ cookiecutter.package_name }}.readthedocs.org/ + :alt: {{ package_name }} + :target: https://{{ package_name }}.readthedocs.org/ :class: only-dark .. include:: ../README.rst @@ -23,7 +23,7 @@ quickstart usage - API + API babel .. contributing diff --git a/babelizer/data/templates/docs/quickstart.rst b/babelizer/data/templates/docs/quickstart.rst index 8753666..753055c 100644 --- a/babelizer/data/templates/docs/quickstart.rst +++ b/babelizer/data/templates/docs/quickstart.rst @@ -3,16 +3,16 @@ Quickstart .. note:: - The following commands will install *{{ cookiecutter.package_name}}* into your current + The following commands will install *{{ package_name}}* into your current environment. Although not necessary, we **highly recommend** you install - *{{ cookiecutter.package_name}}* into its own + *{{ package_name}}* into its own :ref:`virtual environment `. .. include:: ../README.rst :start-after: .. start-quickstart :end-before: .. end-quickstart -If you would like the very latest development version of *{{ cookiecutter.package_name}}* -or want to modify or contribute code to the *{{ cookiecutter.package_name}}* project, +If you would like the very latest development version of *{{ package_name}}* +or want to modify or contribute code to the *{{ package_name}}* project, you will need to do a :ref:`developer installation ` of -*{{ cookiecutter.package_name }}* from source. +*{{ package_name }}* from source. diff --git a/babelizer/data/templates/meson.build b/babelizer/data/templates/meson.build index 6edc29d..a524628 100644 --- a/babelizer/data/templates/meson.build +++ b/babelizer/data/templates/meson.build @@ -1,23 +1,23 @@ project( - '{{ cookiecutter.package_name }}', -{%- if cookiecutter.language == 'c' %} + '{{ package_name }}', +{%- if language == 'c' %} 'c', -{%- elif cookiecutter.language == 'c++' %} +{%- elif language == 'c++' %} 'cpp', -{%- elif cookiecutter.language == 'fortran' %} +{%- elif language == 'fortran' %} 'fortran', {%- endif %} 'cython', - version: '{{ cookiecutter.package_version }}', + version: '{{ package_version }}', ) py = import('python').find_installation(pure: false) -{%- if cookiecutter.language == 'c' %} +{%- if language == 'c' %} compiler = meson.get_compiler('c') -{%- elif cookiecutter.language == 'c++' %} +{%- elif language == 'c++' %} compiler = meson.get_compiler('cpp') -{%- elif cookiecutter.language == 'fortran' %} +{%- elif language == 'fortran' %} compiler = meson.get_compiler('fortran') {%- endif %} @@ -32,13 +32,13 @@ numpy_inc = run_command( ).stdout().strip() incs = include_directories( [ - '{{ cookiecutter.package_name }}/lib', + '{{ package_name }}/lib', # python_inc, numpy_inc, ] ) -{% set dependency_list = cookiecutter.package_requirements.split(',') -%} +{% set dependency_list = package_requirements.split(',') -%} deps = [ {%- for dependency in dependency_list if dependency != '' %} compiler.find_library('{{ dependency }}'), @@ -47,53 +47,53 @@ deps = [ # Files get copied to /site-packages/ install_pkg_srcs = [ - '{{ cookiecutter.package_name }}/__init__.py', - '{{ cookiecutter.package_name }}/_bmi.py', - '{{ cookiecutter.package_name }}/_version.py', + '{{ package_name }}/__init__.py', + '{{ package_name }}/_bmi.py', + '{{ package_name }}/_version.py', ] py.install_sources( install_pkg_srcs, - subdir: '{{ cookiecutter.package_name }}', + subdir: '{{ package_name }}', ) install_lib_srcs = [ - '{{ cookiecutter.package_name }}/lib/__init__.py', -{%- for babelized_class in cookiecutter.components|list|sort %} - '{{ cookiecutter.package_name }}/lib/{{ babelized_class|lower }}.pyx', + '{{ package_name }}/lib/__init__.py', +{%- for babelized_class in components|list|sort %} + '{{ package_name }}/lib/{{ babelized_class|lower }}.pyx', {%- endfor %} ] py.install_sources( install_lib_srcs, - subdir: '{{ cookiecutter.package_name }}/lib', + subdir: '{{ package_name }}/lib', ) -{%- for babelized_class, component in cookiecutter.components|dictsort %} +{%- for babelized_class, component in components|dictsort %} py.extension_module( '{{ babelized_class|lower }}', [ -{%- if cookiecutter.language == 'fortran' %} - '{{ cookiecutter.package_name }}/lib/bmi_interoperability.f90', +{%- if language == 'fortran' %} + '{{ package_name }}/lib/bmi_interoperability.f90', {%- endif %} - '{{ cookiecutter.package_name }}/lib/{{ babelized_class|lower }}.pyx', + '{{ package_name }}/lib/{{ babelized_class|lower }}.pyx', ], dependencies: [ dependency('{{ component.library }}', method : 'pkg-config'), ], include_directories: incs, install: true, - subdir: '{{ cookiecutter.package_name }}/lib', -{%- if cookiecutter.language == 'c++' %} + subdir: '{{ package_name }}/lib', +{%- if language == 'c++' %} override_options : ['cython_language=cpp'], {%- endif %} ) install_subdir( 'meta/{{ babelized_class }}', - install_dir: py.get_install_dir() / '{{ cookiecutter.package_name }}/data', + install_dir: py.get_install_dir() / '{{ package_name }}/data', ) {%- endfor %} # This is a temporary fix for editable installs. -run_command('cp', '-r', '{{ cookiecutter.package_name }}/data', 'build') +run_command('cp', '-r', '{{ package_name }}/data', 'build') diff --git a/babelizer/data/templates/noxfile.py.jinja b/babelizer/data/templates/noxfile.py.jinja index 756f3c9..931178e 100644 --- a/babelizer/data/templates/noxfile.py.jinja +++ b/babelizer/data/templates/noxfile.py.jinja @@ -5,7 +5,7 @@ from itertools import chain import nox -PROJECT = "{{ cookiecutter.package_name }}" +PROJECT = "{{ package_name }}" ROOT = pathlib.Path(__file__).parent PYTHON_VERSIONS = ["3.9", "3.10", "3.11"] @@ -19,10 +19,10 @@ def test(session: nox.Session) -> None: session.install(".[testing]") -{%- for babelized_class, _ in cookiecutter.components|dictsort %} +{%- for babelized_class, _ in components|dictsort %} session.run( "bmi-test", - "{{ cookiecutter.package_name }}.bmi:{{ babelized_class }}", + "{{ package_name }}.bmi:{{ babelized_class }}", "-vvv", ) {%- endfor %} @@ -80,7 +80,7 @@ def build_docs(session: nox.Session) -> None: "--module-first", "-o", "docs/api", - "{{ cookiecutter.package_name }}", + "{{ package_name }}", ) session.run( "sphinx-build", @@ -106,7 +106,7 @@ def live_docs(session: nox.Session) -> None: "docs/_templates", "-o", "docs/api", - "{{ cookiecutter.package_name }}", + "{{ package_name }}", ) session.run( "sphinx-autobuild", @@ -188,5 +188,5 @@ def clean_docs(session: nox.Session) -> None: shutil.rmtree("html") with session.chdir(ROOT / "docs"): - for p in pathlib.Path("api").rglob("{{ cookiecutter.package_name }}*.rst"): + for p in pathlib.Path("api").rglob("{{ package_name }}*.rst"): p.unlink() diff --git a/babelizer/data/templates/pyproject.toml b/babelizer/data/templates/pyproject.toml index 7df1370..6543775 100644 --- a/babelizer/data/templates/pyproject.toml +++ b/babelizer/data/templates/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -{%- if cookiecutter.language == 'python' %} +{%- if language == 'python' %} build-backend = "setuptools.build_meta" requires = [ "setuptools >=61", @@ -10,19 +10,19 @@ requires = ["cython", "numpy", "meson-python", "wheel"] {% endif %} [project] -name = "{{cookiecutter.package_name}}" +name = "{{package_name}}" authors = [ - {name = "{{cookiecutter.info.full_name}}", email = "{{cookiecutter.info.email}}"}, + {name = "{{info.full_name}}", email = "{{info.email}}"}, ] maintainers = [ - {name = "{{cookiecutter.info.full_name}}", email = "{{cookiecutter.info.email}}"}, + {name = "{{info.full_name}}", email = "{{info.email}}"}, ] -description = "PyMT plugin for {{cookiecutter.package_name}}" +description = "PyMT plugin for {{package_name}}" license = {text = "MIT License"} classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Science/Research", - "License :: OSI Approved :: {{ cookiecutter.open_source_license }}", + "License :: OSI Approved :: {{ open_source_license }}", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3 :: Only", @@ -38,11 +38,11 @@ dependencies = [ ] [project.urls] -homepage = "https://github.com/{{ cookiecutter.info.github_username }}/{{ cookiecutter.package_name }}" +homepage = "https://github.com/{{ info.github_username }}/{{ package_name }}" [project.entry-points."pymt.plugins"] -{%- for babelized_class, _ in cookiecutter.components|dictsort %} -{{ babelized_class }} = "{{ cookiecutter.package_name }}.bmi:{{ babelized_class }}" +{%- for babelized_class, _ in components|dictsort %} +{{ babelized_class }} = "{{ package_name }}.bmi:{{ babelized_class }}" {%- endfor %} [project.optional-dependencies] @@ -65,15 +65,15 @@ testing = [ "bmi-tester>=0.5.4", ] -{%- if cookiecutter.language == 'python' %} +{%- if language == 'python' %} [tool.setuptools.packages.find] where = ["."] -include = ["{{cookiecutter.package_name}}*"] +include = ["{{package_name}}*"] {% endif %} [tool.pytest.ini_options] minversion = "5.0" -testpaths = ["{{ cookiecutter.package_name }}", "tests"] +testpaths = ["{{ package_name }}", "tests"] norecursedirs = [".*", "*.egg*", "build", "dist"] addopts = """ --ignore setup.py @@ -98,20 +98,20 @@ line_length = 88 [tool.check-manifest] ignore = [ - "{{ cookiecutter.package_name }}/data", - "{{ cookiecutter.package_name }}/data/**/*", + "{{ package_name }}/data", + "{{ package_name }}/data/**/*", ] [tool.towncrier] directory = "news" -package = "{{ cookiecutter.package_name }}" +package = "{{ package_name }}" filename = "CHANGES.rst" single_file = true underlines = "-^\"" -issue_format = "`#{issue} `_" +issue_format = "`#{issue} `_" title_format = "{version} ({project_date})" wrap = true [tool.zest-releaser] tag-format = "v{version}" -python-file-with-version = "{{ cookiecutter.package_name }}/_version.py" +python-file-with-version = "{{ package_name }}/_version.py" diff --git a/babelizer/data/templates/requirements-build.txt b/babelizer/data/templates/requirements-build.txt index c92dc98..c568365 100644 --- a/babelizer/data/templates/requirements-build.txt +++ b/babelizer/data/templates/requirements-build.txt @@ -1,11 +1,11 @@ # conda requirements needed to build the project -{%- if cookiecutter.language == 'c' -%} +{% if language == 'c' -%} bmi-c c-compiler -{%- elif cookiecutter.language == 'c++' -%} +{%- elif language == 'c++' -%} bmi-cxx cxx-compiler -{%- elif cookiecutter.language == 'fortran' -%} +{%- elif language == 'fortran' -%} bmi-fortran fortran-compiler {%- endif %} diff --git a/babelizer/data/templates/requirements-library.txt b/babelizer/data/templates/requirements-library.txt index 306c4bf..7fcdbe5 100644 --- a/babelizer/data/templates/requirements-library.txt +++ b/babelizer/data/templates/requirements-library.txt @@ -1,3 +1,3 @@ -{%- for requirement in cookiecutter.package_requirements.split(',') %} +{%- for requirement in package_requirements.split(',') -%} {{ requirement|trim }} {%- endfor %} diff --git a/babelizer/data/templates/{{cookiecutter.package_name}}/__init__.py.jinja b/babelizer/data/templates/{{cookiecutter.package_name}}/__init__.py.jinja deleted file mode 100644 index ae1237e..0000000 --- a/babelizer/data/templates/{{cookiecutter.package_name}}/__init__.py.jinja +++ /dev/null @@ -1 +0,0 @@ -{{ cookiecutter.files['__init__.py'] }} diff --git a/babelizer/data/templates/{{cookiecutter.package_name}}/_bmi.py.jinja b/babelizer/data/templates/{{cookiecutter.package_name}}/_bmi.py.jinja deleted file mode 100644 index 8aeff82..0000000 --- a/babelizer/data/templates/{{cookiecutter.package_name}}/_bmi.py.jinja +++ /dev/null @@ -1 +0,0 @@ -{{cookiecutter.files['_bmi.py']}} diff --git a/babelizer/data/templates/{{cookiecutter.package_name}}/_version.py.jinja b/babelizer/data/templates/{{cookiecutter.package_name}}/_version.py.jinja deleted file mode 100644 index f697ff6..0000000 --- a/babelizer/data/templates/{{cookiecutter.package_name}}/_version.py.jinja +++ /dev/null @@ -1 +0,0 @@ -__version__ = "{{cookiecutter.package_version}}" diff --git a/babelizer/data/templates/{{cookiecutter.package_name}}/lib/__init__.py.jinja b/babelizer/data/templates/{{cookiecutter.package_name}}/lib/__init__.py.jinja deleted file mode 100644 index fbbeb6b..0000000 --- a/babelizer/data/templates/{{cookiecutter.package_name}}/lib/__init__.py.jinja +++ /dev/null @@ -1 +0,0 @@ -{{ cookiecutter.files['lib/__init__.py'] }} diff --git a/babelizer/data/templates/{{package_name}}/__init__.py.jinja b/babelizer/data/templates/{{package_name}}/__init__.py.jinja new file mode 100644 index 0000000..87338d6 --- /dev/null +++ b/babelizer/data/templates/{{package_name}}/__init__.py.jinja @@ -0,0 +1 @@ +{{ files['__init__.py'] }} diff --git a/babelizer/data/templates/{{package_name}}/_bmi.py.jinja b/babelizer/data/templates/{{package_name}}/_bmi.py.jinja new file mode 100644 index 0000000..6055b3d --- /dev/null +++ b/babelizer/data/templates/{{package_name}}/_bmi.py.jinja @@ -0,0 +1 @@ +{{ files['_bmi.py'] }} diff --git a/babelizer/data/templates/{{package_name}}/_version.py.jinja b/babelizer/data/templates/{{package_name}}/_version.py.jinja new file mode 100644 index 0000000..4399817 --- /dev/null +++ b/babelizer/data/templates/{{package_name}}/_version.py.jinja @@ -0,0 +1 @@ +__version__ = "{{ package_version }}" diff --git a/babelizer/data/templates/{{package_name}}/lib/__init__.py.jinja b/babelizer/data/templates/{{package_name}}/lib/__init__.py.jinja new file mode 100644 index 0000000..637cf89 --- /dev/null +++ b/babelizer/data/templates/{{package_name}}/lib/__init__.py.jinja @@ -0,0 +1 @@ +{{ files['lib/__init__.py'] }} diff --git a/babelizer/data/templates/{{cookiecutter.package_name}}/lib/_c.pyx b/babelizer/data/templates/{{package_name}}/lib/_c.pyx similarity index 99% rename from babelizer/data/templates/{{cookiecutter.package_name}}/lib/_c.pyx rename to babelizer/data/templates/{{package_name}}/lib/_c.pyx index 6c9216c..57ce1d7 100644 --- a/babelizer/data/templates/{{cookiecutter.package_name}}/lib/_c.pyx +++ b/babelizer/data/templates/{{package_name}}/lib/_c.pyx @@ -90,7 +90,7 @@ def ok_or_raise(status): if status != 0: raise RuntimeError('error code {status}'.format(status=status)) -{%- for babelized_class, component in cookiecutter.components|dictsort %} +{%- for babelized_class, component in components|dictsort %} # start: {{ babelized_class|lower }}.pyx diff --git a/babelizer/data/templates/{{cookiecutter.package_name}}/lib/_cxx.pyx b/babelizer/data/templates/{{package_name}}/lib/_cxx.pyx similarity index 98% rename from babelizer/data/templates/{{cookiecutter.package_name}}/lib/_cxx.pyx rename to babelizer/data/templates/{{package_name}}/lib/_cxx.pyx index 01c4bdf..56d83df 100644 --- a/babelizer/data/templates/{{cookiecutter.package_name}}/lib/_cxx.pyx +++ b/babelizer/data/templates/{{package_name}}/lib/_cxx.pyx @@ -7,7 +7,7 @@ from libcpp.string cimport string from libcpp.vector cimport vector import numpy as np -{%- for babelized_class, component in cookiecutter.components|dictsort %} +{%- for babelized_class, component in components|dictsort %} # start: {{ babelized_class|lower }}.pyx diff --git a/babelizer/data/templates/{{cookiecutter.package_name}}/lib/_fortran.pyx b/babelizer/data/templates/{{package_name}}/lib/_fortran.pyx similarity index 99% rename from babelizer/data/templates/{{cookiecutter.package_name}}/lib/_fortran.pyx rename to babelizer/data/templates/{{package_name}}/lib/_fortran.pyx index 47726f2..674749c 100644 --- a/babelizer/data/templates/{{cookiecutter.package_name}}/lib/_fortran.pyx +++ b/babelizer/data/templates/{{package_name}}/lib/_fortran.pyx @@ -123,7 +123,7 @@ cpdef to_string(bytes): except AttributeError: return bytes -{%- for babelized_class in cookiecutter.components %} +{%- for babelized_class in components %} # start: {{ babelized_class|lower }}.pyx diff --git a/babelizer/data/templates/{{cookiecutter.package_name}}/lib/bmi_interoperability.f90 b/babelizer/data/templates/{{package_name}}/lib/bmi_interoperability.f90 similarity index 99% rename from babelizer/data/templates/{{cookiecutter.package_name}}/lib/bmi_interoperability.f90 rename to babelizer/data/templates/{{package_name}}/lib/bmi_interoperability.f90 index 91ba304..6995dcc 100644 --- a/babelizer/data/templates/{{cookiecutter.package_name}}/lib/bmi_interoperability.f90 +++ b/babelizer/data/templates/{{package_name}}/lib/bmi_interoperability.f90 @@ -3,13 +3,13 @@ ! module bmi_interoperability -{%- for _, component in cookiecutter.components|dictsort %} +{%- for _, component in components|dictsort %} use, intrinsic :: iso_c_binding use bmif_2_0 -{%- if cookiecutter.build.libraries %} -{%- for lib in cookiecutter.build.libraries %} +{%- if build.libraries %} +{%- for lib in build.libraries %} use {{ lib }} {%- endfor %} {%- endif %} diff --git a/babelizer/data/templates/{{cookiecutter.package_name}}/lib/bmi_interoperability.h b/babelizer/data/templates/{{package_name}}/lib/bmi_interoperability.h similarity index 100% rename from babelizer/data/templates/{{cookiecutter.package_name}}/lib/bmi_interoperability.h rename to babelizer/data/templates/{{package_name}}/lib/bmi_interoperability.h From f061df8cc509f6f0c790a0cd2ce440dfa565e91a Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sun, 31 Mar 2024 16:14:34 -0400 Subject: [PATCH 14/38] remove cookiecutter context from metadata --- babelizer/_files/readme.py | 2 +- babelizer/cli.py | 32 +++++++++++++++----------------- babelizer/metadata.py | 8 ++++++++ babelizer/render.py | 23 ++++++++++------------- 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/babelizer/_files/readme.py b/babelizer/_files/readme.py index f9cdbf9..39d9e15 100644 --- a/babelizer/_files/readme.py +++ b/babelizer/_files/readme.py @@ -10,6 +10,6 @@ def render(context: dict[str, Any]) -> str: env = Environment(loader=FileSystemLoader(get_datadir())) - template = env.get_template("{{cookiecutter.package_name}}/README.rst") + template = env.get_template("templates/README.rst") return template.render(**context) diff --git a/babelizer/cli.py b/babelizer/cli.py index 8d828b7..8c4bbef 100644 --- a/babelizer/cli.py +++ b/babelizer/cli.py @@ -68,7 +68,7 @@ def babelize(cd: str) -> None: @click.option( "--template", default=None, - help="Location of cookiecutter template", + help="Location of templates", ) @click.option( "--package-version", @@ -136,7 +136,7 @@ def init( @click.option( "--template", default=None, - help="Location of cookiecutter template", + help="Location of templates", ) @click.option( "--set-version", default=None, help="Set the version of the updated package" @@ -274,21 +274,19 @@ def sample_meson_build(extension: Collection[str]) -> None: @babelize.command() def sample_readme() -> None: context = { - "cookiecutter": { - "language": "python", - "open_source_license": "MIT License", - "package_name": "springfield_monorail", - "info": { - "github_username": "lyle-lanley", - "package_author": "Lyle Lanley", - "summary": "A Monorail!", - "package_license": "MIT License", - }, - "components": { - "Monorail": {"library": "monorail"}, - "Rail": {"library": "rail"}, - }, - } + "language": "python", + "open_source_license": "MIT License", + "package_name": "springfield_monorail", + "info": { + "github_username": "lyle-lanley", + "package_author": "Lyle Lanley", + "summary": "A Monorail!", + "package_license": "MIT License", + }, + "components": { + "Monorail": {"library": "monorail"}, + "Rail": {"library": "rail"}, + }, } print(render_readme(context)) diff --git a/babelizer/metadata.py b/babelizer/metadata.py index f66d1a2..96d7998 100644 --- a/babelizer/metadata.py +++ b/babelizer/metadata.py @@ -330,8 +330,12 @@ def norm(config: dict[str, Any]) -> dict[str, Any]: if "all" in config["ci"]["os"]: config["ci"] = ["linux", "mac", "windows"] + languages = [lib["language"] for lib in config["library"].values()] + language = languages[0] + return { "library": libraries, + "components": config["library"], "build": { "undef_macros": build["undef_macros"], "define_macros": build["define_macros"], @@ -349,6 +353,10 @@ def norm(config: dict[str, Any]) -> dict[str, Any]: "python_version": sorted(config["ci"]["python_version"]), "os": sorted(config["ci"]["os"]), }, + "package_name": config["package"]["name"], + "package_requirements": ",".join(config["package"]["requirements"]), + "language": language, + "open_source_license": config["info"]["package_license"], } def dump(self, fp: io.TextIOBase, fmt: str = "toml") -> None: diff --git a/babelizer/render.py b/babelizer/render.py index 779fee5..9dd1c37 100644 --- a/babelizer/render.py +++ b/babelizer/render.py @@ -50,19 +50,16 @@ def render( template = get_datadir() context = { - "cookiecutter": { - "files": { - "_bmi.py": render_bmi(plugin_metadata), - "__init__.py": render_init(plugin_metadata), - "lib/__init__.py": render_lib_init(plugin_metadata), - ".gitignore": render_gitignore(plugin_metadata), - "LICENSE.rst": render_license(plugin_metadata), - }, - "now": datetime.datetime.now(), - "package_version": version, - } - | plugin_metadata.as_cookiecutter_context() - } + "files": { + "_bmi.py": render_bmi(plugin_metadata), + "__init__.py": render_init(plugin_metadata), + "lib/__init__.py": render_lib_init(plugin_metadata), + ".gitignore": render_gitignore(plugin_metadata), + "LICENSE.rst": render_license(plugin_metadata), + }, + "now": datetime.datetime.now(), + "package_version": version, + } | {k: plugin_metadata[k] for k in plugin_metadata} cookiecutter( template, From d9b6aee172ae4441333f20ab134706ae011abfa8 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sun, 31 Mar 2024 16:27:46 -0400 Subject: [PATCH 15/38] remove calls to cookiecutter package --- babelizer/metadata.py | 38 ------------ babelizer/render.py | 137 ++++++++---------------------------------- 2 files changed, 24 insertions(+), 151 deletions(-) diff --git a/babelizer/metadata.py b/babelizer/metadata.py index 96d7998..9d47561 100644 --- a/babelizer/metadata.py +++ b/babelizer/metadata.py @@ -436,41 +436,3 @@ def parse_entry_point(specifier: str) -> tuple[str, str, str]: ) from None return name, module, obj - - def as_cookiecutter_context(self) -> dict[str, Any]: - """Format metadata in cookiecutter context. - - Returns - ------- - dict - Metadata in cookiecutter context. - """ - languages = [lib["language"] for lib in self._meta["library"].values()] - language = languages[0] - platforms = [_norm_os(name) for name in self._meta["ci"]["os"]] - - return { - "components": self._meta["library"], - "build": { - "undef_macros": self._meta["build"]["undef_macros"], - "define_macros": self._meta["build"]["define_macros"], - "libraries": self._meta["build"]["libraries"], - "library_dirs": self._meta["build"]["library_dirs"], - "include_dirs": self._meta["build"]["include_dirs"], - "extra_compile_args": self._meta["build"]["extra_compile_args"], - }, - "info": { - "full_name": self._meta["info"]["package_author"], - "email": self._meta["info"]["package_author_email"], - "github_username": self._meta["info"]["github_username"], - "project_short_description": self._meta["info"]["summary"], - }, - "ci": { - "os": platforms, - "python_version": self._meta["ci"]["python_version"], - }, - "package_name": self._meta["package"]["name"], - "package_requirements": ",".join(self._meta["package"]["requirements"]), - "language": language, - "open_source_license": self._meta["info"]["package_license"], - } diff --git a/babelizer/render.py b/babelizer/render.py index 9dd1c37..6fa0d22 100644 --- a/babelizer/render.py +++ b/babelizer/render.py @@ -8,8 +8,6 @@ from typing import Any import git -from cookiecutter.exceptions import OutputDirExistsException -from cookiecutter.main import cookiecutter from babelizer.metadata import BabelMetadata @@ -26,6 +24,7 @@ else: # pragma: no cover ( str: - from babelizer._cookiecutter import cookiecutter - - if template is None: - template = get_datadir() - - context = { - "files": { - "_bmi.py": render_bmi(plugin_metadata), - "__init__.py": render_init(plugin_metadata), - "lib/__init__.py": render_lib_init(plugin_metadata), - ".gitignore": render_gitignore(plugin_metadata), - "LICENSE.rst": render_license(plugin_metadata), - }, - "now": datetime.datetime.now(), - "package_version": version, - } | {k: plugin_metadata[k] for k in plugin_metadata} - - cookiecutter( - template, - extra_context=context, - output_dir=output, - no_input=True, - overwrite_if_exists=clobber, - ) - - path = os.path.realpath(output) - - with open(os.path.join(path, "babel.toml"), "w") as fp: - plugin_metadata.dump(fp, fmt="toml") - - git.Repo.init(path) - - return path - - -def render_with_cookiecutter( - plugin_metadata: BabelMetadata, - output: str, - template: str | None = None, - clobber: bool = False, - version: str = "0.1", - make_pretty: bool = False, ) -> str: """Generate a babelized library. @@ -117,78 +73,33 @@ def render_with_cookiecutter( if template is None: template = get_datadir() - context = plugin_metadata.as_cookiecutter_context() - context["files"] = { - "_bmi.py": render_bmi(plugin_metadata), - "__init__.py": render_init(plugin_metadata), - "lib/__init__.py": render_lib_init(plugin_metadata), - ".gitignore": render_gitignore(plugin_metadata), - "LICENSE.rst": render_license(plugin_metadata), - } - - try: - path = render_plugin_repo( - template, - context=dict(context, package_version=version), - output_dir=output, - clobber=clobber, - ) - except OutputDirExistsException as err: - raise OutputDirExistsError(", ".join(err.args)) - - with open(os.path.join(path, "babel.toml"), "w") as fp: - plugin_metadata.dump(fp, fmt="toml") - - if make_pretty and MAKE_PRETTY: - prettify_python(path) - - return os.path.realpath(path) + context = { + "files": { + "_bmi.py": render_bmi(plugin_metadata), + "__init__.py": render_init(plugin_metadata), + "lib/__init__.py": render_lib_init(plugin_metadata), + ".gitignore": render_gitignore(plugin_metadata), + "LICENSE.rst": render_license(plugin_metadata), + }, + "now": datetime.datetime.now(), + "package_version": version, + } | {k: plugin_metadata[k] for k in plugin_metadata} + if os.path.exists(output): + raise OutputDirExistsError(output) -def render_plugin_repo( - template: str, - context: dict[str, Any] | None = None, - output_dir: str = ".", - clobber: bool = False, -) -> str: - """Render a repository for a pymt plugin. + cookiecutter( + template, + extra_context=context, + output_dir=output, + no_input=True, + overwrite_if_exists=clobber, + ) - Parameters - ---------- - template: str - Path (or URL) to the cookiecutter template to use. - context: dict, optional - Context for the new repository. - output_dir : str, optional - Name of the directory that will be the new repository. - clobber: bool, optional - If a like-named repository already exists, overwrite it. + path = os.path.realpath(output) - Returns - ------- - path - Absolute path to the newly-created repository. - """ - context = context or {} - - try: - cookiecutter( - template, - extra_context=context, - output_dir=output_dir, - no_input=True, - overwrite_if_exists=clobber, - ) - except OutputDirExistsException as err: - raise OutputDirExistsError(", ".join(err.args)) - - name = context["package_name"] - - # path = os.path.join(output_dir, "{}".format(context["package_name"])) - # if not os.path.isdir(path): - path = os.path.join(output_dir, name) - if not os.path.isdir(path): - raise RenderError(f"error creating {path}") + with open(os.path.join(path, "babel.toml"), "w") as fp: + plugin_metadata.dump(fp, fmt="toml") git.Repo.init(path) From 9bdceefd319a4697e587b0a57af8ad2b16ae0efe Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sun, 31 Mar 2024 16:28:05 -0400 Subject: [PATCH 16/38] remove cookiecutter dependency --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 47a72c4..1ef5855 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,6 @@ classifiers = [ ] dependencies = [ "click", - "cookiecutter", "gitpython", "importlib-resources; python_version < '3.12'", "jinja2", From f6727e55fbbaa6f14773bbefeb55df3d770b6f78 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sun, 31 Mar 2024 16:30:38 -0400 Subject: [PATCH 17/38] remove prettify_python and black, isort dependencies --- babelizer/render.py | 52 --------------------------------------------- pyproject.toml | 4 ---- 2 files changed, 56 deletions(-) diff --git a/babelizer/render.py b/babelizer/render.py index 6fa0d22..824031a 100644 --- a/babelizer/render.py +++ b/babelizer/render.py @@ -11,14 +11,6 @@ from babelizer.metadata import BabelMetadata -try: - import black as blk - import isort -except ModuleNotFoundError: - MAKE_PRETTY = False -else: - MAKE_PRETTY = True - if sys.version_info >= (3, 11): # pragma: no cover (PY11+) import tomllib else: # pragma: no cover ( None: - """Format a Python file with ``black``. - - Parameters - ---------- - filepath : str - Path-like object to a Python file. - """ - with open(filepath) as fp: - try: - new_contents = blk.format_file_contents( - fp.read(), fast=True, mode=blk.FileMode() - ) - except blk.NothingChanged: - new_contents = None - if new_contents: - with open(filepath, "w") as fp: - fp.write(new_contents) - - -def prettify_python(path_to_repo: str) -> None: - """Format files in babelized project with ``black``. - - Parameters - ---------- - path_to_repo : str - Path-like object to babelized project. - """ - with open(os.path.join(path_to_repo, "babel.toml")) as fp: - meta = tomllib.loads(fp.read()) - module_name = meta["package"]["name"] - - files_to_fix = [ - os.path.join(path_to_repo, module_name, "_bmi.py"), - os.path.join(path_to_repo, module_name, "__init__.py"), - os.path.join(path_to_repo, "docs", "conf.py"), - ] - - config = isort.Config(quiet=True) - for file_to_fix in files_to_fix: - isort.api.sort_file(file_to_fix, config=config) - blacken_file(file_to_fix) diff --git a/pyproject.toml b/pyproject.toml index 1ef5855..5601634 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,10 +65,6 @@ dev = [ "pre-commit", "towncrier", ] -format = [ - "black", - "isort>=5", -] docs = [ "furo", "pygments>=2.4", From 5c4f3b2a2fff3380fd472fa646d3560ce2396f75 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 1 Apr 2024 13:21:31 -0600 Subject: [PATCH 18/38] add get_template_dir function --- babelizer/_datadir.py | 4 ++++ babelizer/_files/readme.py | 7 +++++-- babelizer/cli.py | 8 ++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/babelizer/_datadir.py b/babelizer/_datadir.py index afb08e3..00fc70b 100644 --- a/babelizer/_datadir.py +++ b/babelizer/_datadir.py @@ -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") diff --git a/babelizer/_files/readme.py b/babelizer/_files/readme.py index 39d9e15..c6979ae 100644 --- a/babelizer/_files/readme.py +++ b/babelizer/_files/readme.py @@ -4,12 +4,15 @@ from jinja2 import Environment from jinja2 import FileSystemLoader +from jinja2 import StrictUndefined -from babelizer._datadir import get_datadir +from babelizer._datadir import get_template_dir def render(context: dict[str, Any]) -> str: - env = Environment(loader=FileSystemLoader(get_datadir())) + env = Environment( + loader=FileSystemLoader(get_template_dir()), undefined=StrictUndefined + ) template = env.get_template("templates/README.rst") return template.render(**context) diff --git a/babelizer/cli.py b/babelizer/cli.py index 8c4bbef..4a7781e 100644 --- a/babelizer/cli.py +++ b/babelizer/cli.py @@ -14,7 +14,7 @@ import click import git -from babelizer._datadir import get_datadir +from babelizer._datadir import get_template_dir from babelizer._files.gitignore import render as render_gitignore from babelizer._files.license_rst import render as render_license from babelizer._files.meson_build import render as render_meson_build @@ -83,7 +83,7 @@ def init( META is babelizer configuration information, usually saved to a file. """ - template = template or os.path.join(get_datadir(), "templates") + template = template or get_template_dir() if not quiet: out(f"reading template from {template}") @@ -160,7 +160,7 @@ def update( err("this does not appear to be a babelized folder (missing 'babel.toml')") raise click.Abort() - template = template or get_datadir() + template = template or get_template_dir() if not quiet: out(f"reading template from {template}") @@ -276,7 +276,6 @@ def sample_readme() -> None: context = { "language": "python", "open_source_license": "MIT License", - "package_name": "springfield_monorail", "info": { "github_username": "lyle-lanley", "package_author": "Lyle Lanley", @@ -287,6 +286,7 @@ def sample_readme() -> None: "Monorail": {"library": "monorail"}, "Rail": {"library": "rail"}, }, + "package": {"name": "springfield_monorail"}, } print(render_readme(context)) From 73fc90c97a3487365dedc1a53f401392db67728e Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 1 Apr 2024 13:22:27 -0600 Subject: [PATCH 19/38] raise error for undefined variables in templates --- babelizer/_cookiecutter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/babelizer/_cookiecutter.py b/babelizer/_cookiecutter.py index a34bb77..6028331 100644 --- a/babelizer/_cookiecutter.py +++ b/babelizer/_cookiecutter.py @@ -6,6 +6,7 @@ from jinja2 import Environment from jinja2 import FileSystemLoader +from jinja2 import StrictUndefined from jinja2 import Template from babelizer._post_hook import run @@ -21,7 +22,7 @@ def cookiecutter( ) -> None: if extra_context is None: extra_context = {} - env = Environment(loader=FileSystemLoader(template)) + env = Environment(loader=FileSystemLoader(template), undefined=StrictUndefined) def datetime_format(value: datetime, format_: str = "%Y-%M-%D") -> str: return value.strftime(format_) From a2232abb5ef2667c108af0038f84209605a74659 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Mon, 1 Apr 2024 13:25:07 -0600 Subject: [PATCH 20/38] clean up variable names in templates --- babelizer/_post_hook.py | 2 +- .../data/templates/.github/workflows/test.yml | 4 +- babelizer/data/templates/CREDITS.rst | 2 +- babelizer/data/templates/MANIFEST.in | 8 +-- babelizer/data/templates/README.rst | 50 ++++++++----------- babelizer/data/templates/docs/Makefile | 2 +- babelizer/data/templates/docs/api/.gitignore | 2 +- babelizer/data/templates/docs/conf.py.jinja | 20 ++++---- .../data/templates/docs/developer_install.rst | 22 ++++---- .../data/templates/docs/environments.rst | 8 +-- babelizer/data/templates/docs/index.rst | 10 ++-- babelizer/data/templates/docs/quickstart.rst | 10 ++-- babelizer/data/templates/meson.build | 30 +++++------ babelizer/data/templates/noxfile.py.jinja | 10 ++-- babelizer/data/templates/pyproject.toml | 28 +++++------ .../data/templates/requirements-library.txt | 2 +- .../__init__.py.jinja | 0 .../_bmi.py.jinja | 0 .../_version.py.jinja | 0 .../lib/__init__.py.jinja | 0 .../lib/_c.pyx | 0 .../lib/_cxx.pyx | 0 .../lib/_fortran.pyx | 0 .../lib/bmi_interoperability.f90 | 0 .../lib/bmi_interoperability.h | 0 babelizer/metadata.py | 3 -- babelizer/render.py | 15 ++---- 27 files changed, 105 insertions(+), 123 deletions(-) rename babelizer/data/templates/{{{package_name}} => {{package.name}}}/__init__.py.jinja (100%) rename babelizer/data/templates/{{{package_name}} => {{package.name}}}/_bmi.py.jinja (100%) rename babelizer/data/templates/{{{package_name}} => {{package.name}}}/_version.py.jinja (100%) rename babelizer/data/templates/{{{package_name}} => {{package.name}}}/lib/__init__.py.jinja (100%) rename babelizer/data/templates/{{{package_name}} => {{package.name}}}/lib/_c.pyx (100%) rename babelizer/data/templates/{{{package_name}} => {{package.name}}}/lib/_cxx.pyx (100%) rename babelizer/data/templates/{{{package_name}} => {{package.name}}}/lib/_fortran.pyx (100%) rename babelizer/data/templates/{{{package_name}} => {{package.name}}}/lib/bmi_interoperability.f90 (100%) rename babelizer/data/templates/{{{package_name}} => {{package.name}}}/lib/bmi_interoperability.h (100%) diff --git a/babelizer/_post_hook.py b/babelizer/_post_hook.py index 98d69ea..6f48e7a 100644 --- a/babelizer/_post_hook.py +++ b/babelizer/_post_hook.py @@ -104,7 +104,7 @@ def remove_trailing_whitespace(path: str | Path) -> None: def run(context: dict[str, Any]) -> None: PROJECT_DIRECTORY = Path.cwd().resolve() - package_name = context["package_name"] + package_name = context["package"]["name"] language = context["language"] LIB_DIRECTORY = PROJECT_DIRECTORY / Path(package_name, "lib") diff --git a/babelizer/data/templates/.github/workflows/test.yml b/babelizer/data/templates/.github/workflows/test.yml index 973f34d..469cd6c 100644 --- a/babelizer/data/templates/.github/workflows/test.yml +++ b/babelizer/data/templates/.github/workflows/test.yml @@ -51,7 +51,7 @@ jobs: - name: Test run: | - python -c 'import {{ package_name }}' + python -c 'import {{ package.name }}' {%- for babelized_class in components %} - bmi-test {{ package_name }}.bmi:{{ babelized_class }} -vvv + bmi-test {{ package.name }}.bmi:{{ babelized_class }} -vvv {%- endfor %} diff --git a/babelizer/data/templates/CREDITS.rst b/babelizer/data/templates/CREDITS.rst index 1ae8d38..26e2150 100644 --- a/babelizer/data/templates/CREDITS.rst +++ b/babelizer/data/templates/CREDITS.rst @@ -1,4 +1,4 @@ Credits ======= -* {{ info.full_name }} <{{ info.email }}> +* {{ info.package_author }} <{{ info.package_author_email }}> diff --git a/babelizer/data/templates/MANIFEST.in b/babelizer/data/templates/MANIFEST.in index 52020e2..70075f7 100644 --- a/babelizer/data/templates/MANIFEST.in +++ b/babelizer/data/templates/MANIFEST.in @@ -1,15 +1,15 @@ -recursive-include {{ package_name }}/data * +recursive-include {{ package.name }}/data * include LICENSE include requirements.txt include *.rst include *.txt include Makefile include babel.toml -recursive-include {{ package_name }} *.pyx +recursive-include {{ package.name }} *.pyx recursive-include docs *.py recursive-include docs *.rst recursive-include docs Makefile recursive-exclude meta * recursive-exclude recipe * -recursive-exclude {{ package_name }} *.cpp -recursive-exclude {{ package_name }} *.c +recursive-exclude {{ package.name }} *.cpp +recursive-exclude {{ package.name }} *.c diff --git a/babelizer/data/templates/README.rst b/babelizer/data/templates/README.rst index e2d991d..204291f 100644 --- a/babelizer/data/templates/README.rst +++ b/babelizer/data/templates/README.rst @@ -1,30 +1,26 @@ -{{ '=' * package_name | length }} -{{ package_name }} -{{ '=' * package_name | length }} +{{ '=' * package.name | length }} +{{ package.name }} +{{ '=' * package.name | length }} -{% set is_open_source = open_source_license != 'Not open source' -%} - -{% if is_open_source %} .. image:: https://img.shields.io/badge/CSDMS-Basic%20Model%20Interface-green.svg :target: https://bmi.readthedocs.io/ :alt: Basic Model Interface -.. image:: https://img.shields.io/badge/recipe-{{ package_name }}-green.svg - :target: https://anaconda.org/conda-forge/{{ package_name }} +.. image:: https://img.shields.io/badge/recipe-{{ package.name }}-green.svg + :target: https://anaconda.org/conda-forge/{{ package.name }} -.. image:: https://readthedocs.org/projects/{{ package_name | replace("_", "-") }}/badge/?version=latest - :target: https://{{ package_name | replace("_", "-") }}.readthedocs.io/en/latest/?badge=latest +.. image:: https://readthedocs.org/projects/{{ package.name | replace("_", "-") }}/badge/?version=latest + :target: https://{{ package.name | replace("_", "-") }}.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status -.. image:: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/test.yml/badge.svg - :target: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/test.yml +.. image:: https://github.com/{{ info.github_username }}/{{ package.name }}/actions/workflows/test.yml/badge.svg + :target: https://github.com/{{ info.github_username }}/{{ package.name }}/actions/workflows/test.yml -.. image:: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/flake8.yml/badge.svg - :target: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/flake8.yml +.. image:: https://github.com/{{ info.github_username }}/{{ package.name }}/actions/workflows/flake8.yml/badge.svg + :target: https://github.com/{{ info.github_username }}/{{ package.name }}/actions/workflows/flake8.yml -.. image:: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/black.yml/badge.svg - :target: https://github.com/{{ info.github_username }}/{{ package_name }}/actions/workflows/black.yml -{%- endif %} +.. image:: https://github.com/{{ info.github_username }}/{{ package.name }}/actions/workflows/black.yml/badge.svg + :target: https://github.com/{{ info.github_username }}/{{ package.name }}/actions/workflows/black.yml .. start-intro @@ -44,7 +40,7 @@ Python and the Python Modeling Toolkit, PyMT. - PyMT {% for babelized_class, component in components|dictsort -%} * - {{ component.library }} - - :class:`~{{ package_name }}.{{ babelized_class }}` + - :class:`~{{ package.name }}.{{ babelized_class }}` - .. code-block:: pycon @@ -54,10 +50,8 @@ Python and the Python Modeling Toolkit, PyMT. .. end-intro -{% if is_open_source %} -* Free software: {{ open_source_license }} -* Documentation: https://{{ package_name | replace("_", "-") }}.readthedocs.io. -{% endif %} +* Free software: {{ info.package_license }} +* Documentation: https://{{ package.name | replace("_", "-") }}.readthedocs.io. Quickstart @@ -65,22 +59,22 @@ Quickstart .. start-quickstart -To get started you will need to install the *{{ package_name }}* package. +To get started you will need to install the *{{ package.name }}* package. Here are two ways to do so. Install from conda-forge ------------------------ -If the *{{ package_name }}* package is distributed on *conda-forge*, install it into your current environment with *conda*. +If the *{{ package.name }}* package is distributed on *conda-forge*, install it into your current environment with *conda*. .. code:: bash - conda install -c conda-forge {{ package_name }} + conda install -c conda-forge {{ package.name }} Install from source ------------------- -You can build and install the *{{ package_name }}* package from source using *conda* and *pip*. +You can build and install the *{{ package.name }}* package from source using *conda* and *pip*. First, from the source directory, install package dependencies into your current environment with *conda*. @@ -116,7 +110,7 @@ Model Interface (BMI), or as a PyMT plugin. A BMI is provided by each component in this package: {%- for babelized_class, component in components|dictsort -%} -:class:`~{{ package_name}}.{{ babelized_class }}` +:class:`~{{ package.name}}.{{ babelized_class }}` {% endfor %}. @@ -124,7 +118,7 @@ A BMI is provided by each component in this package: .. code-block:: pycon - >>> from {{ package_name}} import {{ babelized_class }} + >>> from {{ package.name}} import {{ babelized_class }} >>> model = {{ babelized_class }}() >>> model.get_component_name() # Get the name of the component >>> model.get_output_var_names() # Get a list of the component's output variables diff --git a/babelizer/data/templates/docs/Makefile b/babelizer/data/templates/docs/Makefile index d5748f0..c41b854 100644 --- a/babelizer/data/templates/docs/Makefile +++ b/babelizer/data/templates/docs/Makefile @@ -4,7 +4,7 @@ # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = python -msphinx -SPHINXPROJ = {{ package_name }} +SPHINXPROJ = {{ package.name }} SOURCEDIR = . BUILDDIR = _build diff --git a/babelizer/data/templates/docs/api/.gitignore b/babelizer/data/templates/docs/api/.gitignore index 745c9c8..1c3775f 100644 --- a/babelizer/data/templates/docs/api/.gitignore +++ b/babelizer/data/templates/docs/api/.gitignore @@ -1,2 +1,2 @@ # auto-generated with sphinx-apidoc -{{ package_name }}*.rst +{{ package.name }}*.rst diff --git a/babelizer/data/templates/docs/conf.py.jinja b/babelizer/data/templates/docs/conf.py.jinja index 9400196..e151233 100644 --- a/babelizer/data/templates/docs/conf.py.jinja +++ b/babelizer/data/templates/docs/conf.py.jinja @@ -1,4 +1,4 @@ -# {{ package_name }} documentation build configuration file, created by +# {{ package.name }} documentation build configuration file, created by # sphinx-quickstart on Fri Jun 9 13:47:02 2017. # # This file is execfile()d with the current directory set to its @@ -18,7 +18,7 @@ import os import pathlib -import {{ package_name }} +import {{ package.name }} docs_dir = os.path.dirname(__file__) @@ -58,18 +58,18 @@ source_suffix = ".rst" master_doc = "index" # General information about the project. -project = "{{ package_name }}" -copyright = "{{ now|datetimeformat('%Y') }}, {{ info.full_name }}" -author = "{{ info.full_name }}" +project = "{{ package.name }}" +copyright = "{{ now|datetimeformat('%Y') }}, {{ info.package_author }}" +author = "{{ info.package_author }}" # The version info for the project you're documenting, acts as replacement # for |version| and |release|, also used in various other places throughout # the built documents. # # The short X.Y version. -version = {{ package_name }}.__version__ +version = {{ package.name }}.__version__ # The full version, including alpha/beta/rc tags. -release = {{ package_name }}.__version__ +release = {{ package.name }}.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -96,7 +96,7 @@ todo_include_todos = False # a list of builtin themes. # html_theme = "furo" -html_title = "{{ package_name }}" +html_title = "{{ package.name }}" # Theme options are theme-specific and customize the look and feel of a @@ -105,7 +105,7 @@ html_title = "{{ package_name }}" # html_theme_options = { "announcement": None, - "source_repository": "https://github.com/{{ info.github_username }}/{{ package_name }}/", + "source_repository": "https://github.com/{{ info.github_username }}/{{ package.name }}/", "source_branch": "main", "source_directory": "docs", "sidebar_hide_name": False, @@ -132,7 +132,7 @@ html_static_path = ["_static"] # -- Options for HTMLHelp output --------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = "{{ package_name }}doc" +htmlhelp_basename = "{{ package.name }}doc" # -- Options for intersphinx extension --------------------------------------- diff --git a/babelizer/data/templates/docs/developer_install.rst b/babelizer/data/templates/docs/developer_install.rst index dfb47d1..200b284 100644 --- a/babelizer/data/templates/docs/developer_install.rst +++ b/babelizer/data/templates/docs/developer_install.rst @@ -6,30 +6,30 @@ Developer Install .. important:: - The following commands will install *{{ package_name }}* into your current environment. Although - not necessary, we **highly recommend** you install *{{ package_name }}* into its own + The following commands will install *{{ package.name }}* into your current environment. Although + not necessary, we **highly recommend** you install *{{ package.name }}* into its own :ref:`virtual environment `. -If you will be modifying code or contributing new code to *{{ package_name }}*, you will first -need to get *{{ package_name }}*'s source code and then install *{{ package_name }}* from that code. +If you will be modifying code or contributing new code to *{{ package.name }}*, you will first +need to get *{{ package.name }}*'s source code and then install *{{ package.name }}* from that code. Source Install -------------- -*{{ package_name }}* is actively being developed on GitHub, where the code is freely available. +*{{ package.name }}* is actively being developed on GitHub, where the code is freely available. If you would like to modify or contribute code, you can either clone our repository .. code-block:: bash - git clone git://github.com/pymt-lab/{{ package_name }}.git + git clone git://github.com/pymt-lab/{{ package.name }}.git -or download the `tarball `_ +or download the `tarball `_ (a zip file is available for Windows users): .. code-block:: bash - curl -OL https://github.com/{{ info.github_username }}/{{ package_name }}/tarball/master + curl -OL https://github.com/{{ info.github_username }}/{{ package.name }}/tarball/master Once you have a copy of the source code, you can install it into your current Python environment, @@ -38,7 +38,7 @@ Python environment, .. code-block:: bash - cd {{ package_name }} + cd {{ package.name }} mamba install --file=requirements.txt pip install -e . @@ -46,7 +46,7 @@ Python environment, .. code-block:: bash - cd {{ package_name }} + cd {{ package.name }} conda install --file=requirements.txt pip install -e . @@ -54,5 +54,5 @@ Python environment, .. code-block:: bash - cd {{ package_name }} + cd {{ package.name }} pip install -e . diff --git a/babelizer/data/templates/docs/environments.rst b/babelizer/data/templates/docs/environments.rst index ec8dfca..e94c3c3 100644 --- a/babelizer/data/templates/docs/environments.rst +++ b/babelizer/data/templates/docs/environments.rst @@ -18,15 +18,15 @@ should stick with *pip*. .. code-block:: bash conda install mamba -c conda-forge - mamba create -n {{ package_name }} - mamba activate {{ package_name }} + mamba create -n {{ package.name }} + mamba activate {{ package.name }} .. tab:: conda .. code-block:: bash - conda create -n {{ package_name }} - conda activate {{ package_name }} + conda create -n {{ package.name }} + conda activate {{ package.name }} .. tab:: venv diff --git a/babelizer/data/templates/docs/index.rst b/babelizer/data/templates/docs/index.rst index 2fe0fe0..6ea496b 100644 --- a/babelizer/data/templates/docs/index.rst +++ b/babelizer/data/templates/docs/index.rst @@ -1,15 +1,15 @@ .. image:: _static/logo-light.svg :align: center :scale: 15% - :alt: {{ package_name }} - :target: https://{{ package_name }}.readthedocs.org/ + :alt: {{ package.name }} + :target: https://{{ package.name }}.readthedocs.org/ :class: only-light .. image:: _static/logo-dark.svg :align: center :scale: 15% - :alt: {{ package_name }} - :target: https://{{ package_name }}.readthedocs.org/ + :alt: {{ package.name }} + :target: https://{{ package.name }}.readthedocs.org/ :class: only-dark .. include:: ../README.rst @@ -23,7 +23,7 @@ quickstart usage - API + API babel .. contributing diff --git a/babelizer/data/templates/docs/quickstart.rst b/babelizer/data/templates/docs/quickstart.rst index 753055c..aa878fc 100644 --- a/babelizer/data/templates/docs/quickstart.rst +++ b/babelizer/data/templates/docs/quickstart.rst @@ -3,16 +3,16 @@ Quickstart .. note:: - The following commands will install *{{ package_name}}* into your current + The following commands will install *{{ package.name}}* into your current environment. Although not necessary, we **highly recommend** you install - *{{ package_name}}* into its own + *{{ package.name}}* into its own :ref:`virtual environment `. .. include:: ../README.rst :start-after: .. start-quickstart :end-before: .. end-quickstart -If you would like the very latest development version of *{{ package_name}}* -or want to modify or contribute code to the *{{ package_name}}* project, +If you would like the very latest development version of *{{ package.name}}* +or want to modify or contribute code to the *{{ package.name}}* project, you will need to do a :ref:`developer installation ` of -*{{ package_name }}* from source. +*{{ package.name }}* from source. diff --git a/babelizer/data/templates/meson.build b/babelizer/data/templates/meson.build index a524628..8275e9c 100644 --- a/babelizer/data/templates/meson.build +++ b/babelizer/data/templates/meson.build @@ -1,5 +1,5 @@ project( - '{{ package_name }}', + '{{ package.name }}', {%- if language == 'c' %} 'c', {%- elif language == 'c++' %} @@ -32,13 +32,13 @@ numpy_inc = run_command( ).stdout().strip() incs = include_directories( [ - '{{ package_name }}/lib', + '{{ package.name }}/lib', # python_inc, numpy_inc, ] ) -{% set dependency_list = package_requirements.split(',') -%} +{% set dependency_list = package.requirements -%} deps = [ {%- for dependency in dependency_list if dependency != '' %} compiler.find_library('{{ dependency }}'), @@ -47,24 +47,24 @@ deps = [ # Files get copied to /site-packages/ install_pkg_srcs = [ - '{{ package_name }}/__init__.py', - '{{ package_name }}/_bmi.py', - '{{ package_name }}/_version.py', + '{{ package.name }}/__init__.py', + '{{ package.name }}/_bmi.py', + '{{ package.name }}/_version.py', ] py.install_sources( install_pkg_srcs, - subdir: '{{ package_name }}', + subdir: '{{ package.name }}', ) install_lib_srcs = [ - '{{ package_name }}/lib/__init__.py', + '{{ package.name }}/lib/__init__.py', {%- for babelized_class in components|list|sort %} - '{{ package_name }}/lib/{{ babelized_class|lower }}.pyx', + '{{ package.name }}/lib/{{ babelized_class|lower }}.pyx', {%- endfor %} ] py.install_sources( install_lib_srcs, - subdir: '{{ package_name }}/lib', + subdir: '{{ package.name }}/lib', ) @@ -73,16 +73,16 @@ py.extension_module( '{{ babelized_class|lower }}', [ {%- if language == 'fortran' %} - '{{ package_name }}/lib/bmi_interoperability.f90', + '{{ package.name }}/lib/bmi_interoperability.f90', {%- endif %} - '{{ package_name }}/lib/{{ babelized_class|lower }}.pyx', + '{{ package.name }}/lib/{{ babelized_class|lower }}.pyx', ], dependencies: [ dependency('{{ component.library }}', method : 'pkg-config'), ], include_directories: incs, install: true, - subdir: '{{ package_name }}/lib', + subdir: '{{ package.name }}/lib', {%- if language == 'c++' %} override_options : ['cython_language=cpp'], {%- endif %} @@ -90,10 +90,10 @@ py.extension_module( install_subdir( 'meta/{{ babelized_class }}', - install_dir: py.get_install_dir() / '{{ package_name }}/data', + install_dir: py.get_install_dir() / '{{ package.name }}/data', ) {%- endfor %} # This is a temporary fix for editable installs. -run_command('cp', '-r', '{{ package_name }}/data', 'build') +run_command('cp', '-r', '{{ package.name }}/data', 'build') diff --git a/babelizer/data/templates/noxfile.py.jinja b/babelizer/data/templates/noxfile.py.jinja index 931178e..ce09dd6 100644 --- a/babelizer/data/templates/noxfile.py.jinja +++ b/babelizer/data/templates/noxfile.py.jinja @@ -5,7 +5,7 @@ from itertools import chain import nox -PROJECT = "{{ package_name }}" +PROJECT = "{{ package.name }}" ROOT = pathlib.Path(__file__).parent PYTHON_VERSIONS = ["3.9", "3.10", "3.11"] @@ -22,7 +22,7 @@ def test(session: nox.Session) -> None: {%- for babelized_class, _ in components|dictsort %} session.run( "bmi-test", - "{{ package_name }}.bmi:{{ babelized_class }}", + "{{ package.name }}.bmi:{{ babelized_class }}", "-vvv", ) {%- endfor %} @@ -80,7 +80,7 @@ def build_docs(session: nox.Session) -> None: "--module-first", "-o", "docs/api", - "{{ package_name }}", + "{{ package.name }}", ) session.run( "sphinx-build", @@ -106,7 +106,7 @@ def live_docs(session: nox.Session) -> None: "docs/_templates", "-o", "docs/api", - "{{ package_name }}", + "{{ package.name }}", ) session.run( "sphinx-autobuild", @@ -188,5 +188,5 @@ def clean_docs(session: nox.Session) -> None: shutil.rmtree("html") with session.chdir(ROOT / "docs"): - for p in pathlib.Path("api").rglob("{{ package_name }}*.rst"): + for p in pathlib.Path("api").rglob("{{ package.name }}*.rst"): p.unlink() diff --git a/babelizer/data/templates/pyproject.toml b/babelizer/data/templates/pyproject.toml index 6543775..7200de4 100644 --- a/babelizer/data/templates/pyproject.toml +++ b/babelizer/data/templates/pyproject.toml @@ -10,19 +10,19 @@ requires = ["cython", "numpy", "meson-python", "wheel"] {% endif %} [project] -name = "{{package_name}}" +name = "{{package.name}}" authors = [ - {name = "{{info.full_name}}", email = "{{info.email}}"}, + {name = "{{info.package_author}}", email = "{{info.package_author_email}}"}, ] maintainers = [ - {name = "{{info.full_name}}", email = "{{info.email}}"}, + {name = "{{info.package_author}}", email = "{{info.package_author_email}}"}, ] -description = "PyMT plugin for {{package_name}}" +description = "PyMT plugin for {{package.name}}" license = {text = "MIT License"} classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Science/Research", - "License :: OSI Approved :: {{ open_source_license }}", + "License :: OSI Approved :: {{ info.package_license }}", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3 :: Only", @@ -38,11 +38,11 @@ dependencies = [ ] [project.urls] -homepage = "https://github.com/{{ info.github_username }}/{{ package_name }}" +homepage = "https://github.com/{{ info.github_username }}/{{ package.name }}" [project.entry-points."pymt.plugins"] {%- for babelized_class, _ in components|dictsort %} -{{ babelized_class }} = "{{ package_name }}.bmi:{{ babelized_class }}" +{{ babelized_class }} = "{{ package.name }}.bmi:{{ babelized_class }}" {%- endfor %} [project.optional-dependencies] @@ -68,12 +68,12 @@ testing = [ {%- if language == 'python' %} [tool.setuptools.packages.find] where = ["."] -include = ["{{package_name}}*"] +include = ["{{package.name}}*"] {% endif %} [tool.pytest.ini_options] minversion = "5.0" -testpaths = ["{{ package_name }}", "tests"] +testpaths = ["{{ package.name }}", "tests"] norecursedirs = [".*", "*.egg*", "build", "dist"] addopts = """ --ignore setup.py @@ -98,20 +98,20 @@ line_length = 88 [tool.check-manifest] ignore = [ - "{{ package_name }}/data", - "{{ package_name }}/data/**/*", + "{{ package.name }}/data", + "{{ package.name }}/data/**/*", ] [tool.towncrier] directory = "news" -package = "{{ package_name }}" +package = "{{ package.name }}" filename = "CHANGES.rst" single_file = true underlines = "-^\"" -issue_format = "`#{issue} `_" +issue_format = "`#{issue} `_" title_format = "{version} ({project_date})" wrap = true [tool.zest-releaser] tag-format = "v{version}" -python-file-with-version = "{{ package_name }}/_version.py" +python-file-with-version = "{{ package.name }}/_version.py" diff --git a/babelizer/data/templates/requirements-library.txt b/babelizer/data/templates/requirements-library.txt index 7fcdbe5..dd03ffa 100644 --- a/babelizer/data/templates/requirements-library.txt +++ b/babelizer/data/templates/requirements-library.txt @@ -1,3 +1,3 @@ -{%- for requirement in package_requirements.split(',') -%} +{%- for requirement in package.requirements -%} {{ requirement|trim }} {%- endfor %} diff --git a/babelizer/data/templates/{{package_name}}/__init__.py.jinja b/babelizer/data/templates/{{package.name}}/__init__.py.jinja similarity index 100% rename from babelizer/data/templates/{{package_name}}/__init__.py.jinja rename to babelizer/data/templates/{{package.name}}/__init__.py.jinja diff --git a/babelizer/data/templates/{{package_name}}/_bmi.py.jinja b/babelizer/data/templates/{{package.name}}/_bmi.py.jinja similarity index 100% rename from babelizer/data/templates/{{package_name}}/_bmi.py.jinja rename to babelizer/data/templates/{{package.name}}/_bmi.py.jinja diff --git a/babelizer/data/templates/{{package_name}}/_version.py.jinja b/babelizer/data/templates/{{package.name}}/_version.py.jinja similarity index 100% rename from babelizer/data/templates/{{package_name}}/_version.py.jinja rename to babelizer/data/templates/{{package.name}}/_version.py.jinja diff --git a/babelizer/data/templates/{{package_name}}/lib/__init__.py.jinja b/babelizer/data/templates/{{package.name}}/lib/__init__.py.jinja similarity index 100% rename from babelizer/data/templates/{{package_name}}/lib/__init__.py.jinja rename to babelizer/data/templates/{{package.name}}/lib/__init__.py.jinja diff --git a/babelizer/data/templates/{{package_name}}/lib/_c.pyx b/babelizer/data/templates/{{package.name}}/lib/_c.pyx similarity index 100% rename from babelizer/data/templates/{{package_name}}/lib/_c.pyx rename to babelizer/data/templates/{{package.name}}/lib/_c.pyx diff --git a/babelizer/data/templates/{{package_name}}/lib/_cxx.pyx b/babelizer/data/templates/{{package.name}}/lib/_cxx.pyx similarity index 100% rename from babelizer/data/templates/{{package_name}}/lib/_cxx.pyx rename to babelizer/data/templates/{{package.name}}/lib/_cxx.pyx diff --git a/babelizer/data/templates/{{package_name}}/lib/_fortran.pyx b/babelizer/data/templates/{{package.name}}/lib/_fortran.pyx similarity index 100% rename from babelizer/data/templates/{{package_name}}/lib/_fortran.pyx rename to babelizer/data/templates/{{package.name}}/lib/_fortran.pyx diff --git a/babelizer/data/templates/{{package_name}}/lib/bmi_interoperability.f90 b/babelizer/data/templates/{{package.name}}/lib/bmi_interoperability.f90 similarity index 100% rename from babelizer/data/templates/{{package_name}}/lib/bmi_interoperability.f90 rename to babelizer/data/templates/{{package.name}}/lib/bmi_interoperability.f90 diff --git a/babelizer/data/templates/{{package_name}}/lib/bmi_interoperability.h b/babelizer/data/templates/{{package.name}}/lib/bmi_interoperability.h similarity index 100% rename from babelizer/data/templates/{{package_name}}/lib/bmi_interoperability.h rename to babelizer/data/templates/{{package.name}}/lib/bmi_interoperability.h diff --git a/babelizer/metadata.py b/babelizer/metadata.py index 9d47561..7a8a8d8 100644 --- a/babelizer/metadata.py +++ b/babelizer/metadata.py @@ -353,10 +353,7 @@ def norm(config: dict[str, Any]) -> dict[str, Any]: "python_version": sorted(config["ci"]["python_version"]), "os": sorted(config["ci"]["os"]), }, - "package_name": config["package"]["name"], - "package_requirements": ",".join(config["package"]["requirements"]), "language": language, - "open_source_license": config["info"]["package_license"], } def dump(self, fp: io.TextIOBase, fmt: str = "toml") -> None: diff --git a/babelizer/render.py b/babelizer/render.py index 824031a..34f2a5d 100644 --- a/babelizer/render.py +++ b/babelizer/render.py @@ -4,27 +4,18 @@ import datetime import os -import sys -from typing import Any import git -from babelizer.metadata import BabelMetadata - -if sys.version_info >= (3, 11): # pragma: no cover (PY11+) - import tomllib -else: # pragma: no cover ( Date: Mon, 1 Apr 2024 13:25:33 -0600 Subject: [PATCH 21/38] better error message for existing output dir --- babelizer/errors.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/babelizer/errors.py b/babelizer/errors.py index edf1abd..d478790 100644 --- a/babelizer/errors.py +++ b/babelizer/errors.py @@ -35,7 +35,9 @@ class ScanError(BabelizeError): class OutputDirExistsError(BabelizeError): """An exception used when the directory for babelized output exists.""" - pass + def __str__(self) -> str: + """Render a user-readable error message.""" + return f"output directory exists: {self._message}" class SetupPyError(BabelizeError): From 3b1045035aee99e56c9e958a8b274124f36facb5 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Tue, 2 Apr 2024 09:32:22 -0600 Subject: [PATCH 22/38] make utils module private --- babelizer/_cookiecutter.py | 2 +- babelizer/{utils.py => _utils.py} | 0 babelizer/cli.py | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename babelizer/{utils.py => _utils.py} (100%) diff --git a/babelizer/_cookiecutter.py b/babelizer/_cookiecutter.py index 6028331..0b7e7c0 100644 --- a/babelizer/_cookiecutter.py +++ b/babelizer/_cookiecutter.py @@ -10,7 +10,7 @@ from jinja2 import Template from babelizer._post_hook import run -from babelizer.utils import as_cwd +from babelizer._utils import as_cwd def cookiecutter( diff --git a/babelizer/utils.py b/babelizer/_utils.py similarity index 100% rename from babelizer/utils.py rename to babelizer/_utils.py diff --git a/babelizer/cli.py b/babelizer/cli.py index 4a7781e..1dcaec8 100644 --- a/babelizer/cli.py +++ b/babelizer/cli.py @@ -19,14 +19,14 @@ from babelizer._files.license_rst import render as render_license from babelizer._files.meson_build import render as render_meson_build from babelizer._files.readme import render as render_readme +from babelizer._utils import get_setup_py_version +from babelizer._utils import save_files from babelizer.errors import OutputDirExistsError from babelizer.errors import ScanError from babelizer.errors import SetupPyError from babelizer.errors import ValidationError from babelizer.metadata import BabelMetadata from babelizer.render import render -from babelizer.utils import get_setup_py_version -from babelizer.utils import save_files out = partial(click.secho, bold=True, err=True) err = partial(click.secho, fg="red", err=True) From 1eb959a46d4c86c3821342ed5dfb0fcf5513c710 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Tue, 2 Apr 2024 12:05:47 -0600 Subject: [PATCH 23/38] clean up cookiecutter; add babelizer_environment function --- babelizer/_cookiecutter.py | 72 +++++++++++++++++++++++++++----------- babelizer/_files/readme.py | 11 ++---- babelizer/render.py | 8 +---- 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/babelizer/_cookiecutter.py b/babelizer/_cookiecutter.py index 0b7e7c0..8d068f3 100644 --- a/babelizer/_cookiecutter.py +++ b/babelizer/_cookiecutter.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +from collections.abc import Iterable from datetime import datetime from typing import Any @@ -9,20 +10,19 @@ 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, - extra_context: dict[str, Any] | None = None, + context: dict[str, Any] | None = None, output_dir: str = ".", - no_input: bool = True, - overwrite_if_exists: bool = False, ) -> None: - if extra_context is None: - extra_context = {} - env = Environment(loader=FileSystemLoader(template), undefined=StrictUndefined) + if context is None: + context = {} + env = babelizer_environment(template) def datetime_format(value: datetime, format_: str = "%Y-%M-%D") -> str: return value.strftime(format_) @@ -31,30 +31,62 @@ def datetime_format(value: datetime, format_: str = "%Y-%M-%D") -> str: 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, extra_context)) + 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, extra_context)) + 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( - **extra_context - ) + env.get_template(os.path.join(rel_path, filename)).render(**context) ) with as_cwd(output_dir): - run(extra_context) - - -def render_path(path: str, context: dict[str, Any]) -> str: - rendered_path = Template(path).render(**context) + 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) - if ext == ".jinja": - rendered_path = root - - return rendered_path + return rendered_path if ext not in remove_extension else root diff --git a/babelizer/_files/readme.py b/babelizer/_files/readme.py index c6979ae..bd2eec5 100644 --- a/babelizer/_files/readme.py +++ b/babelizer/_files/readme.py @@ -2,17 +2,10 @@ from typing import Any -from jinja2 import Environment -from jinja2 import FileSystemLoader -from jinja2 import StrictUndefined - -from babelizer._datadir import get_template_dir +from babelizer._cookiecutter import babelizer_environment def render(context: dict[str, Any]) -> str: - env = Environment( - loader=FileSystemLoader(get_template_dir()), undefined=StrictUndefined - ) - template = env.get_template("templates/README.rst") + template = babelizer_environment().get_template("README.rst") return template.render(**context) diff --git a/babelizer/render.py b/babelizer/render.py index 34f2a5d..d7cbe50 100644 --- a/babelizer/render.py +++ b/babelizer/render.py @@ -71,13 +71,7 @@ def render( if os.path.exists(output): raise OutputDirExistsError(output) - cookiecutter( - template, - extra_context=context, - output_dir=output, - no_input=True, - overwrite_if_exists=clobber, - ) + cookiecutter(template, context=context, output_dir=output) path = os.path.realpath(output) From 2b63d939756a5af4c25909dfb163cad4261b81f2 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Tue, 2 Apr 2024 12:18:05 -0600 Subject: [PATCH 24/38] add news fragment --- news/96.misc | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 news/96.misc diff --git a/news/96.misc b/news/96.misc new file mode 100644 index 0000000..35872ee --- /dev/null +++ b/news/96.misc @@ -0,0 +1,3 @@ + +Removed dependency on *cookiecutter* by adding our own implementation +of a simplified version. From 768dd096fc9d5e321fb2306b8aeadaaedda10123 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 3 Apr 2024 13:33:40 -0600 Subject: [PATCH 25/38] remove the manifest from the templates --- babelizer/data/templates/MANIFEST.in | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 babelizer/data/templates/MANIFEST.in diff --git a/babelizer/data/templates/MANIFEST.in b/babelizer/data/templates/MANIFEST.in deleted file mode 100644 index 70075f7..0000000 --- a/babelizer/data/templates/MANIFEST.in +++ /dev/null @@ -1,15 +0,0 @@ -recursive-include {{ package.name }}/data * -include LICENSE -include requirements.txt -include *.rst -include *.txt -include Makefile -include babel.toml -recursive-include {{ package.name }} *.pyx -recursive-include docs *.py -recursive-include docs *.rst -recursive-include docs Makefile -recursive-exclude meta * -recursive-exclude recipe * -recursive-exclude {{ package.name }} *.cpp -recursive-exclude {{ package.name }} *.c From 70347d9919b19fef7564148ba214967e0f3cbd6e Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 3 Apr 2024 13:34:08 -0600 Subject: [PATCH 26/38] add setuptools package-data and version for python projects --- babelizer/data/templates/pyproject.toml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/babelizer/data/templates/pyproject.toml b/babelizer/data/templates/pyproject.toml index 7200de4..8dfedec 100644 --- a/babelizer/data/templates/pyproject.toml +++ b/babelizer/data/templates/pyproject.toml @@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta" requires = [ "setuptools >=61", ] -{% else %} +{%- else %} build-backend = "mesonpy" requires = ["cython", "numpy", "meson-python", "wheel"] -{% endif %} +{%- endif %} [project] name = "{{package.name}}" @@ -66,10 +66,19 @@ testing = [ ] {%- if language == 'python' %} + +[tool.setuptools.dynamic.version] +attr = "{{package.name}}._version.__version__" + +[tool.setuptools.package-data] +{{package.name}} = [ + "data/**/*", +] + [tool.setuptools.packages.find] where = ["."] include = ["{{package.name}}*"] -{% endif %} +{%- endif %} [tool.pytest.ini_options] minversion = "5.0" From ffa804c09885ef31491b142c51c17e8e2d929ea4 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 3 Apr 2024 13:41:51 -0600 Subject: [PATCH 27/38] remove the babelizer's manifest --- MANIFEST.in | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index b9ced9b..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,19 +0,0 @@ -include *.rst -include requirements*.txt -include Makefile -exclude .gitmodules -exclude .readthedocs.yaml -recursive-include babelizer/data * -recursive-exclude docs * -recursive-exclude paper * -recursive-include tests * -recursive-include external/bmi-example-c * -recursive-include external/bmi-example-cxx * -recursive-include external/bmi-example-fortran * -recursive-include external/bmi-example-python * -recursive-include external/tests * -include external/requirements.txt -exclude external/bmi-example-c/.git* -exclude external/bmi-example-cxx/.git* -exclude external/bmi-example-fortran/.git* -exclude external/bmi-example-python/.git* From cfc62d714d77819ee6963e6003b5a51c23a91e12 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 3 Apr 2024 13:43:51 -0600 Subject: [PATCH 28/38] recusively include babelizer package data --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5601634..cf527ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,7 +107,7 @@ attr = "babelizer._version.__version__" [tool.setuptools.package-data] babelizer = [ - "data/*", + "data/**/*", ] [tool.setuptools.packages.find] From 36a12189ae62b1a4cf87a787ff632d504530cf70 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 3 Apr 2024 13:44:33 -0600 Subject: [PATCH 29/38] remove the docs Makefile --- babelizer/data/templates/docs/Makefile | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 babelizer/data/templates/docs/Makefile diff --git a/babelizer/data/templates/docs/Makefile b/babelizer/data/templates/docs/Makefile deleted file mode 100644 index c41b854..0000000 --- a/babelizer/data/templates/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = python -msphinx -SPHINXPROJ = {{ package.name }} -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) From f7f8af2998e6b75ed95f0321fe432260578b709c Mon Sep 17 00:00:00 2001 From: mcflugen Date: Thu, 4 Apr 2024 10:03:31 -0600 Subject: [PATCH 30/38] more clean up; move parse_entry_point into _utils.py --- babelizer/_files/bmi_py.py | 28 +++++++++---------- babelizer/_files/gitignore.py | 8 +++--- babelizer/_files/init_py.py | 10 +++---- babelizer/_files/lib_init_py.py | 8 +++--- babelizer/_files/license_rst.py | 8 +++--- babelizer/_utils.py | 42 +++++++++++++++++++++++++++++ babelizer/metadata.py | 48 +++------------------------------ 7 files changed, 75 insertions(+), 77 deletions(-) diff --git a/babelizer/_files/bmi_py.py b/babelizer/_files/bmi_py.py index 1d00e5b..e032fcd 100644 --- a/babelizer/_files/bmi_py.py +++ b/babelizer/_files/bmi_py.py @@ -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))} @@ -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" @@ -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 = [ @@ -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} diff --git a/babelizer/_files/gitignore.py b/babelizer/_files/gitignore.py index 1296106..51cbbe8 100644 --- a/babelizer/_files/gitignore.py +++ b/babelizer/_files/gitignore.py @@ -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]", @@ -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: diff --git a/babelizer/_files/init_py.py b/babelizer/_files/init_py.py index 5999df8..3e69271 100644 --- a/babelizer/_files/init_py.py +++ b/babelizer/_files/init_py.py @@ -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))} diff --git a/babelizer/_files/lib_init_py.py b/babelizer/_files/lib_init_py.py index 269b655..e726f14 100644 --- a/babelizer/_files/lib_init_py.py +++ b/babelizer/_files/lib_init_py.py @@ -5,15 +5,15 @@ from typing import Any -def render(plugin_metadata: Mapping[str, Any]) -> str: +def render(context: Mapping[str, Any]) -> str: """Render lib/__init__.py.""" - package_name = plugin_metadata["package"]["name"] + package_name = context["package"]["name"] imports = [ f"from {package_name}.lib.{cls.lower()} import {cls}" - 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"""\ {os.linesep.join(sorted(imports))} diff --git a/babelizer/_files/license_rst.py b/babelizer/_files/license_rst.py index 1d5bc49..beb65bb 100644 --- a/babelizer/_files/license_rst.py +++ b/babelizer/_files/license_rst.py @@ -5,13 +5,13 @@ from typing import Any -def render(plugin_metadata: Mapping[str, Any]) -> str: +def render(context: Mapping[str, Any]) -> str: """Render LICENSE.rst.""" - license_name = plugin_metadata["info"]["package_license"] + license_name = context["info"]["package_license"] kwds = { - "full_name": plugin_metadata["info"]["package_author"], + "full_name": context["info"]["package_author"], "year": datetime.now().year, - "project_short_description": plugin_metadata["info"]["summary"], + "project_short_description": context["info"]["summary"], } return LICENSE[license_name].format(**kwds) diff --git a/babelizer/_utils.py b/babelizer/_utils.py index 2bfbfff..1b1ef6d 100644 --- a/babelizer/_utils.py +++ b/babelizer/_utils.py @@ -13,6 +13,7 @@ from contextlib import suppress from babelizer.errors import SetupPyError +from babelizer.errors import ValidationError def execute(args: Sequence[str]) -> subprocess.CompletedProcess[bytes]: @@ -106,3 +107,44 @@ def as_cwd(path: str) -> Generator[None, None, None]: os.chdir(path) yield os.chdir(prev_cwd) + + +def parse_entry_point(specifier: str) -> tuple[str, str, str]: + """Parse an entry point specifier into its parts. + + Parameters + ---------- + specifier : str + An entry-point specifier. + + Returns + ------- + tuple of str + The parts of the entry point as (*name*, *module*, *class*). + + Raises + ------ + ValidationError + If the entry point cannot be parsed. + + Examples + -------- + >>> from babelizer.metadata import BabelMetadata + >>> BabelMetadata.parse_entry_point("Foo=bar:Baz") + ('Foo', 'bar', 'Baz') + + >>> BabelMetadata.parse_entry_point("bar:Baz") + Traceback (most recent call last): + ... + babelizer.errors.ValidationError: bad entry point specifier (bar:Baz). specifier must be of the form name=module:class + """ + try: + name, value = (item.strip() for item in specifier.split("=")) + module, obj = (item.strip() for item in value.split(":")) + except ValueError: + raise ValidationError( + f"bad entry point specifier ({specifier}). specifier must be of" + " the form name=module:class" + ) from None + + return name, module, obj diff --git a/babelizer/metadata.py b/babelizer/metadata.py index 7a8a8d8..a7ae50e 100644 --- a/babelizer/metadata.py +++ b/babelizer/metadata.py @@ -22,6 +22,7 @@ else: # pragma: no cover ( None: validate_dict(libraries, required=("language", "entry_point"), optional={}) for entry_point in libraries["entry_point"]: try: - BabelMetadata.parse_entry_point(entry_point) + parse_entry_point(entry_point) except ValidationError: raise ValidationError(f"poorly-formed entry point ({entry_point})") else: @@ -277,9 +278,7 @@ def _header_ext(language: str) -> str: libraries = {} for entry_point in entry_points: - babelized_class, library_name, class_name = BabelMetadata.parse_entry_point( - entry_point - ) + babelized_class, library_name, class_name = parse_entry_point(entry_point) libraries[babelized_class] = { "language": language, "library": library_name, @@ -392,44 +391,3 @@ def format_toml(self) -> str: Serialized metadata as a TOML-formatted string """ return tomli_w.dumps(self._meta, multiline_strings=True) - - @staticmethod - def parse_entry_point(specifier: str) -> tuple[str, str, str]: - """Parse an entry point specifier into its parts. - - Parameters - ---------- - specifier : str - An entry-point specifier. - - Returns - ------- - tuple of str - The parts of the entry point as (*name*, *module*, *class*). - - Raises - ------ - ValidationError - If the entry point cannot be parsed. - - Examples - -------- - >>> from babelizer.metadata import BabelMetadata - >>> BabelMetadata.parse_entry_point("Foo=bar:Baz") - ('Foo', 'bar', 'Baz') - - >>> BabelMetadata.parse_entry_point("bar:Baz") - Traceback (most recent call last): - ... - babelizer.errors.ValidationError: bad entry point specifier (bar:Baz). specifier must be of the form name=module:class - """ - try: - name, value = (item.strip() for item in specifier.split("=")) - module, obj = (item.strip() for item in value.split(":")) - except ValueError: - raise ValidationError( - f"bad entry point specifier ({specifier}). specifier must be of" - " the form name=module:class" - ) from None - - return name, module, obj From c4a8100e4004ecfcef4be13eca0dc5b5131e79c8 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Thu, 4 Apr 2024 10:07:48 -0600 Subject: [PATCH 31/38] update requirements files --- requirements-docs.txt | 8 ++++---- requirements-testing.txt | 5 +++-- requirements.txt | 9 +++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/requirements-docs.txt b/requirements-docs.txt index 2f7798f..ad7389b 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,10 +1,10 @@ # Requirements extracted from pyproject.toml # [project.optional-dependencies.docs] -sphinx>=4 +furo +pygments>=2.4 sphinx-click sphinx-copybutton sphinx-inline-tabs -sphinxcontrib.towncrier -pygments>=2.4 sphinx-inline-tabs -furo +sphinx>=4 +sphinxcontrib.towncrier diff --git a/requirements-testing.txt b/requirements-testing.txt index 0956cba..e11fe5f 100644 --- a/requirements-testing.txt +++ b/requirements-testing.txt @@ -1,8 +1,9 @@ # Requirements extracted from pyproject.toml # [project.optional-dependencies.testing] +bmi-tester>=0.5.9 +coverage[toml] +coveralls pytest pytest-cov pytest-datadir pytest-xdist -coverage[toml] -coveralls diff --git a/requirements.txt b/requirements.txt index 8ea48b4..29a5d30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,10 @@ # Requirements extracted from pyproject.toml # [project.dependencies] -black click gitpython +importlib-resources; python_version < '3.12' +jinja2 +logoizer@ git+https://github.com/mcflugen/logoizer pyyaml -tomlkit -isort>=5 -cookiecutter +tomli-w +tomli; python_version < '3.11' From 2824e9eef01f42ae7413b1f8e91e00b0130cd310 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Thu, 4 Apr 2024 10:21:42 -0600 Subject: [PATCH 32/38] clean up .gitignore; add a couple linters --- .gitignore | 106 ++--------------------------- .pre-commit-config.yaml | 9 +++ external/requirements.txt | 6 +- tests/{test_cli.py => cli_test.py} | 0 4 files changed, 17 insertions(+), 104 deletions(-) rename tests/{test_cli.py => cli_test.py} (100%) diff --git a/.gitignore b/.gitignore index 54107c0..43fd6fa 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 27b394d..42dff1c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -65,6 +65,15 @@ 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 diff --git a/external/requirements.txt b/external/requirements.txt index 496776c..049a9fb 100644 --- a/external/requirements.txt +++ b/external/requirements.txt @@ -1,12 +1,12 @@ -bmi-tester>=0.5.4 bmi-c bmi-cxx bmi-fortran +bmi-tester>=0.5.4 bmipy -cmake c-compiler +cmake cxx-compiler fortran-compiler make -pkg-config pip +pkg-config diff --git a/tests/test_cli.py b/tests/cli_test.py similarity index 100% rename from tests/test_cli.py rename to tests/cli_test.py From b669578b9eb6d1f05f123d62ce2575952e82005b Mon Sep 17 00:00:00 2001 From: mcflugen Date: Thu, 4 Apr 2024 10:36:32 -0600 Subject: [PATCH 33/38] fix parse_entry_point doctest --- babelizer/_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/babelizer/_utils.py b/babelizer/_utils.py index 1b1ef6d..4918553 100644 --- a/babelizer/_utils.py +++ b/babelizer/_utils.py @@ -129,11 +129,11 @@ def parse_entry_point(specifier: str) -> tuple[str, str, str]: Examples -------- - >>> from babelizer.metadata import BabelMetadata - >>> BabelMetadata.parse_entry_point("Foo=bar:Baz") + >>> from babelizer._utils import parse_entry_point + >>> parse_entry_point("Foo=bar:Baz") ('Foo', 'bar', 'Baz') - >>> BabelMetadata.parse_entry_point("bar:Baz") + >>> parse_entry_point("bar:Baz") Traceback (most recent call last): ... babelizer.errors.ValidationError: bad entry point specifier (bar:Baz). specifier must be of the form name=module:class From 4f11dd1109e15575afb08b453a41a4fbabfcc0d6 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Thu, 4 Apr 2024 13:25:33 -0600 Subject: [PATCH 34/38] update the template pre-commit config file --- babelizer/data/templates/.pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/babelizer/data/templates/.pre-commit-config.yaml b/babelizer/data/templates/.pre-commit-config.yaml index 519c7de..372fa38 100644 --- a/babelizer/data/templates/.pre-commit-config.yaml +++ b/babelizer/data/templates/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 24.2.0 + rev: 24.3.0 hooks: - id: black name: black @@ -35,7 +35,7 @@ repos: - flake8-simplify - repo: https://github.com/asottile/pyupgrade - rev: v3.15.1 + rev: v3.15.2 hooks: - id: pyupgrade args: [--py310-plus] @@ -61,7 +61,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.8.0 + rev: v1.9.0 hooks: - id: mypy additional_dependencies: [types-all] From c10fa1bcbdc1b681cb751b20918b32ebeed291e8 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Thu, 4 Apr 2024 15:19:25 -0600 Subject: [PATCH 35/38] remove unused _norm_os function --- babelizer/metadata.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/babelizer/metadata.py b/babelizer/metadata.py index a7ae50e..3141ec5 100644 --- a/babelizer/metadata.py +++ b/babelizer/metadata.py @@ -68,16 +68,6 @@ def validate_dict( ) -def _norm_os(name: str) -> str: - if name == "linux": - name = "ubuntu" - elif name == "mac": - name = "macos" - if not name.endswith("-latest"): - name += "-latest" - return name - - class BabelMetadata(Mapping[str, Any]): """Library metadata.""" From efbcc9ce6ad675a02d7fa7b6f75b4855fbd600d0 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Thu, 4 Apr 2024 15:27:17 -0600 Subject: [PATCH 36/38] update pre-commit config file --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 42dff1c..85d775c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 24.2.0 + rev: 24.3.0 hooks: - id: black name: black @@ -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] @@ -76,7 +76,7 @@ repos: ) - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.8.0 + rev: v1.9.0 hooks: - id: mypy additional_dependencies: [types-all] From be338838d4b2114cfb178f11f15e507f5b02724b Mon Sep 17 00:00:00 2001 From: mcflugen Date: Thu, 4 Apr 2024 15:27:56 -0600 Subject: [PATCH 37/38] move validate_dict into _utils as validate_dict_keys --- babelizer/_utils.py | 42 +++++++++++++++++++++++++++++ babelizer/metadata.py | 61 +++++++++---------------------------------- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/babelizer/_utils.py b/babelizer/_utils.py index 4918553..8659c21 100644 --- a/babelizer/_utils.py +++ b/babelizer/_utils.py @@ -11,6 +11,7 @@ from collections.abc import Sequence from contextlib import contextmanager from contextlib import suppress +from typing import Any from babelizer.errors import SetupPyError from babelizer.errors import ValidationError @@ -148,3 +149,44 @@ def parse_entry_point(specifier: str) -> tuple[str, str, str]: ) from None return name, module, obj + + +def validate_dict_keys( + meta: dict[str, Any], + required: Iterable[str] | None = None, + optional: Iterable[str] | None = None, +) -> None: + """Validate the keys of a dict. + + Parameters + ---------- + meta : dict + Configuration metadata + required : dict, optional + Required keys in configuration. + optional : dict, optional + Optional keys in configuration. + + Raises + ------ + ValidationError + Raised for invalid metadata. + """ + actual = set(meta) + required = set() if required is None else set(required) + optional = required if optional is None else set(optional) + valid = required | optional + + if missing := required - actual: + raise ValidationError( + "missing required key{}: {}".format( + "s" if len(missing) > 1 else "", ", ".join(missing) + ) + ) + + if unknown := actual - valid: + raise ValidationError( + "unknown key{}: {}".format( + "s" if len(unknown) > 1 else "", ", ".join(unknown) + ) + ) diff --git a/babelizer/metadata.py b/babelizer/metadata.py index 3141ec5..29988cd 100644 --- a/babelizer/metadata.py +++ b/babelizer/metadata.py @@ -9,7 +9,6 @@ from collections import defaultdict from collections.abc import Callable from collections.abc import Generator -from collections.abc import Iterable from collections.abc import Mapping from contextlib import suppress from typing import Any @@ -23,51 +22,11 @@ import tomli as tomllib from babelizer._utils import parse_entry_point +from babelizer._utils import validate_dict_keys from babelizer.errors import ScanError from babelizer.errors import ValidationError -def validate_dict( - meta: dict[str, Any], - required: Iterable[str] | None = None, - optional: Iterable[str] | None = None, -) -> None: - """Validate babelizer configuration metadata. - - Parameters - ---------- - meta : dict - Configuration metadata - required : dict, optional - Required keys in configuration. - optional : dict, optional - Optional keys in configuration. - - Raises - ------ - ValidationError - Raised for invalid metadata. - """ - actual = set(meta) - required = set() if required is None else set(required) - optional = required if optional is None else set(optional) - valid = required | optional - - if missing := required - actual: - raise ValidationError( - "missing required key{}: {}".format( - "s" if len(missing) > 1 else "", ", ".join(missing) - ) - ) - - if unknown := actual - valid: - raise ValidationError( - "unknown key{}: {}".format( - "s" if len(unknown) > 1 else "", ", ".join(unknown) - ) - ) - - class BabelMetadata(Mapping[str, Any]): """Library metadata.""" @@ -197,7 +156,9 @@ def validate(config: dict[str, Any]) -> None: """ libraries = config["library"] if "entry_point" in libraries: - validate_dict(libraries, required=("language", "entry_point"), optional={}) + validate_dict_keys( + libraries, required=("language", "entry_point"), optional={} + ) for entry_point in libraries["entry_point"]: try: parse_entry_point(entry_point) @@ -205,13 +166,13 @@ def validate(config: dict[str, Any]) -> None: raise ValidationError(f"poorly-formed entry point ({entry_point})") else: for _babelized_class, library in libraries.items(): - validate_dict( + validate_dict_keys( library, required={"language", "library", "header", "entry_point"}, optional={}, ) - validate_dict( + validate_dict_keys( config["build"], required=None, optional=( @@ -223,11 +184,13 @@ def validate(config: dict[str, Any]) -> None: "extra_compile_args", ), ) - validate_dict(config["package"], required=("name", "requirements"), optional={}) - validate_dict(config["ci"], required=("python_version", "os"), optional={}) + validate_dict_keys( + config["package"], required=("name", "requirements"), optional={} + ) + validate_dict_keys(config["ci"], required=("python_version", "os"), optional={}) try: - validate_dict( + validate_dict_keys( config["info"], required=( "package_author", @@ -239,7 +202,7 @@ def validate(config: dict[str, Any]) -> None: optional={}, ) except ValidationError: - validate_dict( + validate_dict_keys( config["info"], required=( "plugin_author", From cfad50e748447b1c5a1ad1aaa5bfc5647b3e9ec7 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Thu, 4 Apr 2024 21:05:24 -0600 Subject: [PATCH 38/38] changed BabelMetadata to BabelConfig --- babelizer/_utils.py | 4 +- babelizer/cli.py | 26 +++++------ babelizer/{metadata.py => config.py} | 64 ++++++++++++++-------------- babelizer/render.py | 22 +++++----- tests/cli_test.py | 4 +- 5 files changed, 60 insertions(+), 60 deletions(-) rename babelizer/{metadata.py => config.py} (84%) diff --git a/babelizer/_utils.py b/babelizer/_utils.py index 8659c21..7b18b67 100644 --- a/babelizer/_utils.py +++ b/babelizer/_utils.py @@ -161,7 +161,7 @@ def validate_dict_keys( Parameters ---------- meta : dict - Configuration metadata + Dictionary to validate. required : dict, optional Required keys in configuration. optional : dict, optional @@ -170,7 +170,7 @@ def validate_dict_keys( Raises ------ ValidationError - Raised for invalid metadata. + Raised for invalid dict. """ actual = set(meta) required = set() if required is None else set(required) diff --git a/babelizer/cli.py b/babelizer/cli.py index 1dcaec8..029f191 100644 --- a/babelizer/cli.py +++ b/babelizer/cli.py @@ -21,11 +21,11 @@ from babelizer._files.readme import render as render_readme from babelizer._utils import get_setup_py_version from babelizer._utils import save_files +from babelizer.config import BabelConfig from babelizer.errors import OutputDirExistsError from babelizer.errors import ScanError from babelizer.errors import SetupPyError from babelizer.errors import ValidationError -from babelizer.metadata import BabelMetadata from babelizer.render import render out = partial(click.secho, bold=True, err=True) @@ -90,15 +90,15 @@ def init( fmt = pathlib.Path(meta.name).suffix[1:] or "toml" try: - babel_metadata = BabelMetadata.from_stream(cast(io.TextIOBase, meta), fmt=fmt) + babel_config = BabelConfig.from_stream(cast(io.TextIOBase, meta), fmt=fmt) except (ScanError, ValidationError) as error: raise BabelizerAbort(str(error)) - output = babel_metadata["package"]["name"] + output = babel_config["package"]["name"] try: new_folder = render( - babel_metadata, + babel_config, output, template=template, clobber=False, @@ -149,14 +149,14 @@ def update( package_path = os.path.realpath(".") for fname in ("babel.toml", "babel.yaml", "plugin.yaml"): # if (package_path / fname).is_file(): - # metadata_path = package_path / fname + # config_path = package_path / fname if os.path.isfile(os.path.join(package_path, fname)): - metadata_path = os.path.join(package_path, fname) + config_path = os.path.join(package_path, fname) break else: - metadata_path = None + config_path = None - if not metadata_path: + if not config_path: err("this does not appear to be a babelized folder (missing 'babel.toml')") raise click.Abort() @@ -166,7 +166,7 @@ def update( out(f"reading template from {template}") try: - babel_metadata = BabelMetadata.from_path(metadata_path) + babel_config = BabelConfig.from_path(config_path) except ValidationError as error: raise BabelizerAbort(str(error)) @@ -187,7 +187,7 @@ def update( out(f"re-rendering {package_path}") with save_files(["CHANGES.rst", "CREDITS.rst"]): render( - babel_metadata, + babel_config, os.path.dirname(package_path), # package_path.parent, template=template, @@ -196,7 +196,7 @@ def update( ) extra_files = _repo_contents(package_path) - _generated_files( - babel_metadata, template=template, version=version + babel_config, template=template, version=version ) ignore = ["meta*", "notebooks*", "docs*", "**/data"] @@ -310,11 +310,11 @@ def _repo_contents(base: str) -> set[str]: def _generated_files( - babel_metadata: BabelMetadata, template: str | None = None, version: str = "0.1" + babel_config: BabelConfig, template: str | None = None, version: str = "0.1" ) -> set[str]: with tempfile.TemporaryDirectory() as tmpdir: new_folder = render( - babel_metadata, + babel_config, tmpdir, template=template, version=version, diff --git a/babelizer/metadata.py b/babelizer/config.py similarity index 84% rename from babelizer/metadata.py rename to babelizer/config.py index 29988cd..1e466be 100644 --- a/babelizer/metadata.py +++ b/babelizer/config.py @@ -1,4 +1,4 @@ -"""Library metadata used by the babelizer to wrap libraries.""" +"""Library configuration used by the babelizer to wrap libraries.""" from __future__ import annotations @@ -27,8 +27,8 @@ from babelizer.errors import ValidationError -class BabelMetadata(Mapping[str, Any]): - """Library metadata.""" +class BabelConfig(Mapping[str, Any]): + """Babelizer configuration.""" LOADERS: dict[str, Callable[[str], dict[str, Any]]] = { "yaml": yaml.safe_load, @@ -44,7 +44,7 @@ def __init__( plugin: dict[str, Any] | None = None, ci: dict[str, Any] | None = None, ): - """Metadata used by the babelizer to wrap a library. + """Configuration used by the babelizer to wrap a library. Parameters ---------- @@ -77,9 +77,9 @@ def __init__( "ci": dict(ci or {}), } - BabelMetadata.validate(config) + BabelConfig.validate(config) - self._meta = BabelMetadata.norm(config) + self._meta = BabelConfig.norm(config) def __getitem__(self, key: str) -> dict[str, Any]: return self._meta[key] @@ -91,8 +91,8 @@ def __len__(self) -> int: return len(self._meta) @classmethod - def from_stream(cls, stream: io.TextIOBase, fmt: str = "toml") -> BabelMetadata: - """Create an instance of BabelMetadata from a file-like object. + def from_stream(cls, stream: io.TextIOBase, fmt: str = "toml") -> BabelConfig: + """Create an instance of BabelConfig from a file-like object. Parameters ---------- @@ -103,28 +103,28 @@ def from_stream(cls, stream: io.TextIOBase, fmt: str = "toml") -> BabelMetadata: Returns ------- - BabelMetadata - A BabelMetadata instance. + BabelConfig + A BabelConfig instance. """ try: - loader = BabelMetadata.LOADERS[fmt] + loader = BabelConfig.LOADERS[fmt] except KeyError: raise ValueError(f"unrecognized format ({fmt})") try: meta = loader(stream.read()) except yaml.scanner.ScannerError as error: - raise ScanError(f"unable to scan yaml-formatted metadata file:\n{error}") + raise ScanError(f"unable to scan yaml-formatted config file:\n{error}") except tomllib.TOMLDecodeError as error: - raise ScanError(f"unable to scan toml-formatted metadata file:\n{error}") + raise ScanError(f"unable to scan toml-formatted config file:\n{error}") else: if not isinstance(meta, dict): - raise ValidationError("metadata file does not contain a mapping object") + raise ValidationError("config file does not contain a mapping object") return cls(**meta) @classmethod - def from_path(cls, filepath: str) -> BabelMetadata: - """Create an instance of BabelMetadata from a path-like object. + def from_path(cls, filepath: str) -> BabelConfig: + """Create an instance of BabelConfig from a path-like object. Parameters ---------- @@ -133,26 +133,26 @@ def from_path(cls, filepath: str) -> BabelMetadata: Returns ------- - A BabelMetadata instance. + A BabelConfig instance. """ path = pathlib.Path(filepath) with open(filepath) as fp: - return BabelMetadata.from_stream(fp, fmt=path.suffix[1:]) + return BabelConfig.from_stream(fp, fmt=path.suffix[1:]) @staticmethod def validate(config: dict[str, Any]) -> None: - """Ensure babelizer configuration metadata are valid. + """Ensure babelizer configuration is valid. Parameters ---------- config : dict - Metadata to babelize a library. + Configuration to babelize a library. Raises ------ ValidationError - If metadata are not valid. + If configuration is not valid. """ libraries = config["library"] if "entry_point" in libraries: @@ -253,29 +253,29 @@ def _handle_old_style_info(info: dict[str, Any]) -> dict[str, Any]: @staticmethod def norm(config: dict[str, Any]) -> dict[str, Any]: - """Ensure current style metadata are used in babelizer configuration. + """Ensure current style is used in babelizer configuration. Parameters ---------- config : dict - Metadata to babelize a library. + Configuration to babelize a library. Return ------ dict - A dict of babelizer configuration metadata. + A dict of babelizer configuration. """ build: dict[str, list[str]] = defaultdict(list) with suppress(KeyError): build.update(config["build"]) if "entry_point" in config["library"]: - libraries = BabelMetadata._handle_old_style_entry_points(config["library"]) + libraries = BabelConfig._handle_old_style_entry_points(config["library"]) else: libraries = {k: dict(v) for k, v in config["library"].items()} if "plugin_author" in config["info"]: - info = BabelMetadata._handle_old_style_info(config["info"]) + info = BabelConfig._handle_old_style_info(config["info"]) else: info = config["info"] @@ -309,7 +309,7 @@ def norm(config: dict[str, Any]) -> dict[str, Any]: } def dump(self, fp: io.TextIOBase, fmt: str = "toml") -> None: - """Write serialized metadata to a file. + """Write serialized configuration to a file. Parameters ---------- @@ -321,7 +321,7 @@ def dump(self, fp: io.TextIOBase, fmt: str = "toml") -> None: print(self.format(fmt=fmt), file=fp, end="") def format(self, fmt: str = "toml") -> str: - """Serialize metadata to output format. + """Serialize configuration to output format. Parameters ---------- @@ -330,17 +330,17 @@ def format(self, fmt: str = "toml") -> str: Returns ------- - metadata : str - Serialized metadata. + config : str + Serialized configuration. """ return getattr(self, f"format_{fmt}")() def format_toml(self) -> str: - """Serialize metadata as TOML. + """Serialize configuration as TOML. Returns ------- str - Serialized metadata as a TOML-formatted string + Serialized configuration as a TOML-formatted string """ return tomli_w.dumps(self._meta, multiline_strings=True) diff --git a/babelizer/render.py b/babelizer/render.py index d7cbe50..5750f4a 100644 --- a/babelizer/render.py +++ b/babelizer/render.py @@ -14,12 +14,12 @@ from babelizer._files.init_py import render as render_init from babelizer._files.lib_init_py import render as render_lib_init from babelizer._files.license_rst import render as render_license +from babelizer.config import BabelConfig from babelizer.errors import OutputDirExistsError -from babelizer.metadata import BabelMetadata def render( - plugin_metadata: BabelMetadata, + babel_config: BabelConfig, output: str, template: str | None = None, clobber: bool = False, @@ -30,8 +30,8 @@ def render( Parameters ---------- - plugin_metadata : BabelMetadata - The metadata used to babelize the library. + babel_config : BabelConfig + The configuration used to babelize the library. output : str Name of the directory that will be the new repository. template : str, optional @@ -58,15 +58,15 @@ def render( context = { "files": { - "_bmi.py": render_bmi(plugin_metadata), - "__init__.py": render_init(plugin_metadata), - "lib/__init__.py": render_lib_init(plugin_metadata), - ".gitignore": render_gitignore(plugin_metadata), - "LICENSE.rst": render_license(plugin_metadata), + "_bmi.py": render_bmi(babel_config), + "__init__.py": render_init(babel_config), + "lib/__init__.py": render_lib_init(babel_config), + ".gitignore": render_gitignore(babel_config), + "LICENSE.rst": render_license(babel_config), }, "now": datetime.datetime.now(), "package_version": version, - } | {k: plugin_metadata[k] for k in plugin_metadata} + } | {k: babel_config[k] for k in babel_config} if os.path.exists(output): raise OutputDirExistsError(output) @@ -76,7 +76,7 @@ def render( path = os.path.realpath(output) with open(os.path.join(path, "babel.toml"), "w") as fp: - plugin_metadata.dump(fp, fmt="toml") + babel_config.dump(fp, fmt="toml") git.Repo.init(path) diff --git a/tests/cli_test.py b/tests/cli_test.py index 83ff3d4..7037397 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -10,7 +10,7 @@ from click.testing import CliRunner from babelizer.cli import babelize -from babelizer.metadata import BabelMetadata +from babelizer.config import BabelConfig def test_help(): @@ -67,7 +67,7 @@ def test_generate_gives_valid_toml(): assert result.exit_code == 0 config = tomllib.loads(result.output) - BabelMetadata.validate(config) + BabelConfig.validate(config) def test_init_noargs():