Masterpiece Documentation

This module exposes the core concrete classes of the framework, which are intended to be instantiated and used by application developers.

The core package consists of two types of classes:

  1. Abstract Base Classes (ABCs): These define the interfaces that framework implementors should

    follow when creating subclasses. They provide method contracts that must be implemented by concrete subclasses. These classes are not intended to be instantiated directly.

  2. Concrete Classes: These are the classes that can be instantiated and used directly by application

    developers. They represent fully implemented functionality and are ready for use in applications.

This structure ensures clear separation between the framework’s core functionality and the classes that can be used to build applications on top of the framework.

class masterpiece.Application(name, payload=None)[source]

Masterpiece application class. Implements startup argument parsing, plugin management and initialization of class attributes through class specific configuration files.

deserialize()[source]

Deserialize instances from the startup file specified by ‘serialization_file’ class attribute, or ‘–file’ startup argument.

Return type:

None

classmethod get_app_id()[source]

Fetch the application id. Application id determines the folder in which the configuration files for classes are held. Note that a single package can ship with more than just one executable application, all with the same application id.

..todo: Application id ‘_app_id’ is prefixed with ‘_’ to signal that it is a private attribute (python) and that should not be serialized (masterpiece). Isn’t there something like @transient in Python? App id needs to be accessed outside, which is why this get_app_id() method is needed.

Returns:

application id determign application registry for class attribute serialization

Return type:

str

classmethod get_argmaestro()[source]

Fetch the plugmaster object reponsible for plugin management.

Returns:

object

Return type:

PlugMaster

classmethod get_configuration_filename(name)[source]

Generate the user specific file name of the configuration file based on the class name.

Parameters:

name (str) – object managing plugins

Return type:

str

classmethod get_plugmaster()[source]

Fetch the plugmaster object reponsible for plugin management.

Returns:

object

Return type:

PlugMaster

classmethod init_app_id(app_id='myapp')[source]

Initialize application id. Parses initial startup that depend on application id. Must be called before any classes are instanced.

Parameters:
  • -a (str) – Application ID.

  • --app (str) – Application ID.

  • -c (str) – Configuration name, empty string for no configuration

  • --config (str) – Configuration name, empty string for no configuration

  • -i (bool) – Whether to create class configuration files if not already created.

  • --init (bool) – Whether to create class configuration files if not already created.

Return type:

None

install_plugins()[source]

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.

Return type:

None

instantiate_plugin_by_name(name)[source]

Installs the plugin by name, that is, instantiates the plugin class and inserts the instance as child to the application. :type name: str :param name: name of the plugin class :type name: str

Return type:

Optional[MasterPiece]

classmethod load_configuration()[source]

Load class attributes from a configuration file.

Return type:

None

classmethod load_plugins()[source]

Loads and initializes all plugins for instantiation. This method corresponds to importing Python modules with import clauses.

Return type:

None

classmethod parse_args()[source]

Register classes with ArgMaestro.

Return type:

None

print()[source]

Print the instance hierarchy of the application using TreeVisualizer

Return type:

None

classmethod register_plugin_group(name)[source]

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 and apps built on the MasterPiece framework can define more group names, enabling plugins to be developed for any those as well.

Parameters:

name (str) – The name of the plugin group to be registered

Return type:

None

classmethod save_configuration()[source]

Create class configuration file, if configuration is enabled and if the file does not exist yet. See –config startup argument.

Return type:

None

serialize()[source]

Serialize application state to the file specified by ‘serialization_file’ class attribute’.

Return type:

None

classmethod set_argmaestro(argmaestro)[source]

Set the argmaestro object reponsible for plugin management.

Parameters:

argmaestro (PlugMaster) – object managing plugins

Return type:

None

classmethod set_plugmaster(plugmaster)[source]

Set the plugmaster object reponsible for plugin management.

Parameters:

plugmaster (PlugMaster) – object managing plugins

Return type:

None

class masterpiece.Composite(name='group', payload=None, children=None, parent=None)[source]

Class implementing hierarchy. Objects of this class can consist of children.

