Daemon interface

A daemon is the source of authority for one or more mKTL items; when a client gets or sets an item value, that request is handled by the daemon. Exactly how that request gets handled is entirely up to the daemon code; there are no restrictions imposed by the mKTL protocol itself, it is up to the daemon to resolve any consistency issues or conflicting requests.

The default behavior of the mktl.Store is to act as a caching key/value store: values come in, values go out, they may or may not persist across restarts depending on the configuration. This behavior is rarely sufficient for an application; a typical daemon will implement custom code behind the getting and setting of individual items. The Python interface described here expects the user to create custom subclasses of mktl.Daemon, which in turn may instantiate custom subclasses of mktl.Item. Background threads are used widely, polling and callbacks in particular are two areas where dedicated per-item background threads will invoke any/all registered methods; the author of any custom code should not assume that calls arriving via these mechanisms will be serialized or thread-safe to any meaningful degree.

The Daemon class

The mktl.Daemon class is the entry point and overall organization for the daemon; while it provides a common storage location for daemon-wide functions, such as handling requests and publishing broadcasts, from the perspective of the developer the mktl.Store is a container for the mktl.Item instances that define the actual behavior of the system. An incoming set request, for example, is not handled by the any higher level construct, it is passed to the appropriate mktl.Item instance for proper handling.

The mktl.Daemon also defines the initialization sequence, if any, for the daemon: which steps to take upon startup, which mktl.Item subclasses to invoke for specific items, whether other initialization steps must occur such as defining a common communication point to access a hardware controller, or whether the initial state of a set of items needs to be manipulated before proceeding with routine operations.

In most cases the developer will need to create a custom mktl.Daemon subclass to satisfy the operational goals of an application. Likewise, in most cases the developer will need to create custom mktl.Item subclasses to implement application-specific behavior, with special attention paid to the specific methods to override, and additional methods useful in a daemon context.

class mktl.Daemon(store, alias, override=False, options=None)

The mKTL Daemon is a facilitator for common mKTL actions taken in a daemon context: loading a catalog file, instantiating mktl.Item instances, and commencing routine operations.

The developer is expected to subclass the Daemon class and implement a setup() method, and/or a setup_final() method. This subclass is the gateway between mKTL functionality and the domain-specific custom code appropriate for the developer’s application.

The store argument is the name of the store that this daemon is providing items for; alias is the unique name of this mKTL daemon, and is used to locate the catalog file that defines the items for which this daemon is authoritative.

options is expected to be an argparse.ArgumentParser instance, though in practice it can be any Python object with specific named attributes of interest to a Daemon subclass; the options argument is not required. This is intended to be a vehicle for custom subclasses to receive key information from command-line options, such as the location of an auxiliary configuration file containing information about a hardware controller.

add_get_handler(key, method)

Assign a method that will be called for all GET requests for the specified item. See mktl.Item.req_get() for additional details on the expected behavior; inspection of the implementation for that method is recommended to ensure all the necessary actions are covered.

add_get_performer(key, method)

Assign a method that will be called for all GET requests for the specified item. Refer to mktl.Item.add_get_performer() for additional details.

add_handler(key, request, method)

Assign a method that will be called for either GET or SET requests, determined by the request argument, which must be one of ‘get’ or ‘set’.

See mktl.Item.req_get() and mktl.Item.req_set() for additional details on the expected behavior; inspection of the implementation for those methods is recommended to ensure all the necessary actions are covered.

add_handlers(handlers)

This method is intended to be a single call, accepting a sequence of triplets mapping external methods handling GET and SET requests, bypassing the usual handling chain involving mktl.Item instances.

A triplet is the key for an item (omitting the store name), whether this is a ‘get’ or a ‘set’ handler, and a valid reference to a method. For example:

('temperature', 'get', mkwc.get_temperature)

See mktl.Item.req_get() and mktl.Item.req_set() for additional details; inspection of the implementation for those methods is recommended to ensure all the necessary actions are covered.

add_item(item_class, key, *args, **kwargs)

Add an mktl.Item to this daemon instance; this is the entry point for establishing an authoritative item, one that will handle inbound get/set request and the like. The args and kwargs will be passed directly to the item_class when it is called to be instantiated; note that a default mktl.Item takes no additional arguments, this is only meaningful for custom subclasses.

add_performer(key, request, method)

Assign a method that will be called for either GET or SET requests, determined by the request argument, which must be one of ‘get’ or ‘set’.

See mktl.Item.add_get_performer() and mktl.Item.add_set_performer() for additional details.

add_performers(performers)

This method is intended to be a single call, accepting a sequence of triplets mapping external methods performing GET and SET requests onto the mktl.Item instances receiving those requests from this daemon.

A triplet is the key for an item (omitting the store name), whether this is a ‘get’ or a ‘set’ performer, and a valid reference to a method. For example:

('temperature', 'get', mkwc.get_temperature)

See mktl.Item.add_get_performer() and mktl.Item.add_set_performer() for additional details.

add_set_handler(key, method)

Assign a method that will be called for all SET requests for the specified item. See mktl.Item.req_set() for additional details on the expected behavior; inspection of the implementation for that method is recommended to ensure all the necessary actions are covered.

add_set_performer(key, method)

Assign a method that will be called for all SET requests for the specified item. Refer to mktl.Item.add_set_performer() for additional details.

