CM Frontend Reference

This module is an EXOS CM frontend that implements PEP-249, the Python Database API Specification v2.0.

Connection

exos.api.cmfrontend.connect(username)[source]

Create a connection to the database. This will return an instance of the Connection class.

class exos.api.cmfrontend.Connection(username, print_callback=None)[source]

Represents a logical connection to CM as a frontend for PEP-249 compliance. Connections must be created using connect().

CM does not have the concept of a connection, but this class will manage initialization of the underlying library. It also serves as a factory for Cursor instances.

close()[source]

Close this connection now. This is a no-op as CM does not have the concept of connections.

commit()[source]

Commit data written on this connection. This is a no-op as CM does not support transactions.

cursor()[source]

Create a cursor on this connection.

Cursor

class exos.api.cmfrontend.Cursor(connection, vr_name=None)[source]

Object to manage the execution of a request. Cursors are created from the Connection.cursor() method. Cursors are stateful and operate on a single Request at a time. The state is reset with every call to execute().

retries

Number of times to retry a request if a module is unavailable. A module may be unavailable while it is restarting. By default, set to 0 so retries are not attempted.

retry_timeout

Timeout between retries in seconds. By default, 5 seconds.

vr_name

Name of the current VR context. This will be passed to the backend module along with the request, encouraging it to run the request in the context of the given VR. Only a handful of modules are VR context aware, so usually this can be left alone. Defaults to None, indicating no VR context.

messages

List of messages received from CM while executing a request. Per PEP-249, this list is cleared on execute(). The same list instance is used for the life of the cursor.

connection

Per PEP-249, the Connection to which this cursor is associated. Read-only.

description

Per PEP-249, sequence of sequences describing the table over which this cursor is executing. Read-only and set after an execute().

Only the two mandatory items, name and type_code, are provided. The name is that specified in Request.add_field(). The type_code is always STRING.

rowcount

Per PEP-249, number of rows affected by the last sequence. Always -1, indicating not supported.

rownumber

Per PEP-249, read-only attribute providing the current 0-based index of the cursor in the result set. The next fetch operation will fetch the row indexed by rownumber. In other words, after an execute(), it will be 0. After the first fetchone(), it will be 1. And so on. Before an execute(), it will be None.

arraysize

Per PEP-249, number of rows we’ll fetch at a time from the CM. Always 1.

execute(req, param_values=None)[source]

Prepare and execute the given req with the optional param_values.

req must be a Request instance created with request(). Additionally, req must be valid, which means all tables have indexes, all parameters have values, etc., so that complete CM requests can be built.

param_values must be a dictionary mapping each parameter name to a value.

fetchrow()[source]

Fetch the next row of an operation, returning it as a Row object.

fetchrows()[source]

Fetch all remaining rows of an operation, returning them as a sequence of Row objects. Similar to iterrow(), except that this method blocks untils all rows have been loaded into memory.

iterrow()[source]

Generator that produces all remaining rows of an operation, returning them as Row objects. Similar to fetchrows(), except that rows are returned as they are received. The next row is fetched asynchronously.

fetchone()[source]

Per PEP-249, fetch the next row of an operation, returning a single sequence, or None when no more data is available. The field order in sequence matches the field order in the executed Request if specified, else will be unordered.

fetchall()[source]

Per PEP-249, fetch all remaining rows of an operation, returning them as a sequence of sequences (e.g. a list of tuples). Similar to __iter__(), except that this method blocks until all rows have been loaded into memory.

next()[source]

Implement an iterator by calling fetchone() and raising StopIteration as appropriate.

__iter__()[source]

Per PEP-249, return an iterator that produces all remaining rows of an operation, returning each as a single sequence. Similar to fetchall(), except that rows are returned as they are received. The next row is fetched asynchronously.

close()[source]

Close this cursor now. This is a no-op.

Request

exos.api.cmfrontend.request(op=CM_OP_GET, outer_join=False)[source]

Create a new Request instance, which can be used as an operation when calling Cursor.execute().

op is CM_OP_GET or CM_OP_SET. The default is a GET.

If outer_join is True, we’ll peform a “left outer join”. In a “left outer join”, the row is still returned, but the missing data is replaced with None. If outer_join is False, the default, we’ll perform an “inner join”. In an “inner join”, the resulting row is only returned if data could be retrieved from all joined tables.

class exos.api.cmfrontend.Table(module_name, table_name)[source]

Uniquely identifies a table within CM. Tables are scoped to a module, so this is a tuple of (module_name, table_name). Table instances are immutable.

__init__(module_name, table_name)[source]

Create a new Table instance. module_name is the backend that owns the table. table_name identifies the table. Both are strings.

module_name

Name of the table’s module.

table_name

Name of the table.

class exos.api.cmfrontend.Column(table, *args)[source]

Uniquely identify a column within CM. Columns are scoped to a Table, so this is a tuple of (table, column_name), which can also be thought of as (module_name, table_name, column_name). Column instances are immutable.

__init__(table, column_name)[source]

Create a Column instance given a table and column_name. table can be an instance of Table or a tuple of (module_name, table_name), in which case a Table instance is created automatically.

Columns are commonly created as follows:

col=Column((module_name, table_name), column_name)

Alternatively:

tab=Table(module_name, table_name)
col=Column(tab, column_name)
table

The Table in which this column exists.

column_name

The column’s name.

module_name

Short-hand for table.module_name.

