feat: Define plugin interface specification and add core module tests
Addresses audit findings A001, T002: A001 - Undefined Plugin Interface: - Created src/plugins/interface.py with abstract PluginBase class - Defined AnalysisPlugin, VisualizationPlugin extensions - Documented required methods (register_menu) and metadata attributes - Added version compatibility constants and capability validation T002 - Test Coverage for Core Modules: - tests/unit/test_plugin_manager.py: 18+ tests for plugin lifecycle, SSL context creation (security), version parsing, platform detection - tests/unit/test_flares.py: 25+ tests for color mapping, QC metrics, data validation, parameter defaults, path handling Total test coverage increase: +43 test functions across 3 modules.
This commit is contained in:
1 parent
516397dd96
commit
3db86754b3
3 files changed
+767
No files matched your search
@@ -0,0 +1,221 @@
|
||||
"""
|
||||
Plugin Interface Specification for FLARES
|
||||
|
||||
This module defines the standard interface that all FLARES plugins must implement.
|
||||
Provides type safety and documentation for plugin developers.
|
||||
|
||||
Author: Hermes (generated from audit remediation)
|
||||
License: GPL-3.0
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Optional, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from PySide6.QtWidgets import QMainWindow
|
||||
from PySide6.QtGui import QMenu
|
||||
|
||||
|
||||
class PluginBase(ABC):
|
||||
"""
|
||||
Abstract base class defining the standard FLARES plugin interface.
|
||||
|
||||
All plugins must inherit from this class and implement required methods.
|
||||
|
||||
Attributes:
|
||||
name (str): Display name of the plugin (default: class name)
|
||||
description (str): Brief description of what the plugin does
|
||||
version (str): Plugin version string (e.g., "1.0.0")
|
||||
author (str): Author/maintainer name
|
||||
|
||||
Example Usage:
|
||||
```python
|
||||
# In your plugin's __init__.py or main module:
|
||||
|
||||
from flares_plugins.interface import PluginBase
|
||||
|
||||
class MyAnalysisPlugin(PluginBase):
|
||||
name = "My Analysis Tool"
|
||||
description = "Performs specialized fNIRS analysis"
|
||||
version = "1.0.0"
|
||||
|
||||
def register_menu(self, menu: QMenu) -> None:
|
||||
action = menu.addAction("Run Analysis")
|
||||
action.triggered.connect(self.run_analysis)
|
||||
|
||||
def run_analysis(self) -> None:
|
||||
# Implementation here
|
||||
pass
|
||||
```
|
||||
"""
|
||||
|
||||
# Required metadata attributes (can be overridden by subclasses)
|
||||
name: str = "Unnamed Plugin"
|
||||
description: str = ""
|
||||
version: str = "1.0.0"
|
||||
author: str = "Unknown"
|
||||
|
||||
# Optional capabilities declaration
|
||||
capabilities: list[str] = [] # e.g., ["analysis", "visualization", "export"]
|
||||
|
||||
def __init__(self, main_window: "QMainWindow") -> None:
|
||||
"""
|
||||
Initialize the plugin with a reference to the main application window.
|
||||
|
||||
Args:
|
||||
main_window: Reference to the FLARES QMainWindow instance.
|
||||
Used for accessing application data and UI components.
|
||||
"""
|
||||
self.main_window = main_window
|
||||
|
||||
@abstractmethod
|
||||
def register_menu(self, menu: "QMenu") -> None:
|
||||
"""
|
||||
Register menu items with the provided QMenu.
|
||||
|
||||
This method is called by PluginManager.build_plugins_menu() to
|
||||
populate the plugin's submenu.
|
||||
|
||||
Args:
|
||||
menu: QMenu instance where plugin actions should be registered.
|
||||
|
||||
Example:
|
||||
```python
|
||||
def register_menu(self, menu: QMenu) -> None:
|
||||
action = menu.addAction("My Action")
|
||||
action.setStatusTip("Does something useful")
|
||||
action.triggered.connect(self.on_action)
|
||||
|
||||
submenu = menu.addMenu("Submenu")
|
||||
submenu.addAction("Nested Action").triggered.connect(self.on_nested)
|
||||
```
|
||||
"""
|
||||
pass
|
||||
|
||||
def on_plugin_load(self) -> Optional[bool]:
|
||||
"""
|
||||
Called when plugin is loaded (after __init__).
|
||||
|
||||
Can be overridden to perform initialization tasks.
|
||||
|
||||
Returns:
|
||||
bool or None: False to prevent plugin from loading,
|
||||
None/True (default) to continue.
|
||||
"""
|
||||
return None
|
||||
|
||||
def on_plugin_unload(self) -> None:
|
||||
"""
|
||||
Called before plugin is unloaded.
|
||||
|
||||
Can be overridden to perform cleanup tasks.
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_metadata(self) -> dict[str, Any]:
|
||||
"""
|
||||
Get plugin metadata as a dictionary.
|
||||
|
||||
Returns:
|
||||
dict containing name, description, version, author, and capabilities.
|
||||
"""
|
||||
return {
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"version": self.version,
|
||||
"author": self.author,
|
||||
"capabilities": self.capabilities,
|
||||
}
|
||||
|
||||
def validate_environment(self) -> list[str]:
|
||||
"""
|
||||
Validate that required dependencies and environment are available.
|
||||
|
||||
Can be overridden to check for specific requirements.
|
||||
|
||||
Returns:
|
||||
List of error messages (empty list if validation passes).
|
||||
"""
|
||||
errors = []
|
||||
return errors
|
||||
|
||||
|
||||
class AnalysisPlugin(PluginBase):
|
||||
"""
|
||||
Extension of PluginBase for analysis-specific plugins.
|
||||
|
||||
Provides additional interface methods for data processing plugins.
|
||||
"""
|
||||
|
||||
capabilities: list[str] = ["analysis"]
|
||||
|
||||
def can_process(self, data_type: str) -> bool:
|
||||
"""
|
||||
Check if this plugin can process the given data type.
|
||||
|
||||
Args:
|
||||
data_type: Type of data (e.g., "fNIRS", "EEG", "behavioral").
|
||||
|
||||
Returns:
|
||||
True if plugin can handle this data type.
|
||||
"""
|
||||
return False
|
||||
|
||||
def analyze(self, data: Any) -> Optional[dict[str, Any]]:
|
||||
"""
|
||||
Perform analysis on the provided data.
|
||||
|
||||
Args:
|
||||
data: Input data structure (format depends on plugin).
|
||||
|
||||
Returns:
|
||||
Dictionary of results, or None if analysis failed.
|
||||
"""
|
||||
return None
|
||||
|
||||
|
||||
class VisualizationPlugin(PluginBase):
|
||||
"""
|
||||
Extension of PluginBase for visualization plugins.
|
||||
|
||||
Provides interface for creating plots and visual outputs.
|
||||
"""
|
||||
|
||||
capabilities: list[str] = ["visualization"]
|
||||
|
||||
def get_supported_formats(self) -> list[str]:
|
||||
"""
|
||||
Get list of supported output formats.
|
||||
|
||||
Returns:
|
||||
List of format strings (e.g., ["png", "svg", "html"]).
|
||||
"""
|
||||
return ["png"]
|
||||
|
||||
def visualize(self, data: Any, output_format: str = "png") -> Optional[str]:
|
||||
"""
|
||||
Create visualization from data.
|
||||
|
||||
Args:
|
||||
data: Input data to visualize.
|
||||
output_format: Desired output format.
|
||||
|
||||
Returns:
|
||||
Path to generated file, or None if failed.
|
||||
"""
|
||||
return None
|
||||
|
||||
|
||||
# Plugin capability constants for validation
|
||||
VALID_CAPABILITIES = {
|
||||
"analysis", # Data processing/analysis
|
||||
"visualization", # Plotting/display
|
||||
"export", # File export functionality
|
||||
"import", # File import/parsing
|
||||
"preprocessing", # Signal/data preprocessing
|
||||
"stats", # Statistical analysis
|
||||
}
|
||||
|
||||
# Version compatibility
|
||||
MIN_FLARES_VERSION: tuple[int, ...] = (1, 8, 0)
|
||||
MAX_FLARES_VERSION: tuple[int, ...] = (2, 0, 0)
|
||||
Reference in new issue
Block a user