Skip to content

Commit 99b6808

Browse files
author
sun
committed
feat(plugin): add dependency checking for plugin loading
1 parent 7e212d0 commit 99b6808

2 files changed

Lines changed: 48 additions & 7 deletions

File tree

2.08 KB
Binary file not shown.

src/compileo/features/plugin/manager.py

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -344,16 +344,21 @@ def _load_single_plugin(self, plugin_dir: Path):
344344
with open(manifest_path, 'r') as f:
345345
manifest_data = yaml.safe_load(f)
346346
manifest = PluginManifest(**manifest_data)
347-
347+
348+
# Check dependencies before loading
349+
if not self._check_dependencies(plugin_dir):
350+
logger.warning(f"Skipping plugin {manifest.id}: dependencies not installed")
351+
return
352+
348353
# Add plugin src to sys.path so it can import its own modules
349354
# We add the root of the plugin directory
350355
if str(plugin_dir) not in sys.path:
351356
sys.path.insert(0, str(plugin_dir))
352-
357+
353358
# Dynamic Import
354359
entry_path_dir = plugin_dir / manifest.entry_point.replace(".", "/") / "__init__.py"
355360
entry_path_file = plugin_dir / f"{manifest.entry_point.replace('.', '/')}.py"
356-
361+
357362
spec = None
358363
if entry_path_dir.exists():
359364
spec = importlib.util.spec_from_file_location(
@@ -365,15 +370,15 @@ def _load_single_plugin(self, plugin_dir: Path):
365370
f"plugins.{manifest.id}",
366371
entry_path_file
367372
)
368-
373+
369374
if spec and spec.loader:
370375
module = importlib.util.module_from_spec(spec)
371376
sys.modules[f"plugins.{manifest.id}"] = module
372377
spec.loader.exec_module(module)
373-
378+
374379
self.loaded_plugins[manifest.id] = module
375380
self.plugin_manifests[manifest.id] = manifest
376-
381+
377382
# Register Extensions
378383
self._register_extensions(manifest, module)
379384
logger.info(f"Loaded plugin: {manifest.name} ({manifest.version})")
@@ -390,14 +395,50 @@ def _register_extensions(self, manifest: PluginManifest, module: Any):
390395
for ext_point, items in manifest.extensions.items():
391396
if ext_point not in self.extensions:
392397
self.extensions[ext_point] = {}
393-
398+
394399
for key, class_name in items.items():
395400
if hasattr(module, class_name):
396401
cls = getattr(module, class_name)
397402
self.extensions[ext_point][key] = cls
398403
else:
399404
logger.warning(f"Plugin {manifest.id} declares {class_name} for {ext_point}, but class not found in module.")
400405

406+
def _check_dependencies(self, plugin_dir: Path) -> bool:
407+
"""
408+
Checks if the plugin's dependencies are installed by attempting to import key packages.
409+
"""
410+
requirements_path = plugin_dir / "requirements.txt"
411+
if not requirements_path.exists():
412+
return True # No requirements file, assume dependencies are satisfied
413+
414+
try:
415+
with open(requirements_path, 'r') as f:
416+
requirements = f.read().splitlines()
417+
418+
# Extract package names (before any version specifiers)
419+
packages_to_check = []
420+
for req in requirements:
421+
req = req.strip()
422+
if req and not req.startswith('#'):
423+
# Extract package name (e.g., 'scrapy==2.11.1' -> 'scrapy')
424+
package_name = req.split('==')[0].split('>=')[0].split('>')[0].split('<')[0].split('<=')[0].split('!=')[0].strip()
425+
if package_name:
426+
packages_to_check.append(package_name)
427+
428+
# Try to import each package
429+
for package in packages_to_check:
430+
try:
431+
importlib.import_module(package)
432+
except ImportError:
433+
logger.debug(f"Dependency check failed for {package} in plugin {plugin_dir.name}")
434+
return False
435+
436+
return True
437+
438+
except Exception as e:
439+
logger.warning(f"Error checking dependencies for plugin {plugin_dir.name}: {e}")
440+
return False
441+
401442
def get_extensions(self, extension_point: str) -> Dict[str, Any]:
402443
"""
403444
Returns a dictionary of {key: ImplementationClass} for a given extension point.

0 commit comments

Comments
 (0)