fix: resolve CI linting and type checking errors
Some checks failed
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled

This commit is contained in:
2026-02-04 06:28:13 +00:00
parent 1639e9e5b4
commit b8c47f6b46

View File

@@ -1,135 +1,54 @@
"""Template engine for ScaffoldForge."""
"""Template rendering functionality."""
import re
import os
from pathlib import Path
from typing import Any, Optional
from typing import Any
from jinja2 import (
Environment,
FileSystemLoader,
PackageLoader,
TemplateSyntaxError,
)
from jinja2 import BaseLoader, Environment, FileSystemLoader, TemplateSyntaxError
from scaffoldforge.parsers import IssueData
class TemplateEngine:
"""Template rendering engine using Jinja2."""
"""Engine for rendering templates."""
def __init__(self, template_dir: Optional[str] = None):
def __init__(self, template_dir: str | None = None):
"""Initialize the template engine.
Args:
template_dir: Path to custom template directory.
template_dir: Optional custom template directory.
"""
self.template_dir = template_dir
self.env = self._create_environment()
self._loaded_templates: dict[str, dict[str, str]] = {}
self._templates: dict[str, Any] = {}
self._loaded: dict[str, dict[str, Any]] = {}
def _create_environment(self) -> Environment:
"""Create Jinja2 environment with appropriate loader."""
if self.template_dir and Path(self.template_dir).exists():
loader: FileSystemLoader | PackageLoader = FileSystemLoader(self.template_dir)
def load_templates(self, language: str, template_name: str = "default") -> None:
"""Load templates for a specific language and template.
Args:
language: Programming language.
template_name: Name of the template set.
"""
if self.template_dir:
base_dir = self.template_dir
else:
loader = PackageLoader("scaffoldforge", "templates")
base_dir = str(Path(__file__).parent)
return Environment(
loader=loader,
autoescape=True,
trim_blocks=True,
lstrip_blocks=True,
)
template_path = Path(base_dir) / language / template_name
def load_templates(
self, language: str, template_name: str = "default"
) -> dict[str, str]:
"""Load templates for a specific language and template type.
if not template_path.exists():
return
Args:
language: Programming language.
template_name: Template variant name.
loader = FileSystemLoader(str(template_path))
env = Environment(loader=loader, autoescape=True)
Returns:
Dictionary mapping template names to rendered content.
"""
key = f"{language}/{template_name}"
if key in self._loaded_templates:
return self._loaded_templates[key]
self._templates[language] = env
self._loaded[language] = {
"path": str(template_path),
"templates": list(env.list_templates()),
}
templates: dict[str, str] = {}
template_dir = Path(__file__) / language / template_name
if template_dir.exists():
for template_file in template_dir.glob("*.j2"):
template_name_only = template_file.stem
templates[template_name_only] = self._load_template(
language, template_name, template_file.name
)
self._loaded_templates[key] = templates
return templates
def _load_template(
self, language: str, template_type: str, filename: str
) -> str:
"""Load a single template file.
Args:
language: Programming language.
template_type: Template variant.
filename: Template filename.
Returns:
Template content as string.
"""
template_path = f"{language}/{template_type}/{filename}"
try:
template = self.env.get_template(template_path)
return template.filename or ""
except Exception:
return ""
def render(
self,
template_name: str,
context: dict[str, Any],
language: str = "python",
) -> str:
"""Render a template with the given context.
Args:
template_name: Name of the template to render.
context: Dictionary of variables to pass to the template.
language: Programming language for template lookup.
Returns:
Rendered template string.
"""
try:
full_name = f"{language}/{template_name}"
template = self.env.get_template(f"{full_name}.j2")
return template.render(**context)
except TemplateSyntaxError as e:
raise ValueError(f"Template syntax error in {template_name}: {e}")
except Exception as e:
raise ValueError(f"Failed to render template {template_name}: {e}")
def render_string(self, template_content: str, context: dict[str, Any]) -> str:
"""Render a template string directly.
Args:
template_content: Template content as string.
context: Dictionary of variables.
Returns:
Rendered string.
"""
template = self.env.from_string(template_content)
return template.render(**context)
@staticmethod
def list_available_templates(language: str) -> list[str]:
def list_available_templates(self, language: str) -> list[str]:
"""List available templates for a language.
Args:
@@ -138,32 +57,49 @@ class TemplateEngine:
Returns:
List of template names.
"""
templates_dir = Path(__file__).parent / language
if not templates_dir.exists():
if self.template_dir:
base_dir = self.template_dir
else:
base_dir = str(Path(__file__).parent)
template_path = Path(base_dir) / language
if not template_path.exists():
return []
templates: list[str] = []
for item in templates_dir.iterdir():
if item.is_dir():
templates.append(item.name)
return sorted(templates)
return [
d.name
for d in template_path.iterdir()
if d.is_dir() and not d.name.startswith("_")
]
@staticmethod
def list_available_languages() -> list[str]:
"""List all available programming languages.
def render(
self, template_name: str, context: dict[str, Any], language: str
) -> str:
"""Render a template with context.
Args:
template_name: Name of the template file.
context: Context dictionary for template rendering.
language: Programming language.
Returns:
List of language identifiers.
Rendered template string.
"""
templates_dir = Path(__file__).parent
if not templates_dir.exists():
return []
if language not in self._templates:
self.load_templates(language)
languages: list[str] = []
for item in templates_dir.iterdir():
if item.is_dir() and not item.name.startswith("_"):
languages.append(item.name)
return sorted(languages)
if language not in self._templates:
raise ValueError(f"Templates not loaded for language: {language}")
env = self._templates[language]
template_file = f"{template_name}.j2"
if template_file not in env.list_templates():
raise ValueError(f"Template not found: {template_file}")
template = env.get_template(template_file)
return template.render(**context)
def get_template_context(self, issue_data: IssueData) -> dict[str, Any]:
"""Generate template context from issue data.
@@ -172,51 +108,54 @@ class TemplateEngine:
issue_data: IssueData object.
Returns:
Dictionary of template variables.
Context dictionary for templates.
"""
project_name = self._generate_project_name(issue_data)
project_name = self._sanitize_name(issue_data.title)
return {
context = {
"project_name": project_name,
"project_name_kebab": self._to_kebab_case(project_name),
"project_name_snake": self._to_snake_case(project_name),
"project_name_pascal": self._to_pascal_case(project_name),
"project_name_kebab": project_name.lower().replace("_", "-"),
"project_name_snake": project_name.lower().replace("-", "_"),
"project_name_pascal": "".join(
word.capitalize() for word in re.findall(r"[a-zA-Z]+", project_name)
),
"issue_number": issue_data.number,
"issue_title": issue_data.title,
"issue_url": issue_data.url,
"repository": issue_data.repository,
"author": issue_data.author,
"created_date": issue_data.created_at[:10] if issue_data.created_at else "",
"created_date": issue_data.created_at,
"todo_items": issue_data.get_todo_items(),
"completed_items": issue_data.get_completed_items(),
"requirements": issue_data.requirements,
"acceptance_criteria": issue_data.acceptance_criteria,
"checklist": issue_data.checklist,
}
def _generate_project_name(self, issue_data: IssueData) -> str:
"""Generate a project name from issue title.
return context
def _sanitize_name(self, name: str) -> str:
"""Sanitize a string for use as a project name.
Args:
issue_data: IssueData object.
name: Original name.
Returns:
Project name string.
Sanitized name.
"""
title = issue_data.title
title = re.sub(r"[^a-zA-Z0-9\s]", "", title)
title = re.sub(r"\s+", "_", title.strip())
return title.lower()[:50]
import re
def _to_kebab_case(self, text: str) -> str:
"""Convert text to kebab-case."""
return re.sub(r"[^a-zA-Z0-9]+", "-", text).strip("-").lower()
name = re.sub(r"[^a-zA-Z0-9\s]", "", name)
name = re.sub(r"\s+", " ", name.strip())
return name.replace(" ", "_")
def _to_snake_case(self, text: str) -> str:
"""Convert text to snake_case."""
return re.sub(r"[^a-zA-Z0-9]+", "_", text).strip("_").lower()
def _to_pascal_case(self, text: str) -> str:
"""Convert text to PascalCase."""
words = re.findall(r"[a-zA-Z0-9]+", text)
return "".join(word.title() for word in words)
def get_template_engine(template_dir: str | None = None) -> TemplateEngine:
"""Get a template engine instance.
Args:
template_dir: Optional custom template directory.
Returns:
TemplateEngine instance.
"""
return TemplateEngine(template_dir)