module.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. from abc import ABC
  2. from pathlib import Path
  3. from typing import Optional, Dict, Any
  4. import logging
  5. import yaml
  6. from typer import Typer, Option, Argument
  7. from rich.console import Console
  8. from .exceptions import TemplateNotFoundError
  9. from .library import LibraryManager
  10. logger = logging.getLogger('boilerplates')
  11. console = Console()
  12. class Module(ABC):
  13. """Streamlined base module that auto-detects variables from templates."""
  14. # Required class attributes for subclasses
  15. name = None
  16. description = None
  17. files = None
  18. def __init__(self):
  19. if not all([self.name, self.description, self.files]):
  20. raise ValueError(
  21. f"Module {self.__class__.__name__} must define name, description, and files"
  22. )
  23. self.libraries = LibraryManager()
  24. # Initialize variables if the subclass defines _init_variables method
  25. if hasattr(self, '_init_variables'):
  26. self._init_variables()
  27. self.metadata = self._build_metadata()
  28. def _build_metadata(self) -> Dict[str, Any]:
  29. """Build metadata from class attributes."""
  30. metadata = {}
  31. # Add categories if defined
  32. if hasattr(self, 'categories'):
  33. metadata['categories'] = self.categories
  34. # Add variable metadata if defined
  35. if hasattr(self, 'variable_metadata'):
  36. metadata['variables'] = self.variable_metadata
  37. return metadata
  38. def list(self):
  39. """List all templates."""
  40. templates = self.libraries.find(self.name, self.files, sorted=True)
  41. for template in templates:
  42. console.print(f"[cyan]{template.id}[/cyan] - {template.name}")
  43. return templates
  44. def show(self, id: str = Argument(..., help="Template ID")):
  45. """Show template details."""
  46. template = self._get_template(id)
  47. # Header
  48. version = f" v{template.version}" if template.version else ""
  49. console.print(f"[bold magenta]{template.name} ({template.id}{version})[/bold magenta]")
  50. console.print(f"[dim white]{template.description}[/dim white]\n")
  51. # Metadata (only print if exists)
  52. metadata = [
  53. ("Author", template.author),
  54. ("Date", template.date),
  55. ("Tags", ', '.join(template.tags) if template.tags else None)
  56. ]
  57. for label, value in metadata:
  58. if value:
  59. console.print(f"{label}: [cyan]{value}[/cyan]")
  60. # Variables
  61. if template.vars:
  62. console.print(f"Variables: [cyan]{', '.join(sorted(template.vars))}[/cyan]")
  63. # Content
  64. if template.content:
  65. print(f"\n{template.content}")
  66. def _get_template(self, template_id: str):
  67. """Get template by ID with unified error handling."""
  68. template = self.libraries.find_by_id(self.name, self.files, template_id)
  69. if not template:
  70. raise TemplateNotFoundError(template_id, self.name)
  71. return template
  72. def generate(
  73. self,
  74. id: str = Argument(..., help="Template ID"),
  75. out: Optional[Path] = Option(None, "--out", "-o")
  76. ):
  77. """Generate from template."""
  78. template = self._get_template(id)
  79. # Validate template (will raise TemplateValidationError if validation fails)
  80. module_variable_registry = getattr(self, 'variables', None)
  81. template.validate(module_variable_registry, id)
  82. print("TEST SUCCESSFUL")
  83. def register_cli(self, app: Typer):
  84. """Register module commands with the main app."""
  85. module_app = Typer()
  86. module_app.command()(self.list)
  87. module_app.command()(self.show)
  88. module_app.command()(self.generate)
  89. app.add_typer(module_app, name=self.name, help=self.description)