Source code for masterpiece.core.application
import argparse
from typing import List, Type, Optional, Union
from .plugmaster import PlugMaster
from .composite import Composite
from .masterpiece import MasterPiece
[docs]
class Application(Composite):
"""Masterpiece application base class. Implements startup argument parsing,
plugin management and initialization of class attributes through
class specific JSON configuration files.
.. todo:: Abstract the serialization API to support multiple formats (e.g., JSON, XML, etc.).
"""
plugins: List[Type[MasterPiece]] = []
serialization_file: str = ""
plugin_groups = ["masterpiece"]
_plugmaster: PlugMaster
def __init__(self, name: str, payload: Optional[MasterPiece] = None) -> None:
"""Instantiates and initializes. By default, the application log
filename is set to the same as the application name.
Args:
name (str): The name of the application, determining the default log filename.
payload (MasterPiece): Playload object associated with this object.
"""
super().__init__(name, payload)
@classmethod
def load_class_attributes(cls) -> None:
MasterPiece.parse_initial_args()
for name, ctor in MasterPiece.get_registered_classes().items():
ctor.load_from_json()
[docs]
@classmethod
def register_plugin_group(cls, name: str) -> None:
"""Registers a new plugin group within the application. Only plugins that match
the registered groups will be loaded. By default, all 'masterpiece' plugins
are included. Frameworks built on the MasterPiece framework can define
framework-specific group names, enabling plugins to be developed for any
application built on those frameworks. Additionally, individual applications
can introduce application-specific groups (typically named after the application),
allowing plugins to be developed exclusively for that specific application.
Args:
name (str): The name of the plugin group to be registered
"""
if not name in cls.plugin_groups:
cls.plugin_groups.append(name)
[docs]
@classmethod
def load_plugins(cls) -> None:
"""Loads and initializes all plugins for instantiation. This method
corresponds to importing Python modules with import clauses."""
for g in cls.plugin_groups:
cls._plugmaster.load(g)
[docs]
def instantiate_plugin_by_name(self, name: str) -> Union[MasterPiece, None]:
"""Installs the plugin by name, that is, instantiates the plugin class
and inserts the instance as child to the application.
Args:
name (str): name of the plugin class
"""
return self._plugmaster.instantiate_class_by_name(self, name)
[docs]
def install_plugins(self) -> None:
"""Installs plugins into the application by invoking the `install()` method
of each loaded plugin module.
**Note:** This method is intended for testing and debugging purposes only.
In a typical use case, the application should handle the instantiation of classes and
manage their attributes as needed.
"""
self._plugmaster.install(self)
[docs]
def deserialize(self) -> None:
"""Deserialize instances from the startup file specified by 'serialization_file' class attribute, or
'--file' startup argument.
TODO: or by optional method parameter
"""
if self.serialization_file != "":
self.info(f"Loading masterpieces from {self.serialization_file}")
with open(self.serialization_file, "r", encoding="utf-8") as f:
self.deserialize_from_json(f)
self.info(f"File {self.serialization_file} successfully loaded")
[docs]
def serialize(self) -> None:
"""Serialize application state to the file specified by 'serialization_file' class attribute'.
TODO: or by optional method parameter
"""
if self.serialization_file != "":
self.info(f"Saving masterpieces to {self.serialization_file}")
with open(self.serialization_file, "w", encoding="utf-8") as f:
self.serialize_to_json(f)
self.info(f"File {self.serialization_file} successfully written")
[docs]
@classmethod
def register_args(cls, parser: argparse.ArgumentParser):
"""Register startup arguments to be parsed.
Args:
parser (argparse.ArgumentParser): parser to add the startup arguments.
"""
parser.add_argument(
"-f",
"--file",
help="Specify the file to load or save application state.",
)
@classmethod
def configure_args(cls, args) -> None:
if args.file is not None:
cls.serialization_file = args.file
@classmethod
def register(cls):
cls._plugmaster = PlugMaster(MasterPiece.get_app_name())