Skip to content

Commit

Permalink
Basic validation for plugin settings and pattern for extending valida…
Browse files Browse the repository at this point in the history
…tors in construct
  • Loading branch information
Rexeh committed Dec 22, 2023
1 parent 76e0cb3 commit 5c461a4
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 27 deletions.
7 changes: 7 additions & 0 deletions docs/development/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
## Parser Plugins
With version 2.0, Joystick Diagrams has moved to a plugin system to allow further games to be supported faster.

## Plugin Structure
- plugins/ - Directory for plugins
- *plugin name*
- \__init\__.py
- main.py
- config.py

### Example Plugin

```python
Expand Down
23 changes: 17 additions & 6 deletions joystick_diagrams/plugin_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@
import logging
import os
from importlib import import_module
from json import JSONDecodeError
from pathlib import Path
from types import ModuleType

import dynaconf
from dynaconf import ValidationError

import joystick_diagrams.exceptions as JDException
from joystick_diagrams.plugins.plugin_interface import PluginInterface

_logger = logging.getLogger(__name__)
PLUGINS_DIRECTORY = "plugins"
PLUGIN_REL_PATH = ".plugins."


class ParserPluginManager:
Expand All @@ -27,20 +33,25 @@ def __init__(self):
for plugin in self.plugins:
try:
# Try initiate the plugin
self.loaded_plugins.append(self.load_plugin(plugin))
except JDException.PluginNotValid as e:
_logger.error(e)
loaded = self.load_plugin(plugin)
self.validate_plugin(loaded)
self.loaded_plugins.append(loaded)

except (JDException.PluginNotValid, JSONDecodeError, AttributeError, ValidationError) as e:
_logger.error(f"Error with Plugin: {plugin} - {e}")

else:
raise JDException.NoPluginsExist()

def validate_plugin(self, plugin: PluginInterface) -> None | ValidationError:
return plugin.settings.validators.validate_all()

def load_plugin(self, module_path: str) -> ModuleType:
"""Attempt to load the plugin"""
try:
_logger.debug(f"Loading plugin at module path: {module_path}")
directory = ".plugins."
path = os.path.join(directory, module_path)

return import_module(directory + module_path + ".main", package=__package__).ParserPlugin()
return import_module(PLUGIN_REL_PATH + module_path + ".main", package=__package__).ParserPlugin()
except TypeError as e:
_logger.error(f"{e} - {module_path}")
raise JDException.PluginNotValid(error=e, value=module_path) from e
Expand Down
13 changes: 13 additions & 0 deletions joystick_diagrams/plugins/examplePlugin/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from pathlib import Path

from dynaconf import Dynaconf, Validator

settings = Dynaconf(
settings_files=[f"{Path(__file__).parent.joinpath('settings.json')}"],
)

settings.validators.register(
Validator("PLUGIN_NAME", required=True),
Validator("PLUGIN_ICON", required=True),
Validator("VERSION", required=True),
)
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import logging
from pathlib import Path

from joystick_diagrams.plugins.plugin_interface import PluginInterface

from .config import settings
from config import settings
from dynaconf import Dynaconf, Validator

PLUGIN_NAME = "Star Citizen"
PLUGIN_ICON = "img/logo.ico"
VERSION = "0.0.1"
from joystick_diagrams.plugins.plugin_interface import PluginInterface

_logger = logging.getLogger("__name__")

Expand All @@ -16,6 +13,7 @@ class ParserPlugin(PluginInterface):
def __init__(self):
self.path = None
self.settings = settings
self.settings.validators.register()

def process(self):
return None
Expand All @@ -26,16 +24,20 @@ def set_path(self, path: Path) -> bool:

@property
def name(self) -> str:
return f"{PLUGIN_NAME}"
return f"{self.settings.PLUGIN_NAME}"

@property
def version(self) -> str:
return f"{VERSION}"
return f"{self.settings.VERSION}"

@property
def icon(self) -> str:
return f"{Path.joinpath(Path(__file__).parent,PLUGIN_ICON)}"
return f"{Path.joinpath(Path(__file__).parent,self.settings.PLUGIN_ICON)}"

@property
def get_path(self) -> bool:
return self.path


if __name__ == "__main__":
plugin = ParserPlugin()
5 changes: 5 additions & 0 deletions joystick_diagrams/plugins/examplePlugin/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"PLUGIN_NAME": "Star Citizen",
"PLUGIN_ICON": "./img/logo.ico",
"VERSION": "0.0.1"
}
11 changes: 0 additions & 11 deletions joystick_diagrams/plugins/plugin1/config.py

This file was deleted.

1 change: 0 additions & 1 deletion joystick_diagrams/plugins/plugin1/settings.json

This file was deleted.

4 changes: 4 additions & 0 deletions joystick_diagrams/plugins/plugin_interface.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from abc import ABC, abstractmethod
from pathlib import Path

import dynaconf

import joystick_diagrams.exceptions as JDException


class PluginInterface(ABC):
settings: dynaconf.LazySettings

def file_not_valid_exception(self, exceptionMessage: str):
return JDException.FileNotValid(value=exceptionMessage)

Expand Down

0 comments on commit 5c461a4

Please sign in to comment.