library.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. from __future__ import annotations
  2. from pathlib import Path
  3. import logging
  4. from typing import Optional
  5. logger = logging.getLogger(__name__)
  6. # -----------------------
  7. # SECTION: Library Class
  8. # -----------------------
  9. class Library:
  10. """Represents a single library with a specific path."""
  11. def __init__(self, name: str, path: Path, priority: int = 0) -> None:
  12. """Initialize a library instance.
  13. Args:
  14. name: Display name for the library
  15. path: Path to the library directory
  16. priority: Priority for library lookup (higher = checked first)
  17. """
  18. self.name = name
  19. self.path = path
  20. self.priority = priority # Higher priority = checked first
  21. def find_by_id(self, module_name: str, template_id: str) -> tuple[Path, str]:
  22. """Find a template by its ID in this library.
  23. Args:
  24. module_name: The module name (e.g., 'compose', 'terraform')
  25. template_id: The template ID to find
  26. Returns:
  27. Path to the template directory if found
  28. Raises:
  29. FileNotFoundError: If the template ID is not found in this library
  30. """
  31. logger.debug(f"Looking for template '{template_id}' in module '{module_name}' in library '{self.name}'")
  32. # Build the path to the specific template directory
  33. template_path = self.path / module_name / template_id
  34. # Check if the template directory and either template.yaml or template.yml exist
  35. if not (template_path.is_dir() and ((template_path / "template.yaml").exists() or (template_path / "template.yml").exists())):
  36. raise FileNotFoundError(f"Template '{template_id}' not found in module '{module_name}' in library '{self.name}'")
  37. logger.debug(f"Found template '{template_id}' at: {template_path}")
  38. return template_path, self.name
  39. def find(self, module_name: str, sort_results: bool = False) -> list[tuple[Path, str]]:
  40. """Find templates in this library for a specific module.
  41. Args:
  42. module_name: The module name (e.g., 'compose', 'terraform')
  43. sort_results: Whether to return results sorted alphabetically
  44. Returns:
  45. List of Path objects representing template directories
  46. Raises:
  47. FileNotFoundError: If the module directory is not found in this library
  48. """
  49. logger.debug(f"Looking for templates in module '{module_name}' in library '{self.name}'")
  50. # Build the path to the module directory
  51. module_path = self.path / module_name
  52. # Check if the module directory exists
  53. if not module_path.is_dir():
  54. raise FileNotFoundError(f"Module '{module_name}' not found in library '{self.name}'")
  55. # Get all directories in the module path that contain a template.yaml or template.yml file
  56. template_dirs = []
  57. try:
  58. for item in module_path.iterdir():
  59. if item.is_dir() and ((item / "template.yaml").exists() or (item / "template.yml").exists()):
  60. template_dirs.append((item, self.name))
  61. except PermissionError as e:
  62. raise FileNotFoundError(f"Permission denied accessing module '{module_name}' in library '{self.name}': {e}")
  63. # Sort if requested
  64. if sort_results:
  65. template_dirs.sort(key=lambda x: x[0].name.lower())
  66. logger.debug(f"Found {len(template_dirs)} templates in module '{module_name}'")
  67. return template_dirs
  68. # !SECTION
  69. # -----------------------------
  70. # SECTION: LibraryManager Class
  71. # -----------------------------
  72. class LibraryManager:
  73. """Manages multiple libraries and provides methods to find templates."""
  74. # FIXME: For now this is static and only has one library
  75. def __init__(self) -> None:
  76. # get the root path of the repository
  77. repo_root = Path(__file__).parent.parent.parent.resolve()
  78. self.libraries = [
  79. Library(name="default", path=repo_root / "library", priority=0)
  80. ]
  81. def find_by_id(self, module_name: str, template_id: str) -> Optional[tuple[Path, str]]:
  82. """Find a template by its ID across all libraries.
  83. Args:
  84. module_name: The module name (e.g., 'compose', 'terraform')
  85. template_id: The template ID to find
  86. Returns:
  87. Path to the template directory if found, None otherwise
  88. """
  89. logger.debug(f"Searching for template '{template_id}' in module '{module_name}' across all libraries")
  90. for library in sorted(self.libraries, key=lambda x: x.priority, reverse=True):
  91. try:
  92. template_path, lib_name = library.find_by_id(module_name, template_id)
  93. logger.debug(f"Found template '{template_id}' in library '{library.name}'")
  94. return template_path, lib_name
  95. except FileNotFoundError:
  96. # Continue searching in next library
  97. continue
  98. logger.debug(f"Template '{template_id}' not found in any library")
  99. return None
  100. def find(self, module_name: str, sort_results: bool = False) -> list[tuple[Path, str]]:
  101. """Find templates across all libraries for a specific module.
  102. Args:
  103. module_name: The module name (e.g., 'compose', 'terraform')
  104. sort_results: Whether to return results sorted alphabetically
  105. Returns:
  106. List of Path objects representing template directories from all libraries
  107. """
  108. logger.debug(f"Searching for templates in module '{module_name}' across all libraries")
  109. all_templates = []
  110. for library in sorted(self.libraries, key=lambda x: x.priority, reverse=True):
  111. try:
  112. templates = library.find(module_name, sort_results=False) # Sort at the end
  113. all_templates.extend(templates)
  114. logger.debug(f"Found {len(templates)} templates in library '{library.name}'")
  115. except FileNotFoundError:
  116. # Module not found in this library, continue with next
  117. logger.debug(f"Module '{module_name}' not found in library '{library.name}'")
  118. continue
  119. # Remove duplicates based on template name (directory name)
  120. seen_names = set()
  121. unique_templates = []
  122. for template in all_templates:
  123. name, library_name = template
  124. if name.name not in seen_names:
  125. unique_templates.append((name, library_name))
  126. seen_names.add(name.name)
  127. # Sort if requested
  128. if sort_results:
  129. unique_templates.sort(key=lambda x: x[0].name.lower())
  130. logger.debug(f"Found {len(unique_templates)} unique templates total")
  131. return unique_templates
  132. # !SECTION