Skip to content

Commit

Permalink
Refactor template handling
Browse files Browse the repository at this point in the history
Refactor `catalog_templates` to incorporate directory names for better path resolution. Updated `pyproject.toml` templates to align with the new requirements structure. Added logging for template loading in the Jinja2 environment.
  • Loading branch information
coordt committed Nov 10, 2024
1 parent 4af116f commit 228c06a
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 35 deletions.
6 changes: 5 additions & 1 deletion project_forge/rendering/environment.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
"""Tools and classes for managing the Jinja2 rendering environment."""

import logging
import re
from collections import ChainMap
from pathlib import Path
from typing import Any, Callable, List, Optional

from jinja2 import BaseLoader, Environment, TemplateNotFound, Undefined

logger = logging.getLogger(__name__)


class InheritanceMap(ChainMap[str, Path]):
"""Provides convenience functions for managing template inheritance."""
Expand Down Expand Up @@ -57,7 +60,7 @@ def __init__(self, inheritance_map: InheritanceMap):
def get_source(self, environment: Environment, template: str) -> tuple[str, str | None, Callable[[], bool] | None]:
"""Load the template."""
# Parse the name of the template
bits = template.split("/")
bits = template.split("/", maxsplit=1)
index = 0
if len(bits) == 2 and bits[0].isdigit():
index = int(bits[0])
Expand All @@ -77,6 +80,7 @@ def get_source(self, environment: Environment, template: str) -> tuple[str, str
raise TemplateNotFound(template) # Maybe this wasn't one of our customized extended paths

path = inheritance[index]
logger.debug(f"Loading template {template_name} from: {path}")
source = path.read_text()

# look for an `extends` tag
Expand Down
20 changes: 10 additions & 10 deletions project_forge/rendering/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ def catalog_templates(template_dir: Path) -> Dict[str, Path]:
For a file structure like:
/path-to-templates/
{{ repo_name }}/
file1.txt
subdir/
file2.txt
empty-subdir/
{{ repo_name }}/
file1.txt
subdir/
file2.txt
empty-subdir/
A call to `catalog_templates(Path("/path-to-templates"))` would return:
A call to `catalog_templates(Path("/path-to-templates/{{ repo_name }}/"))` would return:
{
"{{ repo_name }}": Path("/path-to-templates/{{ repo_name }}"),
Expand All @@ -37,14 +36,15 @@ def catalog_templates(template_dir: Path) -> Dict[str, Path]:
Returns:
A mapping of the relative path as a string to the full path
"""
templates = {}
root_dir = template_dir.parent
templates = {template_dir.name: template_dir}
for root, dirs, files in template_dir.walk():
for file in files:
template_path = root / file
templates[str(template_path.relative_to(template_dir))] = template_path
templates[str(template_path.relative_to(root_dir))] = template_path
for dir_ in dirs:
template_path = root / dir_
templates[str(template_path.relative_to(template_dir))] = template_path
templates[str(template_path.relative_to(root_dir))] = template_path
return {key: templates[key] for key in sorted(templates)}


Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/python-boilerplate/pattern.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ prompt = "What is the GitHub user name or organization?"
default = "whoami"
type = "str"

[[extra_context.requirements]]
[extra_context.requirements]
prod = [
"environs",
]
Expand Down
18 changes: 10 additions & 8 deletions tests/fixtures/python-boilerplate/{{ repo_name }}/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{% extends "{{ repo_name }}/pyproject.toml" %}
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Expand All @@ -15,25 +16,26 @@ keywords = ["{{ repo_name }}"]
dynamic = ["version"]
license = { file = "LICENSE" }
requires-python = ">=3.9"
dependencies = [{% block dependencies %}{% for pkg, version in dependencies.items() %}
"{{ pkg }}{{ version }}",
dependencies = [{% block dependencies %}{% for pkg in requirements.prod %}
"{{ pkg }}",
{%- endfor %}{% endblock dependencies %}
]

[project.optional-dependencies]
{% block optional_dependencies %}
dev = [{% block dev_dependencies %}{% for pkg, version in dev_requirements.items() %}
"{{ pkg }}{{ version }}",
dev = [{% block dev_dependencies %}{% for pkg in requirements.dev %}
"{{ pkg }}",
{%- endfor %}{% endblock dev_dependencies %}
]
test = [{% block test_dependencies %}{% for pkg, version in test_requirements.items() %}
"{{ pkg }}{{ version }}",
test = [{% block test_dependencies %}{% for pkg in requirements.test %}
"{{ pkg }}",
{%- endfor %}{% endblock test_dependencies %}
]
docs = [{% block docs_dependencies %}{% for pkg, version in docs_requirements.items() %}
"{{ pkg }}{{ version }}",
docs = [{% block docs_dependencies %}{% for pkg in requirements.docs %}
"{{ pkg }}",
{%- endfor %}{% endblock docs_dependencies %}
]
{% endblock optional_dependencies %}

[tool.hatch.version]
path = "{{ repo_name }}/__init__.py"
6 changes: 1 addition & 5 deletions tests/fixtures/python-package/{{ repo_name }}/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
{% extends "pyproject.toml" %}

{% block test_dependencies %}{{ super() }}{% for pkg, version in test_requirements.items() %}
"{{ pkg }}{{ version }}",
{%- endfor %}{% endblock test_dependencies %}
{% extends "{{ repo_name }}/pyproject.toml" %}
28 changes: 18 additions & 10 deletions tests/test_rendering/test_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@ def test_result_keys_are_relative_filepaths(self, tmp_path: Path):
# Assemble
generate_fake_templates(tmp_path)
template1 = tmp_path / "template1"
expected_keys = {"subdir", "empty", "inherit.txt", "subdir/subdir.txt", "template1.txt"}
expected_keys = {
"template1/subdir",
"template1/empty",
"template1/inherit.txt",
"template1/subdir/subdir.txt",
"template1",
"template1/template1.txt",
}

# Act
result = catalog_templates(template1)
Expand All @@ -43,14 +50,13 @@ def test_result_keys_are_relative_filepaths(self, tmp_path: Path):
assert set(result.keys()) == expected_keys

for key in expected_keys:
assert (template1 / key).exists()
assert (tmp_path / key).exists()

def test_result_values_are_full_paths(self, tmp_path: Path):
"""The returned values are full filepaths as `Path`s."""
# Assemble
generate_fake_templates(tmp_path)
template1 = tmp_path / "template1"
expected_keys = {"subdir", "empty", "inherit.txt", "subdir/subdir.txt", "template1.txt"}

# Act
result = catalog_templates(template1)
Expand Down Expand Up @@ -88,14 +94,16 @@ def test_multiple_paths_has_multiple_maps(self, tmp_path: Path):
len(result.maps) == len(template_paths) + 1
), "Number of children should match number of template paths plus 1"
assert result.maps[0] == {
"inherit.txt": tmp_path / "template2/inherit.txt",
"template2.txt": tmp_path / "template2/template2.txt",
"template2/inherit.txt": tmp_path / "template2/inherit.txt",
"template2/template2.txt": tmp_path / "template2/template2.txt",
"template2": tmp_path / "template2",
}
assert result.maps[1] == {
"inherit.txt": tmp_path / "template1/inherit.txt",
"template1.txt": tmp_path / "template1/template1.txt",
"subdir/subdir.txt": tmp_path / "template1/subdir/subdir.txt",
"empty": tmp_path / "template1/empty",
"subdir": tmp_path / "template1/subdir",
"template1/inherit.txt": tmp_path / "template1/inherit.txt",
"template1/template1.txt": tmp_path / "template1/template1.txt",
"template1/subdir/subdir.txt": tmp_path / "template1/subdir/subdir.txt",
"template1/empty": tmp_path / "template1/empty",
"template1/subdir": tmp_path / "template1/subdir",
"template1": tmp_path / "template1",
}
assert result.maps[2] == {}

0 comments on commit 228c06a

Please sign in to comment.