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.
-
static