library.py 6.9 KB

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