This class can be used for grouping masterpieces into larger entities to model any real world apparatus. Hierarchical entities can be manipulated exactly the same way as the most primitive objects, e.g. copied, serialized, or manipulated via do() method:

Example:

sensors = Composite("motionsensors")
sensors.add(ShellyMotionSensor("downstairs"))
sensors.add(ShellyMotionSensor("upstairs"))

def some_action(node: MasterPiece, context : MyContext) -> bool:
    ...
    return 1 # continue traversal

# Run traversal
sensors.do(some_action, my_context)
add(h)[source]

Add new object as children. The object to be inserted must be derived from MasterPiece base class.

Parameters:

h (T) – object to be inserted.

Return type:

None

do(action, context)[source]

Recursively traverses the tree, from root to leaf, left to right direction, calling the provided action on each node.

Parameters:
  • action (Callable[[MasterPiece, Dict[str, Any]], bool]) – A callable that takes (node, context) and returns a boolean.

  • context (Dict[str, Any]) – Any context data that the action may use.

Return type:

bool

Returns:

None

from_dict(data)[source]

Recursively deserialize the group from a dictionary, including its children.

Parameters:

data (dict) – data to deserialize from.

Return type:

None

run()[source]

Dispatches first the call to all children and then to the super class. It is up to the sub classes to implement the actual functionality for this method.

Return type:

None

run_forever()[source]

Dispatches first the call to all children and then to the super class. It is up to the sub classes to implement the actual functionality for this method.

Return type:

None

shutdown()[source]

Shuts down the object. First, it dispatches the call to all child objects, then calls the superclass method to stop the associated payload object, if one exists.

Return type:

None

shutdown_children()[source]

Shuts down the children.

Return type:

None

start_children()[source]

Start all children.

Return type:

None

class masterpiece.Format(stream)[source]

Abstract base class for formats. Implements two sets of methods for serializing both class attributes and instance attributes.

abstract deserialize(obj)[source]

Load attributes from the given JSON stream into the object.

Parameters:

obj (Any) – The object to deserialize into.

Return type:

None

abstract load_configuration(clazz)[source]

Load class attributes from a strea. :type clazz: Type[MasterPiece] :param clazz: :type clazz: Type[Piece]

Return type:

None

abstract save_configuration(clazz)[source]

Save class attributes to a stream. :type clazz: Type[MasterPiece] :param clazz: :type clazz: Type[Piece]

Return type:

None

abstract serialize(obj)[source]

Serialize the object to the given JSON stream.

Parameters:

obj (Any) – The object to serialize.

Return type:

None

class masterpiece.JsonFormat(stream)[source]

The JsonFormat class provides methods for serializing and deserializing objects to and from JSON format.

Features:

  • Serializes object attributes to a JSON file or stream.

  • Deserializes object attributes from a JSON file or stream.

Usage:

To use the JsonFormat, create an instance by passing the target stream. Then, call the serialize or deserialize method with the appropriate object.

Example:

from masterpiece.core import JsonFormat, MasterPiece

# Create a JsonFormat instance with a file stream
with open("output.json", "w") as f:
    json_format = JsonFormat(f)
    json_format.serialize(piece)  # piece is the object to serialize

with open("output.json", "r") as f:
    json_format = JsonFormat(f)
    json_format.deserialize(piece)  # piece is the object to deserialize
deserialize(obj)[source]

Load attributes from the given JSON stream into the object.

Parameters:

obj (Any) – The object to deserialize into.

Return type:

None

load_configuration(clazz)[source]

Load class attributes from a JSON file. :type clazz: Type[MasterPiece] :param clazz: :type clazz: Type[Piece]

Return type:

None

save_configuration(clazz)[source]

Create class configuration file, if configuration is enabled and if the file does not exist yet. See –config startup argument. :type clazz: Type[MasterPiece] :param clazz: :type clazz: Type[Piece]

Return type:

None

serialize(obj)[source]

Serialize the object to the given JSON stream.

Parameters:

obj (Any) – The object to serialize.

Return type:

None

class masterpiece.Log(name, level=20)[source]

