yarp API

Value type

At the core of the yarp API is the Value type. This type is defined below.

yarp.NoValue = NoValue

A special value indicating that a yarp value has not been assigned a value.

class yarp.Value(initial_value=NoValue)[source]

A continuous or instantaneous value which can be read and set.

This base class defines the fundamental type in yarp: the ‘value’.

The actual data contained by this object should be regarded as immutable with changes being made by replacing the Python object with a new one to affect changes.

value

A property holding the current continuous value held by this object. If not yet set, or if this object represents only instantaneous values, this will be NoValue.

Setting this property sets the (continuous) contents of this value (raising the on_value_changed() callback afterwards).

To set the instantaneous value, see set_instantaneous_value().

To change the value without raising a callback, set the _value attribute directly. This may be useful if you wish to make this Value mimic another by, in a callback function, setting _value in this Value directly from the other Value’s value and calling set_instantaneous_value() with the passed variable explicitly. You must always be sure to call set_instantaneous_value() after changing _value.

set_instantaneous_value(new_value)[source]

Set the instantaneous value of this Value, calling the on_value_changed callbacks with the passed value but not storing it in the value property (which will remain unchanged).

on_value_changed(cb)[source]

Registers callback as a callback function to be called when this value changes.

The callback function will be called with a single argument: the value now held by this object. If the value is continuous, the value given as the argument will match the Value.value property. Otherwise, if this value is instantaneous, the value will not be reflected in the Value.value property.

Note

There is no way to remove callbacks. For the moment this is an intentional restriction: if this causes you difficulties this is a good sign what you’re doing is ‘serious’ enough that yarp is not for you.

This function returns the callback passed to it making it possible to use it as a decorator if desired.

Aggregate Values

The yarp API provides a limited set of convenience functions which which turn certain native Python data structures into Values which update whenever the underlying Values do.

yarp.value_list(list_of_values)[source]

Returns a Value consisting of a fixed list of other Values. The returned Value will change whenever one of its members does.

Parameters:
list_of_values: [:py:class:`Value`, …]

A fixed list of Values. The value of this object will be an array of the underlying values. Callbacks will be raised whenever a value in the list changes.

It is not possible to modify the list or set the contained values directly from this object.

