Public Modules

Model

class restio.model.BaseModel(**kwargs)

A representation of a remote object model.

BaseModel is an abstract class that should be extended to represent models incoming from or outgoing to a remote REST API.

Models can exist independently from Sessions but contain an internal state that indicates the status of the model within the current context. The Sessions are responsible to control this state. Also, each model contains a set of control attributes that indicate which fields are watched by restio internals. By default, all Field descriptors in the model will become field attributes. Fields declared with pk=True will be used by restio to optimize the caching of the models in a Session.

Models that change over time will contain an internal dictionary with the latest know persistent value of each field. This is done to guarantee fast rollback of the values when the Session is invalid, and to also indicate which values might have changed within the session scope. If a field is modified directly, the model will intercept the change and save the older value into the persistent dictionary until _persist is called. During a _rollback call, however, the stored values are re-assigned to their original attributes. Each attribute change will also dispatch an update event so that the session is aware of changes and manages the model’s internal state accordingly. The persistent dictionary (through the helper method is_field_modified) can also be used by DAO’s to verify which values where updated prior to sending a request through the REST API, thus allowing for proper optimization and minimizing chances of conflicting changes on the remote object.

All models automatically generate a random internal UUID when created. This UUID is used internally for comparison purposes, and externally as an identity. Although this attribute is not explicitly set as private, it should never be modified.

dependency_fields

Returns the values of each field that have relationship with other models.

Returns:The dictionary of fields and their values
fields

Returns the values of each field in the model instance.

Returns:A dict with keys containing the string names of the fields, and values containing the value of the corresponding field.
get_children(recursive: bool = False, children: Optional[Set[restio.model.BaseModel]] = None, top_level: Optional[restio.model.BaseModel] = None) → Set[restio.model.BaseModel]

Returns the list of all children of the current model. This algorithm checks in runtime for all objects refered by the instance and that are part of fields marked with depends_on=True. When recursive is True, then the algorithm will recursively search through all children.

children and top_level are control variables that indicate which models have already been inspected by this function, in order to avoid infinite recursion if any circular dependency exists. In most cases, they should be left empty.

Parameters:
  • recursive – If True, recursively searches for children. Returns only first degree relationships otherwise. Defaults to False.
  • children – List of existing models already inspected.
  • top_level – The top-level model from where inspection started.
Returns:

The list of children.

is_field_modified(field_name: str) → bool

Indicates of field with name field_name has been modified.

Parameters:field_name – The name of the field.
Raises:ValueError – When the field name does not exist.
Returns:True if field is modified, False otherwise.
primary_keys

Returns a dictionary containing all primary keys. The keys will be ordered in the same order as they are declared in the model type, also following the order in which they appear in class inheritance.

This property is optimized to minimize the number of iterations done in the model instance by internalizing a cache with the latest retrieved primary keys. This cache is reset for every modification of a primary key and recovered during the next call to the property.

Returns:The ordered tuple of values.
class restio.fields.Field(type_: Union[Type[T_co], str], *, pk: bool, allow_none: bool, depends_on: bool, frozen: restio.fields.base.FrozenType, init: bool = True, default: Union[T_co, None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], T_co], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)

Base type for Fields.

default

Extracts the default value of the field.

Raises:ValueError – When no default value has been set during initialization.
Returns:The default value.
has_default

Indicates if the field has a default value set during the initialization.

Returns:True if a default value has been set, False otherwise.
setter(method: Optional[Callable[[Model_co, T_co], T_co]])

Defines the setter function method for the current field. method is only triggered when a value is assigned to the field through the descriptor protocol.

