blob: 06c29e83ba99b3935fe36d8bfc4d5e587403b2ce [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
ARIA's storage Sub-Package
Path: aria.storage
Storage package is a generic abstraction over different storage types.
We define this abstraction with the following components:
1. storage: simple mapi to use
2. driver: implementation of the database client mapi.
3. model: defines the structure of the table/document.
4. field: defines a field/item in the model.
API:
* application_storage_factory - function, default ARIA storage factory.
* Storage - class, simple storage mapi.
* models - module, default ARIA standard models.
* structures - module, default ARIA structures - holds the base model,
and different fields types.
* Model - class, abstract model implementation.
* Field - class, base field implementation.
* IterField - class, base iterable field implementation.
* drivers - module, a pool of ARIA standard drivers.
* StorageDriver - class, abstract model implementation.
"""
import copy
from contextlib import contextmanager
from aria.logger import LoggerMixin
from . import sql_mapi
__all__ = (
'Storage',
'ModelStorage',
'ResourceStorage'
)
class Storage(LoggerMixin):
"""
Represents the storage
"""
def __init__(self,
api_cls,
api_kwargs=None,
items=(),
initiator=None,
initiator_kwargs=None,
**kwargs):
"""
:param api_cls: API cls for each model.
:param api_kwargs:
:param items: the items to register
:param initiator: a func which initializes the storage before the first use.
This function should return a dict, this dict would be passed in addition to the api kwargs.
This enables the creation of any unpickable objects across process.
:param initiator_kwargs:
:param kwargs:
"""
super(Storage, self).__init__(**kwargs)
self.api = api_cls
self.registered = {}
self._initiator = initiator
self._initiator_kwargs = initiator_kwargs or {}
self._api_kwargs = api_kwargs or {}
self._additional_api_kwargs = {}
if self._initiator:
self._additional_api_kwargs = self._initiator(**self._initiator_kwargs)
for item in items:
self.register(item)
self.logger.debug('{name} object is ready: {0!r}'.format(
self, name=self.__class__.__name__))
@property
def _all_api_kwargs(self):
kwargs = self._api_kwargs.copy()
kwargs.update(self._additional_api_kwargs)
return kwargs
def __repr__(self):
return '{name}(api={self.api})'.format(name=self.__class__.__name__, self=self)
def __getattr__(self, item):
try:
return self.registered[item]
except KeyError:
return super(Storage, self).__getattribute__(item)
@property
def serialization_dict(self):
return {
'api': self.api,
'api_kwargs': self._api_kwargs,
'initiator': self._initiator,
'initiator_kwargs': self._initiator_kwargs
}
def register(self, entry):
"""
Register the entry to the storage
:param name:
:return:
"""
raise NotImplementedError('Subclass must implement abstract register method')
class ResourceStorage(Storage):
"""
Represents resource storage.
"""
def register(self, name):
"""
Register the resource type to resource storage.
:param name:
:return:
"""
self.registered[name] = self.api(name=name, **self._all_api_kwargs)
self.registered[name].create()
self.logger.debug('setup {name} in storage {self!r}'.format(name=name, self=self))
class ModelStorage(Storage):
"""
Represents model storage.
"""
def __init__(self, *args, **kwargs):
if kwargs.get('initiator', None) is None:
kwargs['initiator'] = sql_mapi.init_storage
super(ModelStorage, self).__init__(*args, **kwargs)
def register(self, model_cls):
"""
Register the model into the model storage.
:param model_cls: the model to register.
:return:
"""
model_name = model_cls.__modelname__
if model_name in self.registered:
self.logger.debug('{name} in already storage {self!r}'.format(name=model_name,
self=self))
return
self.registered[model_name] = self.api(name=model_name,
model_cls=model_cls,
**self._all_api_kwargs)
self.registered[model_name].create()
self.logger.debug('setup {name} in storage {self!r}'.format(name=model_name, self=self))
def drop(self):
"""
Drop all the tables from the model.
:return:
"""
for mapi in self.registered.values():
mapi.drop()
@contextmanager
def instrument(self, *instrumentation):
original_instrumentation = {}
try:
for mapi in self.registered.values():
original_instrumentation[mapi] = copy.copy(mapi._instrumentation)
mapi._instrumentation.extend(instrumentation)
yield self
finally:
for mapi in self.registered.values():
mapi._instrumentation[:] = original_instrumentation[mapi]