Default logging.Logger-based logger implementation for logging events to the MasterPiece application log and/or the console.

TODO: While logging.Logger is the de facto logging implementation in Python, many Python components have undergone repeated deprecations. As a result, we should not assume that it will remain stable. Abstracting the logger API is a simple process that takes just a few minutes. By investing this small amount of time, we can shield our application code from potential quirks and deprecations introduced by third-party libraries, ensuring greater stability and flexibility in the long run.

close()[source]

Close all handlers to free resources.

Return type:

None

classmethod parse_level(level)[source]

Map the given symbolic log level to log level value.

Parameters:

level (str) – DEBUG, WARNING, INFO, ERROR etc.

Returns:

Log.DEBUG etc.

Return type:

int

class masterpiece.MasterPiece(name='noname', payload=None, parent=None)[source]

An object with a name. Base class of everything. Serves as the foundational base class for any real-world object that can be a part of a hierarchy.

acquire_time_spent()[source]

Reads off the average time the thread has spent in its update() method since the last call, and resets the statistics for the next measurement cycle. This method can be used for monitoring healtiness of the thread.

Returns:

elapsed seconds.

Return type:

elapsed (float)

classmethod classattrs_from_dict(attributes)[source]

Set only the class’s own attributes from a dictionary.

Return type:

None

classmethod classattrs_to_dict()[source]

Convert the class’s own attributes to a dictionary, excluding inherited and private ones.

Return type:

dict[str, Any]

copy()[source]

Create and return a copy of the current object.

This method serializes the current object to a dictionary using the to_dict method, creates a new instance of the object’s class, and populates it with the serialized data using the from_dict method.

This method uses class identifier based instantiation (see factory method pattern) to create a new instance of the object, and ‘to_dict’ and ‘from_dict’ methods to initialize object’s state.

Return type:

MasterPiece

Returns:

A new instance of the object’s class with the same state as the original object.

Example:

clone_of_john = john.copy()
debug(msg, details='')[source]

Logs the given debug message to the application log.

Parameters:
  • msg (str) – The information message to be logged.

  • details (str) – Additional detailed information for the message to be logged

Return type:

None

do(action, context)[source]

Execute the given action to the object, by calling the provided action.

Parameters:
  • action (Callable[["MasterPiece", dict[str, Any]], bool]) – A callable that takes

  • (node

  • boolean. (context) and returns a)

  • context (dict[str, Any]) – Any context data that the action may use.

Return type:

bool

Returns:

The return value from the executed action.

error(msg, details='')[source]

Logs the given error message to the application log.

Parameters:
  • msg (str) – The message to be logged.

  • details (str) – Additional detailed information for the message to be logged

Return type:

None

classmethod factory()[source]

Fetch the dictionary holding class names and associated classes.

Returns:

with class names and associated classes

Return type:

factory

classmethod find_class(class_id)[source]

Create an instance of the class corresponding to the given class identifier.

Parameters:
  • class_id (str) – Identifier of the class to instantiate.

  • *args – Optional arguments to pass to the class constructor.

Returns:

An instance of the class corresponding to the given class identifier.

Return type:

MasterPiece

from_dict(data)[source]

Update instance attributes from a dictionary.

Return type:

None

classmethod get_class_id()[source]

Return the class id of the class. Each class has an unique name that can be used for instantiating the class via Object.instantiate() method.

Parameters:

cls (class) – class

Returns:

unique class identifier through which the class can be instantiated by factory method pattern.

Return type:

id (str)

classmethod has_class_method_directly(method_name)[source]

Check if the method is defined directly in the class (not inherited).

Return type:

bool

info(msg, details='')[source]

Logs the given information message to the application log.

Parameters:
  • msg (str) – The information message to be logged.

  • details (str) – Additional detailed information for the message to be logged

Return type:

None

classmethod init_class(clazz)[source]

Initialize class. Registers the class into the class factory .

Parameters:

clazz (class) – class to be initialized

Return type:

None

classmethod instantiate(class_id, *args)[source]

Create an instance of the class corresponding to the given class identifier.

