Source code for dlab_core.containers

# *****************************************************************************
#
# 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.
#
# ******************************************************************************

from dlab_core.domain.exceptions import DLabException

[docs]LC_ERR_CANNOT_OVERRIDE_FROZEN = 'Cannot override frozen service "{key}".'
[docs]class ContainerException(DLabException): """Base Container Exceptions. Raised during container execution.""" pass
[docs]class ContainerTypeException(ContainerException): """Raised when try to assign wrong data type.""" pass
[docs]class ContainerFrozenServiceException(ContainerException): """Raised when try to access a key that is represent frozen data in a dictionary (dict). """ def __init__(self, key): super(ContainerFrozenServiceException, self).__init__( LC_ERR_CANNOT_OVERRIDE_FROZEN.format(key=key)
)
[docs]class ContainerKeyException(ContainerException): """Raised when try to access a key that isn't in a dictionary (dict).""" pass
[docs]class ContainerExpectedCallableException(ContainerException): """Raised when trying to call non callable object.""" pass
[docs]class Container: """Instantiates the container. Objects and parameters can be passed as argument to the constructor. :type args: dict :param args: The parameters or objects. :raise ContainerTypeException: Handle wrong input data type. """ def __init__(self, args=None): self._data = {} self._frozen = set() self._raw = {} self._protected = set() if args is None: args = {} elif not isinstance(args, dict): raise ContainerTypeException(type(args)) for key in args: self[key] = args[key]
[docs] def __setitem__(self, key, value): """Sets a parameter or an object. Objects must be defined as Closures. Allowing any python callable leads to difficult to debug problems as function names (strings) are callable (creating a function with the same name as an existing parameter would break your container). :type key: str :param key: The unique identifier for the parameter or object. :param value: The value or a closure of the parameter. :raise ContainerFrozenServiceException: Prevent override of a frozen data. """ if key in self._frozen: raise ContainerFrozenServiceException(key) self._data[key] = value
[docs] def __getitem__(self, key): """Gets a parameter or an object. :type key: string :param key: The unique identifier for the parameter or object. :return: The value of the parameter or an object. :raise ContainerKeyException: Do not allow to get out of scope element. """ if key not in self._data: raise ContainerKeyException(key) raw = self._data[key] if any([ not callable(raw), key in self._frozen, raw in self._raw, raw in self._protected, ]): return raw self._raw[key] = raw self._frozen.add(key) val = self._data[key] = raw(self) return val
[docs] def keys(self): """Return set-like object providing a view on container keys. :rtype: set :return: Container keys list. """ return self._data.keys()
[docs] def __len__(self): """Count elements of an object. :rtype: int :return: The custom count as an integer. """ return len(self._data)
[docs] def __delitem__(self, key): """Unset element from container :type key: string :param key: The unique identifier for the parameter or object. :raise ContainerKeyException: Do not allow to get out of scope element. """ if key not in self._data.keys(): raise ContainerKeyException(key) raw = self._data[key] del self._data[key] if key in self._frozen: self._frozen.remove(key) if key in self._raw: del self._raw[key] if raw in self._protected: self._protected.remove(raw)
[docs] def clear(self): """Remove all items from container. """ self._data.clear() self._frozen.clear() self._raw.clear() self._protected.clear()
# self._factories.clear()
[docs] def __iter__(self): """Get an iterator from an Container object. :rtype: iterator :return: Iterator object that loops through each element in the object. """ return iter(self._data)
[docs] def raw(self, key): """Gets a parameter or the closure defining an object. :type key: string :param key: The unique identifier for the parameter or object. :return: The value of the parameter or the closure defining an object: :raise ContainerKeyException: Do not allow to get out of scope element. """ if key not in self._data: raise ContainerKeyException(key) if key in self._raw: return self._raw[key] return self._data[key]
[docs] def protect(self, call): """Protects a callable from being interpreted as a service. This is useful when you want to store a callable as a parameter. :type call: function :param call: A callable to protect from being evaluated. :rtype: callable :return: The passed callable. :raise ContainerExpectedCallableException: Protect from call non callable object. """ if not callable(call): raise ContainerExpectedCallableException(call) self._protected.add(call) return call
[docs] def factory(self, call): """Marks a callable as being a factory service. :type call: function :param call: A service definition to be used as a factory. :rtype callable :return: The passed callable :raise ContainerExpectedCallableException: Protect from call non callable object. """ raise NotImplementedError
[docs] def extend(self, key, call): """Extends an object definition. Useful when you want to extend an existing object definition, without necessarily loading that object. :type key: string :param key: The unique identifier for the parameter or object. :type call: callable :param call: The passed callable. :rtype: callable :return: The wrapped callable. :raise ContainerFrozenServiceException: Prevent extend of a frozen data. :raise ContainerExpectedCallableException: Protect from call non callable object. """ if not callable(call): raise ContainerExpectedCallableException(call) if key in self._frozen: raise ContainerFrozenServiceException(key) factory = self.raw(key) self[key] = lambda c: call(factory(c), c)