The Item class

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.

Variables:
  • key – The key (name) for this item.

  • full_key – The store and key for this item, in store.key format.

  • store – The mktl.Store instance containing this item.

  • config – The JSON description of this item.

  • log_on_set – Indicates whether this item will log SET requests. The default is True.

  • publish_on_set – Indicates whether this item will publish a new value whenever perform_set() is successfully invoked. The default is True.

The bulk of mKTL interactions involve 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. In-place operators will set the current value of the item.

Three properties allow unified access to getting and setting an item’s value, regardless of whether the local application is authoritative for that specific item. The use of properties implies default behavior for both get() and set() in a client context, and for publish() in a daemon context. The use of these properties is encouraged as the preferred approach to getting and setting item values; any functionality beyond the default behavior requires using the methods 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.

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. In the absence of any configured formatting the current value will be returned as a string.

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

See also quantity and value.

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.

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.

set(new_value, wait=True, reply=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. Set reply to False to disable all error handling and acknowledgements for the request (fire and forget); setting reply to False implies *wait is also False. 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.

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.

A callback method should expect three arguments: a reference to the Item from whence the callback originated, the new value for the item, and the timestamp associated with the new value. For example:

def my_callback(item, new_value, new_timestamp):

Callback methods should strive to be lightweight in terms of their execution time; any callbacks registered for an item will be called in series, and there are no provisions for shortening the queue if a backlog occurs.

All of the client functionality remains unchanged when an Item is used in a daemon context. The following methods have utility when a daemon is authoritative for an item.

from_payload(payload)

Recreate the fundamental Python type of the value from the provided mktl.protocol.message.Payload instance, and return it to the caller.

This default handler will interpret the bulk component, if any, of the payload as an N-dimensional numpy array, with the description of the array present in the other fields of the payload.

This is the inverse of to_payload().

poll(period)

Poll for a new value every period seconds. Polling will be discontinued if period is set to None or zero. Polling will invoke req_poll(), and occurs at the requested interval within a background thread unique to this item.

publish(new_value, timestamp=None, repeat=False)

Publish a new value, which is expected to be the Python native representation of the new value. If timestamp is set it is expected to be a UNIX epoch timestamp; the current time will be used if it is not provided. Newly published values are always cached locally. If repeat is set to True the value will be published regardless of whether it is a repeat of the previously published value.

Note that, for simple cases, an authoritative daemon can set the value property to publish a new value instead of calling publish() directly. In other words, these two calls are equivalent:

item.value = new_value
item.publish(new_value)
to_payload(value=None, timestamp=None, **kwargs)

Interpret the provided arguments into a mktl.protocol.message.Payload instance; if the value is not specified the current value of this Item will be used; if the timestamp is not specified the current time will be used. This is particularly important as a step in a custom perform_get() implementation.

This is the inverse of from_payload().

watch(item)

Register a callback with the referenced item, such that the current item updates itself any time the referenced item changes. This is functionally equivalent to calling:

item.register(self.req_poll)

Some of the daemon-specific methods are intended to be overridden as part of implementing custom, application-specific logic. It may not be necessary to override all of these methods depending on the needs of the application.

perform_get()

Acquire the most up-to-date value available for this Item and return it to the caller. The most common return value is any Python object that can be serialized as JSON; return None if no new value is available.

If a mktl.protocol.message.Payload instance is returned it will be used as-is, otherwise the return value will be passed to to_payload() for encapsulation. The primary motivation for returning a Payload instead of a bare value is if the embedded timestamp needs to be set to some value other than the current time.

Returning None will not clear the currently known value, that will only occur if the returned Payload instance is assigned None as the ‘value’; this is not expected to be a common occurrence, but if a custom perform_get() implementation wants that to occur they need to instantiate and return the Payload instance directly rather than use to_payload().

perform_set(new_value)

Implement any custom behavior that should occur as a result of a set request for this item. No return value is expected. Any subclass implementations should raise an exception in order to trigger an error response.

Any return value, though again none is expected, will be encapsulated via to_payload(), after the same fashion as perform_get(), and included in the response to the original request.

validate(value)

A hook for a daemon to validate a new value. The default behavior is a no-op; any checks should raise exceptions if they encounter a problem with the incoming value. The ‘validated’ value must be returned by this method; this allows for the possibility that the incoming value has been translated to a more acceptable format, for example, converting the string ‘123’ to the integer 123 for a numeric item type.

The req_*() set of methods can be overridden, but for nearly all applications their use is strictly internal and further customization should not be necessary.

req_get(request)

Handle a GET request. The request argument is a protocol.message.Request instance; the value returned from req_get() is identical to the value returned by perform_get(), which is where custom handling by subclasses is expected to occur.

req_poll(repeat=False)

Handle a background poll request, established by calling poll(). perform_get() is where custom handling by subclasses is expected to occur. The payload returned from req_poll() is identical to the payload returned by perform_get().

A common pattern for custom subclasses involves registering req_poll() as a callback on other items, so that the value of this item can be refreshed when external events occur. The watch() method exists to facilitate that pattern.

req_set(request)

Handle a SET request. The request argument is a protocol.message.Request instance; the value returned from req_set() will be returned to the caller, though no such return value is expected. Any calls to req_set() are expected to block until completion. Custom handling by subclasses is expected to occur in perform_set().

If the publish_on_set attribute is set to True (this is the default) a call to publish() will occur at the tail end of any successful SET request. Custom subclasses can set this attribute to False to inhibit that behavior.