Parameters:
  • class_id (str) – Identifier of the class to instantiate.

  • *args (Any) – Optional arguments to pass to the class constructor.

Returns:

An instance of the class corresponding to the given class identifier.

Return type:

MasterPiece

classmethod log_debug(msg, details='')[source]

Logs the given debug message to the application log.

Parameters:
  • msg (str) – The message to be logged.

  • details (str) – Additional detailed information for the message to be logged

Return type:

None

classmethod log_error(msg, details='')[source]

Logs the given message to the application log.

Parameters:
  • msg (str) – The message to be logged.

  • details (str) – Additional detailed information for the message to be logged

Return type:

None

classmethod log_info(msg, details='')[source]

Logs the given message to the application log.

Parameters:
  • msg (str) – The message to be logged.

  • details (str) – Additional detailed information for the message to be logged

Return type:

None

classmethod log_warning(msg, details='')[source]

Logs the given debug message to the application log.

Parameters:
  • msg (str) – The message to be logged.

  • details (str) – Additional detailed information for the message to be logged

Return type:

None

make_url()[source]

Generate the URL for the composite, including all children.

Return type:

URL

classmethod register()[source]

Register the class.

Called immediately upon class initialization, right before the class attributes are loaded from the class specific configuration files.

Subclasses can extend this with custom register functionality:

class MyMasterPiece(MasterPiece):

    @classmethod
    def register(cls):
        super().register()  # Don't forget
        cls._custom_field = True
Return type:

None

resolve_url(url)[source]

Find a MasterPiece in the hierarchy matching the URL.

Return type:

Optional[MasterPiece]

root()[source]

Fetch the root object

Returns:

root object

Return type:

MasterPiece

run()[source]

Run the masterpiece. Dispatches the call to payload object and returns the control to the caller.

Return type:

None

run_forever()[source]

Run the payload forever. This method will return only when violently terminated. If the object does not have playload object, or it is not instance of ‘MasterPiece’ class then returns immediately and this method has no effect.

Return type:

None

classmethod set_log(l)[source]

Set logger.

Parameters:

l (logger) – logger object

Return type:

None

shutdown()[source]

Shutdown the payload object. If the payload object is None, or is not instance of MasterPiece, then the call has no effect.

Return type:

None

to_dict()[source]

Convert instance attributes to a dictionary.

Return type:

dict[str, Any]

update_metrics(elapsed)[source]

Updates the number of update() methods calld, and time spent in the method. This method is called internally from the update() method.

Parameters:

elapsed (float) – elapsed seconds.

Return type:

None

warning(msg, details='')[source]

Logs the given warning message to the application log.

Parameters:
  • msg (str) – The message to be logged.

  • details (str) – Additional detailed information for the message to be logged

Return type:

None

class masterpiece.MasterPieceThread(client)[source]

Base class for threads used for tasks such as data acquisition that need to be run asynchronously. This class defines the update() method, in which subclasses can execute their specific code. The update_interval() method (default is 60 seconds) determines how frequently the update() method is called.

Parameters:

Thread (client) – MQTT client for the thread

log(type, msg, details)[source]

Log event to event log.

Parameters:
  • type (str) – one of the following: “info”, “debug”, “warning”, “error”

  • msg (str) – message to be logged

  • details (str) – detailed description

Return type:

None

publish(topic, message, qos=1, retain=True)[source]

Publish the given message to given MQTT topic with specified quality of service and retain.

Parameters:
  • topic (str) – topic

  • message (str) – message to be published

  • qos (int) – quality of service

  • retain (bool) – retain the message

Return type:

None

run()[source]

Thread loop.

Calls update() method in a loop and if the return value is True sleeps the update_interval() number of seconds before the next update call. If the update method returns False then the error is logged, and the sleep time is shortened to 5 seconds to retry. After three subsequent failures the update_interval is reset to original

Return type:

None

stop()[source]

Request the thread to stop processing further tasks.

Note that the method does not wait the thread to terminate. If the thread is sleeping, it will be awakened and stopped. If the thread is in the middle of its code execution, it will finish its current job before stopping. In oder to wait until the thread has completed its call join() method.