For instantaneous list members, the instantaneous value will be present in the version of this list passed to registered callbacks but otherwise not retained. (Typically the instantaneous values will be represented by NoValue in value or in callbacks resulting from other Values changing.

yarp.value_tuple(tuple_of_values)[source]

A Value consisting of a tuple of other Values.

Parameters:
tuple_of_values: (:py:class:`Value`, …)

A fixed tuple of Values. The value of this object will be a tuple of the underlying values. Callbacks will be raised whenever a value in the tuple changes.

It is not possible to modify the tuple or set the contained values directly from this object.

For instantaneous tuple members, the instantaneous value will be present in the version of this tuple passed to registered callbacks but otherwise not retained. (Typically the instantaneous values will be represented by NoValue in value or in callbacks resulting from other Values changing.

yarp.value_dict(dict_of_values)[source]

A Value consisting of a dictionary where the values (but not keys) are Values.

Parameters:
dict_of_values: {key: :py:class:`Value`, …}

A fixed dictionary of Values. The value of this object will be a dictionary of the underlying values. Callbacks will be raised whenever a value in the dictionary changes.

It is not possible to modify the set of keys in the dictionary nor directly change the values of its elements from this object.

For instantaneous dictionary members, the instantaneous value will be present in the version of this dict passed to registered callbacks but otherwise not retained. (Typically the instantaneous values will be represented by NoValue in value or in callbacks resulting from other Values changing.

Value casting

The following low-level funcitons are provided for creating and casting Value objects.

yarp.ensure_value(value)[source]

Ensure a variable is a Value object, wrapping it accordingly if not.

  • If already a Value, returns unmodified.
  • If a list, tuple or dict, applies ensure_value() to all contained values and returns a value_list, value_tuple or value_dict respectively.
  • If any other type, wraps the variable in a continous Value with the initial value set to the defined value.
yarp.make_instantaneous(source_value)[source]

Make a persistent :py:class`Value` into an instantaneous one which ‘fires’ whenever the persistant value is changed.

yarp.make_persistent(source_value, initial_value=NoValue)[source]

Make an instantaneous Value into a persistant one, keeping the old value between changes. Initially sets the Value to initial_value.

Value Operators

The Value class also supports many (but not all) of the native Python operations, producing corresponding (continuous) Value objects as results. These operations support the mixing of Value objects and other suitable Python objects. The following operators are supported:

  • Arithmetic
    • a + b
    • a - b
    • a * b
    • a @ b
    • a / b
    • a // b
    • a % b
    • divmod(a, b)
    • a ** b
  • Bit-wise
    • a << b
    • a >> b
    • a & b
    • a | b
    • a ^ b
  • Unary
    • -a
    • +a
    • abs(a)
    • ~a
  • Comparison
    • a < b
    • a <= b
    • a == b
    • a != b
    • a >= b
    • a > b
  • Container operators
    • a[key]
  • Numerical conversions
    • complex(a)
    • int(a)
    • float(a)
    • round(a)
  • Python object/function usage
    • a(...) will call the value as a function and return a Value containing the result. This value will be updated by re-calling the function whenever the Value changes. Like fn(), arguments may be Value objects and these will be unwrapped before the function is called and will also cause the function to be re-evaluated whenever they change. Do not use this to call functions with side effects.
    • a.name equivalent to yarp.getattr(a, "name")

Unfortunately this list doesn’t include boolean operators (i.e. not, and, or and bool). This is due to a limitation of the Python data model which means that bool may only return an actual boolean value, not some other type of object. As a workaround you can substitute:

  • bool(a) for a == True (works in most cases)
  • a and b for a & b (works for boolean values but produces numbers)
  • a or b for a | b (works for boolean values but produces numbers)

For a similar reasons, the len and in operators are also not supported.

This list also doesn’t include mutating operators, for example a[key] = b. This is because the Python objects within a Value are treated as being immutable.

Finally, to reiterate, the result of these operators will always be continuous Values. For instantaneous versions of these operators, see the Python builtins section below.

Python builtins

The yarp API provides Value-compatible versions of a number of Python builtins and functions from the standard library:

  • Builtins
    • bool(a)
    • any(a)
    • all(a)
    • min(a)
    • max(a)
    • sum(a)
    • map(a)
    • sorted(a)
    • str(a)
    • repr(a)
    • str_format(a, ...) (equivalent to a.format(...))
    • oct(a)
    • hex(a)
    • zip(a)
    • len(a)
    • getattr(object, name[, default])
  • Most non-mutating, non-underscore prefixed functions from the operator module.

These wrappers produce continuous Values. Corresponding versions prefixed with instantaneous_ are provided which produce instantaneous Values.

Function wrappers

The primary mode of interaction with yarp Values is intended to be via simple Python functions wrapped with fn() or instantaneous_fn(). These wrappers are defined below.

yarp.fn(f)[source]

Decorator. Wraps a function so that it may be called with Value objects and itself return a persistent Value.

Say a function is defined and wrapped with fn() like so:

>>> @yarp.fn
... def add(a, b):
...     return a + b

The function can now be called with Value objects like so:

>>> a = yarp.Value(1)
>>> b = yarp.Value(2)
>>> c = add(a, b)

The returned value will itself be a Value object which will be updated whenever any of the arguments change.

>>> c.value
3

The wrapped function doesn’t need to know anything about Value objects: the wrapper unpacks the Values of each argument before passing it on and automatically wrapps the return value in a Value. (Non-Value arguments passed to the function are automatically passed through without modification).

The wrapped function is called once immediately when it is called and then again as required when its arguments change. The output Value will be persistent.

See also: instantaneous_fn().

yarp.instantaneous_fn(f)[source]

Decorator. Like fn() but the function output will be wrapped as an instantaneous Value.

The only other difference is that the function will not be called immediately and instead will only be called later when its inputs change.

General Value manipulation

The following utility functions are defined which accept and return Values.

yarp.replace_novalue(source_value, replacement_if_novalue=None)[source]

If the source_value is NoValue, return replacement_if_novalue instead.

Parameters:
source_value : Value

An instantaneous or continuous Value.

replacement_if_novalue : Python object or Value

Replacement value to use if source_value has the value NoValue.

Returns:
A continuous :py:class:`Value` which will be a copy of ``source_value`` if
``source_value`` is not :py:data:`NoValue`, otherwise the value of
``replacement_if_novalue`` is used instead.
yarp.window(source_value, num_values)[source]

Produce a moving window over a Value’s historical values.

This function treats the Value it is passed as a persistent Value, even if it is instantaneous (since a window function doesn’t really have any meaning for a instantaneous values).

The num_values argument may be a (persistent) Value or a constant indicating the number of entries in the window. If this value later reduced, the contents of the window will be truncated immediately. If it is increaesd, any previously dropped values will not return. num_values is always assumed to be an integer greater than zero and never NoValue.

yarp.no_repeat(source_value)[source]

Don’t pass on change callbacks if the Value hasn’t changed.

Works for both continuous and instantaneous Values.

yarp.filter(source_value, rule=NoValue)[source]

Filter change events.

The filter rule should be a function which takes the new value as an argument and returns a boolean indicating if the value should be passed on or not.

If the source value is persistent, the persistent value will remain unchanged when a value change is not passed on.

If the filter rule is None, non-truthy values and NoValue will be filtered out. If the filter rule is NoValue (the default) only NoValue will be filtered out.

Temporal Value manipulation

The following utility functions are defined which accept and return Values but may delay or filter changes. These all use asyncio internally and require that a asyncio.BaseEventLoop be running.

yarp.delay(source_value, delay_seconds, loop=None)[source]

Produce a time-delayed version of a Value.

Supports both instantaneous and continous Values. For continuous Values, the initial value is set immediately.

The delay_seconds argument may be a constant or a Value giving the number of seconds to delay value changes. If it is increased, previously delayed values will be delayed further. If it is decreased, values which should already have been output will be output rapidly one after another.

The loop argument should be an asyncio.BaseEventLoop in which the delays will be scheduled. If None, the default loop is used.

yarp.time_window(source_value, duration, loop=None)[source]

Produce a moving window over a Value’s historical values within a given time period.

This function treats the Value it is passed as a persistent Value, even if it is instantaneous (since a window function doesn’t really have any meaning for an instantaneous value).

The duration may be a constant or a (persistent) Value giving the window duration as a number of seconds. The duration should be a number of seconds greater than zero and never be NoValue. If the value is reduced, previously inserted values will be expired earlier, possibly immediately if they should already have expired. If the value is increased, previously inserted values will have an increased timeout.

The loop argument should be an asyncio.BaseEventLoop in which windowing will be scheduled. If None, the default loop is used.

yarp.rate_limit(source_value, min_interval=0.1, loop=None)[source]

Prevent changes occurring above a particular rate, dropping or postponing changes if necessary.

The min_interval argument may be a constant or a Value. If this value is decreased, currently delayed values will be output early (or immediately if the value would have been output previously). If increased, the current delay will be increased.

The loop argument should be an asyncio.BaseEventLoop in which the delays will be scheduled. If None, the default loop is used.

File-backed Values

The following function can be used to make very persistent Values

yarp.file_backed_value(filename, initial_value=NoValue)[source]

A persistent, file-backed value.

Upon creation, the value will be loaded from the specified filename. Whenever the value is changed it will be rewritten to disk. Changes made to the file while your program is running will be ignored.

If the file does not exist, it will be created and the value set to the value given by initial_value.

The value must be pickleable.

Time Values

The following function can be used to get the (continously changing) date and time:

yarp.now(interval=1.0, tz=None, loop=None)[source]

Returns a continuous Value containing a datetime.datetime object holding the current time, refreshed every interval seconds.

The interval argument may be a constant or a Value giving the number of seconds to wait between updates. If the Value changes, the time until the next update will be reset starting from that moment in time.

The tz argument is passed on to datetime.datetime.now(). This must be a constant.

The loop argument should be an asyncio.BaseEventLoop in which the delays will be scheduled. If None, the default loop is used.