Wrapper development#

SimPhoNy Wrappers are software components that transform data from the assertional-knowledge form to the data structures of other software and back. Wrappers are abstracted to users as sessions, which may be viewed as “boxes” where ontology individuals can be stored.

Sessions work in a way similar to databases. To start using them, one first has to “open” or “connect” to them. After that, changes can be made on the data they contain, but such changes are not permanent until a “commit” is performed. When one finishes working with them, the connection should be “closed”. Unconfirmed changes are lost when the connection is “closed”.

Therefore, developing a wrapper involves crafting:

  • An abstraction of the concepts handled by the software as terminological knowledge, that can then be used to represent the information as assertional knowledge.

  • A database-like interface that is used by SimPhoNy to communicate with the software.

For the latter, SimPhoNy defines the Wrapper API, that must be implemented by the developer.

Wrapper abstract class#

The database-like interface used by SimPhoNy to communicate with the software, called Wrapper API, is defined by the simphony_osp.development.Wrapper abstract class. Objects belonging to the Wrapper class are indirectly controlled by the interactions between the user and session objects, as the diagram below shows.

Connection between sessions and wrappers

UML object diagram showing the objects involved in the SimPhoNy wrapper mechanism that are relevant from a developer’s perspective.

SimPhoNy makes use of the RDFLib library to handle RDF data. Thus, the session is in fact an interface to an RDFLib Graph. As the user interacts with the session, triples from the underlying graph are queried, added or removed. The library also provides a further abstraction, the store. Stores abstract triplestores, a kind of database that can store collections of RDF graphs in the form of triples. SimPhoNy implements a special kind of store that is designed to communicate with SimPhoNy wrapper objects, the final pieces of the chain.

The wrapper object’s interface is what the developer must implement in order to make the software interoperable with SimPhoNy. As pointed out in the diagram, there are several RDF Graph objects, session objects and lists (of ontology individual objects) that are accessible from the wrapper object. Those offer several ways for the developer to retrieve the inputs from the user and translate them into inputs for the software, or conversely, reflect the outputs from the software into the user’s session.

Perhaps the most important of all is the base graph. The base graph is an RDFLib Graph that is accessible from the wrapper object, and must be kept in sync with the software’s data structures at all times, as it constitutes their RDF representation. The goal of the SimPhoNy Wrapper API is to facilitate this task to the developer.

Note

If an RDFLib store plug-in already exists for a specific software, then it can be trivially reused to implement a SimPhoNy wrapper. Just create a graph using the plug-in, and set it as the base graph for the SimPhoNy wrapper object.

API Overview#

The flowchart below illustrates the lifecycle of a session connected to a wrapper object: from its creation to the moment it is closed.

A sequence of method calls is executed as a consequence of each possible action the user can take. Each sequence is represented using a different color, and the action that triggers it is written next to its accompanying arrow, that points to the first method call in the sequence.

Wrapper session lifecycle

Flowchart showing the catalogue of possible user actions and how they translate to calls to the methods of the wrapper class, that the wrapper developer must implement.

The Wrapper object is spawned when the user opens the session. The open and populate methods are then subsequently called in order to gain access to the resources needed by the software and pre-populate the session with ontology individuals if necessary. After that, the session is ready to be used. The user may then access or modify the assertional knowledge (triggering the optional, low-level RDF manipulation methods), access files associated to ontology individuals belonging to the File class (triggering the load method), add or change files, commit the changes or request the software to compute new results. When the user is done, the session is closed.

API Specification#

This section describes the expected behavior of all methods and features from the Wrapper API.

Main methods#

open#

Secure access to the resources used by your software. For example:

  • spawn simulation engine objects

  • open a connection to a database

  • open files that need to be accessed

simphony_osp.development.Wrapper.open(self, configuration: str, create: bool = False) None#

Open the data source that the wrapper interacts with.

You can expect calls to this method even when the data source is already accesible, therefore, an implementation similar to the one below is recommended.

>>> def open(self, configuration: str, create: bool = False):
>>>    if your_data_source_is_already_open:
>>>        return
>>>        # To improve the user experience you can check if the
>>>        # configuration string leads to a resource different from
>>>        # the current one and raise an error informing the user.
>>>
>>>    # Connect to your data source...
>>>    # your_data_source_is_already_open is for now True.

