Wrapper development
Contents
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.
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.
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.