cleanup(*args, **kwargs)

Subclasses should override the cleanup() method to perform any/all actions prior to shutting down the daemon, such as publishing a “shutting down” message, or cleanly terminating a connection with a hardware device. This method is guaranteed to only be invoked a single time. The default implementation of this method takes no actions.

describe_items()

Subclasses should override the describe_items() method to supplement the daemon’s authoritative catalog block with any items whose description is generated at run time, as opposed to loaded from a file.

The sole return argument is a dictionary, keyed by mKTL item keys, and the value is another dictionary containing the fields of the item description.

For example:

items = dict()

items['watermelons'] = dict()
items['watermelons']['description'] = 'Quantity of watermelons.'
items['watermelons']['units'] = 'melons'
items['watermelons']['persist'] = True
items['watermelons']['type'] = 'numeric'
items['watermelons']['format'] = "%d"

return items

The default implementation of this method takes no actions.

parse_options(options)

Subclasses should override the parse_options() method to interpret the options attribute, which is a argparse.ArgumentParser instance, and is passed as an argument to this method for convenience. This method is called early in the initialization of the Daemon, allowing the subclass to establish its local configuration prior to describing or instantiating any items.

For example, the options.appconfig attribute may indicate the location of a configparser-formatted configuration file. A subclass would use this method to read the file, parse its contents, and establish local variables for subsequent use by other methods.

The default implementation of this method takes no actions.

setup()

Subclasses should override the setup() method to invoke add_item() for any custom mktl.Item subclasses or otherwise execute custom code. When setup() is called the bulk of the Daemon machinery is in place, but cached values have not been loaded, nor has the presence of this daemon been announced. The default implementation of this method takes no actions.

setup_final()

Subclasses should override the setup_final() method to execute any/all code that should occur after all mktl.Item instances have been created, including any non-custom mktl.Item instances, but before this daemon announces its availability on the local network. The default implementation of this method takes no actions.

stop()

Request that this daemon stop execution.

Store metadata

Largely for internal use, there are some aspects of the mktl.meta.Catalog class that are directly relevant for use within a daemon. A reference to the appropriate Catalog instance can be accesssed as an attribute of the mktl.Store, or via the mktl.meta.catalog() method; mktl.meta.Catalog instances are singletons.

class mktl.meta.Catalog(store, alias=None)

A convenience class to represent the JSON description of an mKTL store. To first order this acts like a dictionary, returning the description for a single key, or the full catalog block for a single UUID or unique alias.

In addition to acting as a repository for the description of all items, the Catalog instance also provides translation routines for some item values; the behavior of these translations is fully driven by the item description, and does not depend on custom mktl.Item subclasses.

convert_units(value, old, new)

Use the pint module to convert the provided value from old units to new units. The value as a pint.Quantity instance will be returned if new is set to None.

from_format(key, value)

Translate the provided value according to the description of the item identified by the supplied key. For example, if the item is enumerated, this method will enable one-way mapping from string values to integers; for example, ‘Off’ to 0, ‘On’ to 1, etc.

This is the inverse of to_format().

from_quantity(key, quantity)

Translate the provided pint.Quantity instance to the unformatted representation appropriate for the item identified by the supplied key. This is only relevant for numeric types that have defined units; a TypeError exception will be raised for items that do not have units.

keys(authoritative=False)

Return an iterable sequence of keys for the items represented in this catalog. If authoritative is set to True, only return the keys for locally authoritative items; any keys with a leading underscore (built-in items) will be omitted from the reported authoritative set.

to_format(key, value)

Translate the provided value according to the description of the item identified by the supplied key. For example, if the item is enumerated, this method will enable one-way mapping from integer values to representative strings; for example, 0 to ‘Off’, 1 to ‘On’, etc.

This is the inverse of from_format().

to_quantity(key, value, units=None)

Translate the provided value according to the description of the item identified by the supplied key to a pint.Quantity instance. This is only relevant for numeric types that have defined units; a TypeError exception will be raised for items that do not have units. The returned quantity will be translated to the specified units, if any, otherwise the default ‘unformatted’ units are used.

update(block, save=True)

Update the locally cached catalog to include any/all contents in the provided block. A catalog block is a Python dictionary in the on-disk client format, minimally including the keys ‘store’, ‘alias’, ‘uuid’, and ‘items’.

uuids(authoritative=False)

Return an iterable sequence of UUIDs represented in this catalog. If authoritative is set to True, only return the authoritative UUID.

In addition to the class itself there are an assortment of helper methods, two of which are potentially relevant in a daemon context:

mktl.meta.authoritative(store, alias, items)

Declare an authoritative catalog block for use by a local authoritative daemon. This is the expected entry point for a daemon that generates or otherwise provides its own JSON catalog via custom routines.

mktl.meta.catalog(store, alias=None)

Retrieve the locally cached Catalog instance for the specified store. A KeyError exception is raised if there are no locally cached catalog blocks for that store. A typical client will only interact with mktl.get(), which in turn calls this method.

mkd executable

The mkd executable provides a command-line interface to invoke a persistent daemon executing a mktl.Daemon subclass as described above. It is the natural starting point for any persistent daemon implementing the scheme described in this document, since that is the precise purpose it is written for.

Refer to the section covering mkd in the Executables documentation for additional details.