5. Agent-oriented programming — Turberfield utilities guide 0.34.0

# Turberfield utilities guide 0.34.0

## 5. Agent-oriented programming

A Turberfield application is made up of cohesive programs called Controllers. Each controller contains specialised objects which run on their own as autonomous agents.

When an agent has to share the data it creates, it’s known as an Expert. In Turberfield, experts:

> - can be configured with global settings like file paths and host names
> - listen on a queue for messages from another expert
> - listen on a queue for messages from another controller
> - operate autonomously within an event loop
> - publish certain attributes to other experts within the same controller
> - publish certain attributes to other controllers via RSON

Turberfield provides the `expert` module to standardise this pattern. Subclasses of `Expert` inherit these behaviours.

## 5.1. Using an Expert subclass

Turberfield defines a four-stage process for setting up Expert objects, running them, and checking their results. There are conventions for each of these interactions:

- Configuration, where the class helps you build options for your new object.
- Instantiation, where you connect the object to its inputs.
- Invocation, which is compatible with the asyncio event loop.
- Inspection, the mechanism whereby your code can see the outputs of Experts in operation.

### 5.1.1. Configuration

Suppose for example that a certain Expert subclass requires an output argument to tell it where to save application data files. Typically that would be known to the controller process as a command line argument or configuration file setting.

To configure a new object of this Expert class, you would first call the `options` method of the class:

    options = SomeExpertSubclass.options(output="/var/experts")

The return value of the `options` call is a Python dictionary compatible with the keyword argument parameters of the Expert subclass. The keys of this dictionary are the names of attributes which your object will publish publicly. The corresponding value of a key shows the way the attribute will be published, as follows:

`Expert.` `Attribute` (name). A regular public attribute.

`Expert.` `Event` (name). A public attribute which is an asyncio.Event.

`Expert.` `HATEOAS` (name, attr, dst). A sequence of items in a JSON-formatted web page, publicly readable as a local file.

`Expert.` `RSON` (name, attr, dst). A sequence of items in RSON format, publicly readable as a local file.

### 5.1.2. Instantiation

Some Experts will define positional parameters specific to their operation. Aside from those, you can also pass unnamed positional arguments and the keyword arguments you got from the configuration step.

Unnamed positional arguments must be objects compatible with the asyncio.Queue interface. The Expert will `watch` for messages you pass into them.

    wiring = (asyncio.Queue(), PipeQueue.pipequeue("/tmp/pq.fifo"))
    expert = SomeExpertSubclass(*wiring, **options)

### 5.1.3. Invocation

Experts are active objects which run in an event loop. They support Python call semantics. The result of calling an Expert is a coroutine you can pass to asyncio for use as a Task:

    loop = asyncio.get_event_loop()
    task = asyncio.Task(expert(loop=loop))
    loop.run_until_complete(asyncio.wait(asyncio.Task.all_tasks(loop)))

### 5.1.4. Inspection

Experts either publish the data they generate to a local file, or to the public attribute of their class. Exactly what goes where will depend on the class and can be discovered from the `options` call.

For example, should SomeExpertSubclass define an `Event` called someEvent, you’d use it like this:

    yield from SomeExpertSubclass.public.someEvent.wait()

## 5.2. Subclassing Expert

class `turberfield.utils.expert.` `Expert`(*args, **kwargs)

  A base class for Information Experts.

  static `options`()

    Subclasses must override the base class implementation.

    The method returns an ordered dictionary. Each key is the name of a piece of public data to be managed by the Expert class. The object mapped to that key configures how the data is to be published. See the `declare` method for details.

  `__init__`(*args, **kwargs)

    Subclasses must begin by invoking the superclass initialiser:

        super().__init__(*args, **kwargs)

    Unnamed positional arguments must be compatible with asyncio.Queue. Keyword arguments should have been generated by the `options` method.

  `__call__`(loop=None)

    Subclasses must override the base class implementation.

    This method makes the object callable. It is a coroutine to be launched as an asyncio.Task.

  `declare`(data, loop=None)

    Parameters: | data (a dictionary) – data to be published

    Invoke this method from within `__call__` to publish data via the class-defined interface.

    The keyword arguments previously supplied to `__init__` determine what attributes are published, according to the following mechanisms:

    Interface type | Publishing mechanism
    --- | ---
    `Attribute` | `<Subclass>.public.<name>` mirrors the data value.
    `Event` | `<Subclass>.public.<name>` is set or cleared by the data value.
    `RSON` | `RSON.dst` is the file path to the data in RSON format.
    `HATEOAS` | `HATEOAS.dst` is the file path to the data as a JSON web page.

  `watch`(q, **kwargs)

    Subclasses may override the base class implementation.

    This method is used to create one coroutine for each queue of input. The job of the method is to watch the queue for messages and dispatch them appropriately.