If you are using a custom base graph, please set self.base = your_graph within this method. Otherwise, an empty base graph will be created instead.

Parameters
  • configuration – Used to locate or configure the data source to be opened.

  • create – Whether the data source should be created if it does not exist. When false, if the data source does not exist, you should raise an exception. When true, create an empty data source.

populate#

Populate the base session so that it represents the initial state of the software. For example:

  • given a path to a simulation settings file, populate the session with entities descibing the contents of the file

  • populate the session with dataset individuals after connecting to a data repository

simphony_osp.development.Wrapper.populate(self) None#

Populate the base session so that it represents the data source.

This command is run after the data source is opened. Here you are expected to populate the base graph so that its information mimics the information on the data source, unless you are generating triples on the fly using the triples method. The default session inside this method is a session based on the base graph.

The base graph is available on self.base, and a session based on the base graph is available on self.session and self.session_base.

commit#

Reflect user’s changes on the session in the software’s data structure. For example:

  • configure a simulation’s settings

  • update an SQL table

  • modify a file

simphony_osp.development.Wrapper.commit(self) None#

This method commits the changes made by the user.

Within this method, you have access to the following resources:

  • self.base: The base graph (rw). You are not expected to modify it.

  • self.old_graph: The old graph (ro).

  • self.new_graph: The new graph (ro).

  • self.buffer: The buffer of triples caught by add and remove (rw) that you now have to reflect on the data structures of your software.

  • self.session_base: A session based on the base graph (rw). You are not expected to modify it.

  • self.session_old: A session based on the old graph (ro).

  • self.session_new: A session based on the new graph (ro).

  • self.session: same as self.session_new.

  • self.added: A list of added individuals (rw). You are not expected to modify the entities.

  • self.updated: A list of updated individuals (rw). You are not expected to modify the entities.

  • self.deleted: A list of deleted individuals (rw). You are not expected to modify the entities.

Before updating the data structures, check that the changes provided by the user do not leave them in a consistent state. This necessary because SimPhoNy cannot revert the changes you make to your data structures. Raise an AssertionError if the check fails.

Raises

AssertionError – When the data provided by the user would produce an inconsistent or unpredictable state of the data structures.

The difference with respect to the compute method is that this method should not update the content’s of the session itself.

close#

Release the resources used by your software. For example:

  • terminate the running process

  • close the connection to a database

  • close files that are not needed

simphony_osp.development.Wrapper.close(self) None#

Close the data source that the interface interacts with.

This method should NOT commit uncommitted changes.

This method should close the connection that was obtained in open, and free any locked up resources.

You can expect calls to this method even when the data source is already closed. Therefore, an implementation like the following is recommended.

>>> def close(self):
>>>    if your_data_source_is_already_closed:
>>>        return
>>>
>>>    # Close the connection to your data source.
>>>    # your_data_source_is_already_closed is for now True

compute (optional)#

Perform a computation using the data currently present on the software’s data structures and update the base session to reflect the changes afterwards. For example:

  • run a simulation

simphony_osp.development.Wrapper.compute(self, **kwargs: Union[str, int, float, bool, None, Iterable[Optional[Union[str, int, float, bool]]]]) None#

Compute new information (e.g. run a simulation).

Compute the new information on the backend and reflect the changes on the base graph. The default session is the base session.

The base graph is available on self.base, and a session based on the base graph is available on self.session and self.session_base.

__init__ (optional)#

The __init__ method allows the user to provide extra parameters in the form of JSON-serializable keyword arguments.

Note

This method does not appear on the flowchart for simplicity, but is executed before the open method.

simphony_osp.development.Wrapper.__init__(self, **kwargs: Union[str, int, float, bool, None, Iterable[Optional[Union[str, int, float, bool]]]])#

Initialize the wrapper.

The __init__ method accepts JSON-serializable keyword arguments in order to let the user configure parameters of the wrapper that are not configurable via the ontology. For example, the type of solver used by a simulation engine.

Save such parameters to private attribute to use them later (e.g. in the open method).

Parameters

kwargs – JSON-serializable keyword arguments that contain no nested JSON objects (check the type hint for this argument).

File manipulation methods#

load (optional)#

Receive an identifier of a file individual and retrieve the corresponding file.

simphony_osp.development.Wrapper.load(self, key: str) BinaryIO#

Retrieve a file.

Provide a file handle associated with the provided key.

Parameters

