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 theMKTL_HOMEenvironment 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
StoreorIteminstance. 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). AStoreinstance 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 aStoreorItemthey 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
Storeimplements 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 themktl.protocolsubmodule, not here.- keys()
Return a sequence of all keys 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.
- 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.Quantityinstance, which will only work if the item is configured to have physical units.Use of the
formatted,quantity, andvalueproperties 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.Quantityinstance.This property can also be used to set the new value of the item using a valid
pint.Quantityinstance; the provided quantity will be translated to the base units of the item before proceeding.
- 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 thisIteminstance, 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.Requestinstance, which has amktl.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 thepint.Quantityrepresentation 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, andvalueproperties 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
Itemwill automatically call this method` upon being instantiated; an authoritative variant will do so upon a call toregister(). In other words, it should never be necessary to call this method directly.