Return type:

None

update()[source]

Method called from the threads run loop.

Up to the sub classes to implement.

Returns:

True upon succesfull update. False implies an error .

Return type:

bool

update_interval()[source]

Fetch the update interval in seconds. The default is 60.

Returns:

number of seconds

Return type:

float

class masterpiece.PlugMaster(name)[source]

The Plugmaster class is responsible for managing and loading plugins into an application.

The Plugmaster is designed to work with plugins that are Masterpiece objects or subclasses thereof. Plugins can optionally be derived from the Plugin class.

If a plugin implements the Plugin interface, it is responsible for determining what objects should be added to the application.

If a plugin is not a Plugin class, it is simply loaded, and it is the responsibility of the application configuration file or the application code to determine how to utilize the plugin.

find_class_by_name(name)[source]

Find and return a plugin class by its name.

Return type:

Optional[Type[MasterPiece]]

Returns:

plugin class (MasterPiece) or none if not found

get()[source]

Fetch the list of plugins classes.

Returns:

List of plugins

Return type:

List[Type[MasterPiece]]

install(app)[source]

Instantiate and add all the registered plugins to the application.

Typically is up to the application or the configuration to define the instances to be added. This method is provided for testing purposes only.

Parameters:
  • app (Composite) – parent object (application), for hosting the instances

  • plugin. (created by the)

Return type:

None

instantiate_class_by_name(app, name)[source]

Instantiate and add the plugin into the application.

Parameters:
  • app (Composite) – parent object, for hosting the instances createdby the plugin.

  • name (str) – name of the plugin to be instantiated

Return type:

Optional[MasterPiece]

load(name)[source]

Fetch the entry points associated with the ‘name’, call their ‘load()’ methods and insert to the list of plugins. Note: Python’s ‘importlib.metadata’ API has been redesigned a couple of times in the past. The current implementation has been tested with Python 3.8, 3.9 and 3.12.

Parameters:

name (str) – Name determining the plugins to be loaded.

Return type:

None

class masterpiece.Plugin(name='noname', payload=None, parent=None)[source]

Abstract base class for plugins.

abstract install(app)[source]

Instantiates and installs the classes in the plugin module into the given ‘app’ target object. This is an abstract method that the plugin classes must implement. Plugins may choose not to do anything here and instead leave it up to the user, or a higher level software layer.

Parameters:

app (Composite) – application to plug into

Return type:

None

class masterpiece.TreeVisualizer(color)[source]

The TreeVisualizer class is designed to visually represent hierarchical structures using ASCII art and colors, making it easy to understand the relationships between nodes in a tree. It is particularly useful for visualizing instances of the MasterPiece and Composite classes, which represent elements in a tree structure.

Features:

  • Supports customizable colors for node representation using the Colorama library.

  • Prints the hierarchy of nodes with clear visual indicators (├─, └─, and │) to represent parent-child relationships.

  • Automatically resets colors after each print to maintain consistent output.

Usage:

To use the TreeVisualizer, first create an instance by specifying the desired color. Then, call the print_tree method with the root node of your tree.

Example:

from masterpiece.core import TreeVisualizer, MasterPiece, Composite

# Create a sample hierarchy
parent = Composite("parent")
child1 = MasterPiece("child1")
child2 = MasterPiece("child2")
parent.add(child1)
parent.add(child2)

# Initialize the visualizer with a specified color
visualizer = TreeVisualizer("green")

# Print the hierarchy
visualizer.print_tree(parent)
get_color()[source]

Return the corresponding Colorama color code for the specified color.

Return type:

Any

print_tree(node, prefix='', is_last=True)[source]

Print the hierarchy of the node using ├─, └─, and │ with the specified color.

Parameters:
  • node (MasterPiece) – The root node to print.

  • prefix (str, optional) – The prefix for the current level.

  • is_last (bool, optional) – Whether this node is the last child.

Return type:

None

class masterpiece.classproperty(func)[source]

A decorator that allows you to define class-level properties. Replaces the deprecated combination of @classmethod and @property.