key – Identifier of the individual associated with the file.

Returns

File handle associated with the provided key.

save (optional)#

Receive an identifier of a file individual and a file handle and save the contents somewhere.

simphony_osp.development.Wrapper.save(self, key: str, file: BinaryIO) None#

Save a file.

Read the bytestream offered as a file handle and save the contents somewhere, associating them with the provided key for later retrieval.

Parameters
  • key – Identifier of the individual associated with the file.

  • file – File (as a file-like object) to be saved.

delete (optional)#

Receive the identifier of a file individual and delete the stored file.

simphony_osp.development.Wrapper.delete(self, key: str) None#

Delete a file.

Delete the file associated with the provided key.

Parameters

key – Identifier of the individual associated with the file.

RDF manipulation methods#

These methods operate at the RDF triple level. When a triple (or pattern) is added or removed, they can intercept the operation and decide whether the triple should go to the base graph or not when a commit operation is executed. The intercepted triples get stored in a buffer that is accessible during the commit operation.

They are useful when one wants to prevent storing the same information twice (in the base graph and in the software’s data structure).

add (optional)#

simphony_osp.development.Wrapper.add(self, triple: Tuple[rdflib.term.Node, rdflib.term.Node, rdflib.term.Node]) bool#

Inspect and control the addition of triples to the base graph.

Parameters

triple – The triple being added.

Returns

True when the triple should be added to the base graph. False when the triple should be caught, and therefore not added to the base graph. This triple will be latter available during commit on the buffer so that the changes that it introduces can be translated to the data structure.

remove (optional)#

simphony_osp.development.Wrapper.remove(self, pattern: Tuple[Optional[rdflib.term.Node], Optional[rdflib.term.Node], Optional[rdflib.term.Node]]) Iterator[Tuple[rdflib.term.Node, rdflib.term.Node, rdflib.term.Node]]#

Inspect and control the removal of triples from the base graph.

Parameters

pattern – The pattern being removed.

Returns

An iterator with the triples that should be removed from the base graph. Any triples not included will not be removed, and will be available on the buffer during commit.

triples (optional)#

simphony_osp.development.Wrapper.triples(self, pattern: Tuple[Optional[rdflib.term.Node], Optional[rdflib.term.Node], Optional[rdflib.term.Node]]) Iterator[Tuple[rdflib.term.Node, rdflib.term.Node, rdflib.term.Node]]#

Intercept a triple pattern query.

Can be used to produce triples that do not exist on the base graph on the fly.

Parameters

pattern – The pattern being queried.

Returns

An iterator yielding triples

Options#

SimPhoNy includes several configurable features that facilitate wrapper development. Some of them are enabled by default, while others are disabled. Enabling or disabling them consists of configuring the value of an attribute in the implementation of the Wrapper abstract class. This subsection describes the attributes that can be configured.

entity_tracking (default True)#

Entity tracking is actually what makes it possible to use the added, updated and deleted attributes of the Wrapper abstract class from within the commit method. When set to True (the default), whenever triples describing a new subject are added, SimPhoNy will add an ontology individual object to the added list. If the individual already existed and still exists, but just some triples for which it is the subject have been added or removed, then it is put in the updated list. When all triples describing the individual are deleted, an ontology individual object based on the session’s status before the commit is put in the deleted list.

Disabling this feature speeds up commits.

cache (default False)#

When set to True, the InterfaceDriver tries to retrieve triples from a cache managed by SimPhoNy instead of from the implementation of the Wrapper abstract class. or its base graph. Whenever there is a cache miss, SimPhoNy queries the Wrapper object for that specific triple pattern. In addition, if the pattern contains a subject, SimPhoNy will also query all information about such ontology individual as well as all “parent”, “children” and “neighboring” individuals. For an individual, its parents are those other individuals who are connected to it through a relationship in which the parent is the subject and the aforementioned individual is the object. Similarly, the children are those individuals connected to it through a relationship where the aforementioned individual is the subject, and the children are the objects. The neighbors are the children of the parent individual.

This feature is useful whenever there is communication with a remote server involved, as it replaces large amounts of small exchanges of information with the server with small amounts of large exchanges of information.

Packaging template#

This page is meant to offer a mid-level view on what SimPhoNy Wrappers are and how do they work. If you are interested in developing one, you may find a template for building and packaging a wrapper in the wrapper development repository.