Parameters:method – The method to be called for setting the value. Method should accept 2 parameters and must return the value to be assigned to the field. The first parameter will contain the instance from which the setter was called, and the second will contain the value assigned.
Raises:ValueError – When the signature of method is incorrect.
Returns:The decorated method.
class restio.fields.ContainerField(type_: Union[Type[T_co], str], sub_type: Union[Type[SubT], str], *, pk: bool, allow_none: bool, depends_on: bool, frozen: restio.fields.base.FrozenType, init: bool = True, default: Union[T_co, None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], T_co], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.IterableField(type_: Union[Type[T_co], str], sub_type: Union[Type[SubT], str], *, allow_none: bool, depends_on: bool, frozen: restio.fields.base.FrozenType, init: bool = True, default: Union[T_co, None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], T_co], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.IntField(*, pk: bool = False, init: bool = True, default: Union[int, None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], int], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, allow_none: bool = False, frozen: restio.fields.base.FrozenType = <FrozenType.NEVER: 1>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.StrField(*, pk: bool = False, init: bool = True, default: Union[str, None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], str], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, allow_none: bool = False, frozen: restio.fields.base.FrozenType = <FrozenType.NEVER: 1>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.BoolField(*, pk: bool = False, init: bool = True, default: Union[bool, None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], bool], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, allow_none: bool = False, frozen: restio.fields.base.FrozenType = <FrozenType.NEVER: 1>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.FloatField(*, pk: bool = False, init: bool = True, default: Union[float, None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], float], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, allow_none: bool = False, frozen: restio.fields.base.FrozenType = <FrozenType.NEVER: 1>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.UUIDField(*, pk: bool = False, init: bool = True, default: Union[uuid.UUID, None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], uuid.UUID], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, allow_none: bool = False, frozen: restio.fields.base.FrozenType = <FrozenType.NEVER: 1>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.EnumField(type_: Type[EnumType], *, pk: bool = False, init: bool = True, default: Union[EnumType, None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], EnumType], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, allow_none: bool = False, frozen: restio.fields.base.FrozenType = <FrozenType.NEVER: 1>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.TupleField(sub_type: Type[SubT], *, init: bool = True, depends_on: bool = False, default: Union[Tuple[SubT, ...], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], Tuple[SubT, ...]], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, frozen: restio.fields.base.FrozenType = <FrozenType.NEVER: 1>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.FrozenSetField(sub_type: Type[SubT], *, init: bool = True, depends_on: bool = False, default: Union[FrozenSet[SubT], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], FrozenSet[SubT]], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, frozen: restio.fields.base.FrozenType = <FrozenType.NEVER: 1>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.ModelField(model_type: Union[Type[Model_co], str], *, init: bool = True, default: Union[Model_co, None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], Model_co], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, allow_none: bool = True, depends_on: bool = True, frozen: restio.fields.base.FrozenType = <FrozenType.NEVER: 1>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.TupleModelField(model_type: Union[Type[Model_co], str], *, init: bool = True, default: Union[Tuple[Model_co, ...], Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], Tuple[Model_co, ...]], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, depends_on: bool = True, frozen: restio.fields.base.FrozenType = <FrozenType.NEVER: 1>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.FrozenSetModelField(model_type: Union[Type[Model_co], str], *, init: bool = True, default: Union[FrozenSet[Model_co], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, default_factory: Union[Callable[[], FrozenSet[Model_co]], None, Type[restio.fields.base.MISSING]] = <class 'restio.fields.base.MISSING'>, depends_on: bool = True, frozen: restio.fields.base.FrozenType = <FrozenType.NEVER: 1>, setter: Optional[Callable[[Model_co, T_co], T_co]] = None, repr: bool = True, type_check: bool = True)
class restio.fields.FrozenType

Indicates when a particular field should not be modified.

  • NEVER: The field can be modified at any time (read and write).
  • UPDATE: The field should be read-only for a model update.
  • CREATE: The field should be read-only when creating the model.
  • ALWAYS: The field is always read-only.

Data Access Object

class restio.dao.BaseDAO(model_type: Optional[Type[Model_co]] = None)

Base abstract class for Data Access Objects (DAO).

The subclasses of BaseDAO in the restio framework represent the core of the data access layer to a particular model in a remote REST API. Sessions use the DAOs to perform CRUD operations on the remote server using the async methods get, add, update and remove.

Each DAO instance should be registered to a Session instance and associated to a model type through its constructor. The model type is stored internally and used by the Session to identify the DAO that is responsible for a particular model. When bound to a Session instance, the DAO instance receives a reference to the Session, which is always available internally.

Differently from a regular abstract class, the methods of BaseDAO subclasses don’t need to be overwritten unless the Session needs to make use of them. In that case, not implementing the method will cause the Session to fail in runtime for not-implemented add, remove or update. Methods that fail during a Session commit might raise an Exception, which will be picked up by the Session. Models can be modified in the methods of the DAO, and the values will be persisted to the model’s cache accordingly if the operation is successful (no exception thrown).

It is recommended that each get, add, remove and update method is overwritten in the subclass if the operation is permitted by the REST API.

add(obj: Model_co)

Creates a model in the remote server.

Parameters:obj – The model instance to be created.
get(**keys) → Model_co

Retrieves a model from the remote server.

# noqa: DAR202 return

Parameters:keys – The keyword arguments representing the primary keys of the model to be retrieved.
Returns:The model retrieved from the remote server.
remove(obj: Model_co)

Removes a model from the remote server.

Parameters:obj – The model instance to be removed.
session

Returns the Session instance to which the DAO instance is bound.

Raises:RuntimeError – When the DAO instance is not bound to any Session instance.
Returns:The Session instance.
update(obj: Model_co)

Updates a model in the remote server.

Parameters:obj – The model to be updated.
class restio.dao.DAOTask(node: restio.graph.Node[+Model_co][Model_co], func: Callable[[Model_co], Awaitable[None]])

Wrapper object that, when awaited, contains a asyncio.Task ran by a a DAO method during a Session commit.

When a DAOTask instance is awaited for the first time, it triggers run_task() and returns the result from the undelying task wrapping func. Awaiting the same instance multiple times will not retrigger the task, but instead will return the value from the first call. Alternatively, it is also possible to retrieve the underlying task directly through the attribute task.

Running a DAOTask will also record the start_time and end_time of the first execution.

duration

Returns the duration of the execution (in seconds).

Raises:RuntimeError – When the execution has not been finished.
Returns:The duration (in seconds).
model

Returns the BaseModel contained by the node.

Returns:The BaseModel instance.
run_task() → asyncio.Task[Optional[Model_co]]

Creates and returns an asyncio.Task that runs func when called for the first time.

When called multiple times, returns the existing asyncio.Task created during the first execution.

Returns:The asyncio.Task instance.
task

Contains the underlying asyncio.Task.

Raises:RuntimeError – When a Task has not yet been triggered.
Returns:The asyncio.Task instance.

Session

class restio.session.PersistencyStrategy

Defines the strategy of error handling during a Session commit.

  • INTERRUPT_ON_ERROR (default):
    The Session will be interrupted if any operation made by a DAO throws an exception. The operations already performed will be persisted on local cache, and the on-going tasks in the other dependency trees will be finalized. The commit operation will hang until all running tasks finalize, then a list of all DAOTask’s will be returned by the commit. This is the recommended approach.
  • CONTINUE_ON_ERROR:
    The Session will continue processing all models in the dependency tree that have been marked for modification. The framework will persist on local cache only the models that have been successfuly synchronized to the remote store. No cancellation will be done.
class restio.session.Session(strategy: restio.session.PersistencyStrategy = <PersistencyStrategy.INTERRUPT_ON_ERROR: 1>)

Manages a local session scope for interfacing with a remote REST API server.

The Session will manage an internal cache where it stores the models retrieved from or persisted to the remote REST API. Models are uniquely identified by their internal id and their primary keys. Trying to retrieve the same model twice within the same scope will make the Session return the cached models instead of querying the remote server again.

When all models have been modified, created or deleted, the Session can be persisted on the remote server with commit.

The Session should know how to manipulate each model type by registering the respective DAOs with register_dao. When asked to get, add or remove a model, the Session will look up for the DAO associated with the provided model type. If the DAO raises an exeption during commit, then the Session will handle the erros based on the predefined PersistenceStrategy provided during instantiation.

get operations will be executed on spot, while add, remove and _update will be scheduled to run during commit. The Session will decide in which order to manipulate each model based on dependency trees generated in runtime. The Session will try to parallelize the calls to the remote server as much as possible to guarantee data consistency. The dependency trees vary with the current internal state of each model instance as soon as commit is called. The new state of each model is managed by the Session itself during the commit.

When called by a Session, DAO instances are allowed to modify the models locally during get, add or update without causing the models’ states to change, and without being affected by the defined frozen attribute of each field.

After a commit is done, all models that have been persisted on the remote server successfully will be persisted on the local cache, even if errors occur in other parallel tasks.

add(model: restio.model.BaseModel)

Adds the new model to the local cache and schedules for adding in the remote server during commit.

Parameters:model – The model instance.
Raises:RuntimeError – If the model has been already registered in cache.
commit(raise_for_status: bool = True) → List[restio.dao.DAOTask]

Persists all models on the remote server. Models that have been successfully submited are also persisted on the local cache by the end of the operation.

Parameters:raise_for_status – The commit will raise a SessionException if at least one task executed in the commit raises an exception.
Returns:A list containing all DAOTask’s performed by the commit, in the order in which the operations have been finalized.
get(model_type: Type[Model_co], **keys) → Model_co

Tries retrieving the model of type model_type and primary keys keys from the local cache. If not found, then calls the get method of the DAO associated to the model_type.

Parameters:
  • model_type – The BaseModel subclass representing the model.
  • keys – The primary key values that represents the model.
Raises:
  • ValueError – When a primary key value is missing.
  • RuntimeError – When no model is found with the given primary keys.
Returns:

The model instance.

get_dao(model_type: Type[Model_co]) → restio.dao.BaseDAO[+Model_co][Model_co]

Returns the DAO associated with model_type.

Parameters:model_type – The BaseModel subclass.
Raises:NotImplementedError – If no DAO has been associated to the provided model_type.
Returns:The DAO instance.
query(query: restio.query.BaseQuery[+Model_co][Model_co], force: bool = False) → Tuple[Model_co, ...]

Runs custom query query and registers results in the local cache.

When new queries are run for the first time, the results are persisted into the query and model caches. Models that already exist in cache (by checking their primary keys) are not registered again (even if force=True), to preserve the information already stored and avoid discrepancies.

The returning values from the query will contain only the models that are registered in the cache. Discarded models are replaced with their cached version.

This method will always return an ordered tuple containing all the values as they were registered in cache, even when the return type of the query is another iterable other than tuple.

Parameters:
  • query – The query instance to be executed.
  • force – Forces running the query even if it has been already cached.
Raises:

TypeError – When the provided query is not a BaseQuery.

Returns:

The list of models retrieved by the query.

static raise_for_status(tasks: Iterable[restio.dao.DAOTask])

Raises a SessionException if at least one resulting DAOTask from tasks returned by Session.commit() contains an underlying exception raised.

Parameters:tasks – The list of DAOTasks incoming from Session.commit()
Raises:SessionException – When at least one task has raised an exception.
register_dao(dao: restio.dao.BaseDAO[restio.model.BaseModel][restio.model.BaseModel])

Registers a DAO instance to the session and maps its model type in the local dictionary.

Parameters:dao – The DAO instance.
register_model(model: restio.model.BaseModel, force: bool = False) → bool

Registers the model in the internal cache. If the model is already registered and force is False, then the operation is skipped.

Parameters:
  • model – The model instance to be registered.
  • force – Forces the operation if True, skips otherwise. Defaults to False.
Raises:

RuntimeError – When at least one child of the provided model is not yet registered in the cache.

Returns:

True if the model has been registered, False if it has been skipped.

register_query(query: restio.query.BaseQuery[+Model_co][Model_co], models: Iterable[Model_co], force: bool = False)

Registers the query and its models in the internal cache. If any of the models is already registered and force is False, then they are not persisted into the model cache again.

All query results are stored as tuples, to preserve the order in an immutable structure.

Parameters:
  • query – The query instance to be registered.
  • models – The iterable containing the models to be registered as a result of the query. The order of iteration is preserved in the cache as a tuple.
  • force – Forces the operation if True, skips otherwise. Defaults to False.
remove(model: restio.model.BaseModel) → bool

Schedules model for removal on the remote server during commit.

Parameters:model – The model instance.
Returns:True if model is scheduled for removal, False otherwise.
reset()

Resets the internal cache. All references to models are lost.

rollback()

Discards all changes from the local cache. New models are discarded and local changes to models retrieved from the remote are ignored.

state

Returns the state in which the Session is within the current context.

Returns:The SessionState set for the context.
unregister_model(model: restio.model.BaseModel)

Unregisters model from the internal cache.

Parameters:model – The model instance to be unregistered.
update_cache()

Inspects all processed models during a commit an forces registering again. This is mainly used to guarantee that models with changed primary keys have been re-registered in the cache with the correct value.

exception restio.session.SessionException(_exception_tasks: List[Tuple[restio.dao.DAOTask, BaseException]], _successful_tasks: List[restio.dao.DAOTask])

Raised when at least one Exception is raised by a DAOTask during a commit.

class restio.session.SessionState

Stores the current state of the Session. The state is used by the Session scope for decision making when particular actions are being executed.

  • STANDBY: The session has been created and is not performing any action.
  • GET: The session is currently acquiring a model from the remote server.
  • ADD: The session is currently adding a new model to the internal cache.
  • COMMIT: The session is performing a commit.
  • ROLLBACK: The session is performing a rollback.
restio.session.sessionstate(state: restio.session.SessionState) → Callable[[T], T]

Decorates the current method to define the state of the session during its execution.

# noqa: DAR301 yield

Parameters:state – The state to be set to the session while the operation is in place.
Returns:The function decorator.