Client interface

A client is any software component interacting with a daemon, in the form of issuing requests, receiving responses to requests, and receiving asynchronous updates of new item values. The Python interface described here makes liberal use of background threads, callbacks in particular can and will arrive from background threads as asynchronous updates arrive.

Unlike the Daemon interface, a typical client application will not need subclasses of the classes defined here, they are expected to be used directly, as-is.

Getting started

mKTL will cache information locally; there are no restrictions on where this information gets cached, other than it needs to be cached somewhere. The default location for this cache is:

$HOME/.mKTL

You can override this location by setting the MKTL_HOME environment variable to the absolute path of your choice; any directory specified in this fashion will be created if it does not already exist. Alternatively, the program may invoke the mktl.home() method to specify the location at run time.

mktl.home(default=None)

Return the directory location where we should be loading and/or saving configuration files. This defaults to $HOME/.mKTL, but can be overridden by calling this method with a valid path, or by setting the MKTL_HOME environment variable. Note that changes to the environment variable will be ignored unless it is set prior to the first invocation of this method.

You may need to pre-populate your local cache with information from the brokers handling the configuration for the store(s) you wish to access. The mk command line tool will handle this process; for a given IP address or hostname, you would invoke:

mk discover 192.168.5.34

This discovery process will query that address for any available configurations, but more importantly, will cache the address of that broker and use it again in the future if/when discovering new services, without an explicit request to do so. In other words, it should only be necessary to run this discovery process a single time in order to gain access to a new set of mKTL stores.

The mktl.get() method is the universal entry point to retrieve a mktl.Store or mktl.Item instance; client configuration is automatically refreshed if necessary, and the remainder of the connection logic is handled by the mktl.Store.

All other client operations, such as getting and setting item values, are handled via the mktl.Item instance.

mktl.get(store, key=None)

The get() method is intended to be the primary entry point for all interactions with a key/value store.

The return value is either a cached Store or Item instance. If both a store and a key are specified, the requested item will be returned from the specified store. The same will occur if the sole argument is a store and key name concatenated with a dot (store.KEY). A Store instance will be returned if the sole argument provided is a store name. Both the store and the key are case-insensitive, all arguments are translated to lower case before proceeding.

If the caller always uses get() to retrieve a Store or Item they will always receive the same instance of that class. In that sense, get() is a factory method enforcing a singleton pattern.

The Store class

The mktl.Store class is primarily an organizational structure, providing a dictionary-like interface to retrieve mktl.Item instances.

class mktl.Store(name)

The Store implements a key/value store, effectively a Python dictionary with some additional context. A store has a unique name within the local mKTL context; which daemons will be contacted to handle further requests is determined on a per-Item basis, re-use of connections is managed in the mktl.protocol submodule, not here.

keys()

Return a sequence of all keys in this store.

values()

Return a sequence of all mktl.Item instances in this store.

The Item class

The bulk of the client interactions will occur with the mktl.Item class. In addition to the methods described here an mktl.Item instance can be used with Python operators, such as addition/concatenation or multiplication. The behavior of an mktl.Item when used in this fashion will be aligned with the native Python binary type for the item value; for example, if an item test.BAR has an integer value 12, test.BAR + 5 will return the integer value 17. If test.BAR is instead the string value ‘12’, the same operation would raise a TypeError exception; however, test.BAR + '5' would return the string value ‘125’, just like you would expect for string concatenation.

class mktl.Item(store, key, subscribe=True, authoritative=False, pub=None)

An Item represents a key/value pair, where the key is the name of the Item, and the value is whatever is provided by the authoritative daemon. The principal way for both clients and daemons to get or set the value is via the value() property.

A non-authoritative Item will automatically subscribe() itself to any available updates.

property formatted

Get and set the human-readable representation of the item. For example, the formatted variant of an enumerated type is the string string representation, as opposed to the integer reported as the current item value. These permutations are driven by the JSON configuration of the item. The current value will be returned as a string in the absence of any configured formatting.

This property can also be used to set the new value of the item using the formatted representation.

See also quantity and value.

get(refresh=False, formatted=False, quantity=False)

Retrieve the current value. Set refresh to True to prompt the daemon responding to the request to return the most up-to-date value available, potentially bypassing any local cache. Set formatted to True to receive the human-readable formatting of the value, if any such formatting is available; similarly, set quantity to true to receive the value as a pint.Quantity instance, which will only work if the item is configured to have physical units.

Use of the formatted, quantity, and value properties is encouraged in the case where a synchronous refresh is not required.

property quantity

Get and set the current value of the item as a pint.Quantity instance.

This property can also be used to set the new value of the item using a valid pint.Quantity instance; the provided quantity will be translated to the base units of the item before proceeding.

See also formatted and value.

register(method, prime=False)

Register a callback to be invoked whenever a new value is received from a remote daemon, regardless of how that new value arrived. subscribe() will automatically be invoked if it has not been invoked for this Item instance, the client does not need to call it separately. If prime is set to True the callback will be invoked using the current value of the item, if any, before returning; no priming call will occur if the item has no value.

set(new_value, wait=True, formatted=False, quantity=False)

Set a new value. Set wait to True to block until the request completes; this is the default behavior. If wait is set to False, the caller will be returned a mktl.protocol.message.Request instance, which has a mktl.protocol.message.Request.wait() method that can optionally be invoked to block until completion of the request; the wait will return immediately once the request is satisfied. There is no return value for a blocking request; failed requests will raise exceptions.

The optional formatted and quantity options enable calling set() with either the string-formatted representation or the pint.Quantity representation of the item; the new value is still the first argument, but set one of formatted or quantity to True to indicate it should be interpreted.

Use of the formatted, quantity, and value properties is encouraged in the case where a blocking set operation is desired.

subscribe(prime=True)

Subscribe to all future broadcast events. Doing so ensures that locally cached values will always be current, regardless of any local polling behavior. If prime is True a call will be made to get() to refresh the locally cached value before this method returns. A failure on a priming call will be caught and ignored, it will not be reported to the caller.

A non-authoritative Item will automatically call this method` upon being instantiated; an authoritative variant will do so upon a call to register(). In other words, it should never be necessary to call this method directly.

property value

Get and set the current value of the item. The caller should use get() and set() directly for additional control over how these respective calls are handled, the handling invoked here relies on default values for all optional arguments.

See also formatted and quantity.