table_name

Short-hand for table.table_name.

class exos.api.cmfrontend.Request(op=2, outer_join=False, bulk=1, order='FIFO')[source]

Represent a CM Request. It identifies the requested fields, the source for each field, and any required parameters and indexes. A Request instance can be sent as an operation to Cursor.execute(). On execute(), the Request will be validated to ensure it is properly populated. InterfaceError is raised if it is not.

Request instances must be created via the request() method.

outer_join

If True, we’ll perform a “left outer join”. If False, the default, we’ll perform an “inner join”. See request() for more information.

op

Either CM_OP_GET or CM_OP_SET.

method
tables

List of tables encountered while building this request. Tables are not added explicitly, but discovered as fields, indexes, and params are added.

main_table

The main table in this request, which will be used to drive the request when interacting with CM. It is arbitrarily taken as the first table encountered while building the request, otherwise None.

fields

Ordered list of fields that will be included in the response. This is a mapping from a user-defined label to the fields’s data source, typically an instance of Column. Fields can be added to the request via add_field().

indexes

Mapping from Table to a list of indexes found within that table. Each index is an instance of Column. Indexes must be identified by the client and are added to the request via add_index(). When the Request is validated, at least one index must exist for each table. Indexes are not included in the response, unless the Column was also passed to add_field().

params

List of request parameters. This is a mapping from a Column instance to the parameter’s value, typically a constant, but, in the case of a join, could also be another Column. The value may also be None, indicating that a constant value will be passed on execute().

Parameters are added to the request via add_param().

add_index(index_column)[source]

Identify an index Column. CM requires an index for each table so that it can getnext through the rows of a table. A client must identify which Columns are indexes.

index_column must be an instance of Column or a tuple that Column knows how to handle.

add_param(param_column, param_value=None)[source]

Identify a parameter Column. On a GET, parameters are typically filters or join conditions. On a SET, they are typically updated field values. Parameters are passed to CM “as is” with the expectation that the client and the module know how to talk to each other.

Not all Columns can be parameters. On a GET, CM supports only a subset of Columns as filters. On a SET, not all Columns are writeable. Again, it is assumed the client and module know how to talk to each other.

param_column must be an instance of Column or a tuple that Column knows how to handle.

param_value can be any of the following:

  • None. This indicates a constant value will be passed on Cursor.execute().
  • A constant. Same as None, except that the value is provided now instead of on Cursor.execute().
  • Another Column. This builds a join condition where the two columns are joined with an equals.

param_value constants are stringified with the str() function each time an XML request is sent to the CM backend. As such, a dynamic param_value is possible by implementing the value’s __str__() method.

add_field(field_label, field_source)[source]

Identify a field in the response. Fields are kept in an ordered list, so the response’s order will match the order in which the fields were added.

field_label is the string that will be used to identify the field in the response. It is arbitrary and user-defined.

field_source indicates how the field’s value will be retrieved. It can be any of the following:

  • An instance of Column. In this case, the data source is CM and the given Column will be retrieved.
  • A callable. Reserved for future. For now, an exception is thrown. In the future, a callable will allow a field’s value to be derived, perhaps from other field values.
  • An instance of any other class. The field source is a literal and the string version will be echoed back as the field’s value.
copy()[source]

Return a shallow copy of this Request.

set_vr_name(vr)[source]

Set the VR context for this Request. It will override the Cursor’s VR context. See Cursor.vr_name.

Row

class exos.api.cmfrontend.Row(rowid, op_status)[source]

Represent a row of response from CM. Instances of Row are returned by Cursor.fetchrow(), Cursor.fetchrows(), and Cursor.iterrow().

PEP-249 defines responses as arrays of values. The cmfrontend module additionally provides the Row object to provide a richer interface into the data returned by CM.

rowid

This row’s ID. Matches up with Cursor.rownumber.

opMsg

Any message returned by CM or None.

field_values

Dict of field values in the Row. This is a mapping from the field’s label to the fields’s value.

Errors

class exos.api.cmfrontend.ModuleUnavailable(modname)[source]

The request cannot be processed because the module is unavailable. This may be a temporary condition. The when_ready() method may be used to be notified when the condition clears.

This is an OperationalError.

module_name

The module that was unavailable.

when_ready(callback=None, timeout=5)[source]

Call the given callback when the module might have become available or the timeout is reached. At this point, it’s safe to try the request again, but it might also fail again.

If a callback is not provided, the caller will be blocked instead.

PEP-249 Errors

Error classes as defined and required by PEP-249.

class exos.api.cmfrontend.Error[source]
class exos.api.cmfrontend.Warning[source]
class exos.api.cmfrontend.InterfaceError[source]
class exos.api.cmfrontend.DatabaseError[source]
class exos.api.cmfrontend.InternalError[source]
class exos.api.cmfrontend.OperationalError[source]
class exos.api.cmfrontend.ProgrammingError[source]
class exos.api.cmfrontend.IntegrityError[source]
class exos.api.cmfrontend.DataError[source]
class exos.api.cmfrontend.NotSupportedError[source]

PEP-249 Types

Types classes as defined and required by PEP-249. Only the STRING type is currently used in cmfrontend.

exos.api.cmfrontend.STRING
exos.api.cmfrontend.BINARY
exos.api.cmfrontend.NUMBER
exos.api.cmfrontend.DATETIME
exos.api.cmfrontend.ROWID