Commit 0b005a39 by Simon János

actually the whole code has been refactored

parent eed8e4b7
import copy
import json
import uuid import uuid
from enum import Enum from enum import Enum
from voluptuous import Schema, Invalid, MultipleInvalid from voluptuous import Schema, Invalid, MultipleInvalid, Any, ALLOW_EXTRA, PREVENT_EXTRA
class Resource(object): class Resource(object):
DEFAULT_ID_LENGTH = 8 DEFAULT_ID_LENGTH = 8
ADDITIONAL_SCHEMA = {}
def __new__(cls, *args, **kwargs):
attributes = cls.__attributes_from_dict(args[0] if len(args) else kwargs)
resource_type = attributes['type'].value
if resource_type is Resource:
raise InvalidResourceException('Resource class is abstract')
cls.__validate_attributes(attributes, cls.__schema(resource_type, extend=True))
instance = super(Resource, resource_type).__new__(resource_type)
instance._attributes = attributes # pylint: disable=protected-access
return instance
def __init__(self, *args, **kwargs): @classmethod
if len(args): def __attributes_from_dict(cls, attributes):
resource = args[0] cls.__validate_attributes(attributes, cls.__schema(extend=False))
if isinstance(resource, Resource): attributes = copy.deepcopy(attributes)
self.__id = resource.id attributes['id'] = attributes.get('id', Resource.__generate_random_id())
self.__type = Resource.Type[resource.type] attributes['type'] = ResourceType.validate(attributes.get('type', cls))
else: return attributes
self.__attributes_from_dict(resource)
else: @classmethod
self.__attributes_from_dict(kwargs) def __validate_attributes(cls, attributes, schema):
def __attributes_from_dict(self, attributes):
schema = Schema({
'id': str,
'type': Resource.Type.validate
})
try: try:
schema(attributes) schema(attributes)
self.__id = attributes.get('id', Resource._random_id())
self.__type = Resource.Type[attributes.get('type', 'null')]
except MultipleInvalid as invalid: except MultipleInvalid as invalid:
raise InvalidResourceException(invalid) raise InvalidResourceException(invalid)
@property @classmethod
def id(self): def __schema(cls, resource_type=None, extend=False):
return self.__id try:
string_validator = Any(str, unicode)
except NameError:
string_validator = Any(str)
schema = Schema(schema={'id': string_validator, 'type': ResourceType.validate}, extra=ALLOW_EXTRA)
if extend:
additional = resource_type.ADDITIONAL_SCHEMA if resource_type else cls.ADDITIONAL_SCHEMA
schema = schema.extend(additional, extra=PREVENT_EXTRA)
return schema
@property @property
def type(self): def type(self):
return self.__type.name return str(self._attributes['type'])
def __getattr__(self, item):
try:
return self._attributes[item]
except KeyError:
raise AttributeError('Attribute (%s) is not set on resource')
@staticmethod @staticmethod
def _random_id(): def __generate_random_id():
return uuid.uuid4().hex[:Resource.DEFAULT_ID_LENGTH] return uuid.uuid4().hex[:Resource.DEFAULT_ID_LENGTH]
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, Resource) and \ try:
self.id == other.id and \ for attr, other_value in other.items():
self.type == other.type value = self._attributes.get(attr)
if value is None or value != other_value:
return False
return True
except AttributeError:
return False
def __repr__(self): def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, self.id) return json.dumps(self, default=Resource.json_encoder)
def __hash__(self): def __iter__(self):
return hash(self.__repr__()) return self._attributes.__iter__()
class Type(Enum): def keys(self):
null = 0 return self._attributes.keys()
instance = 1
@classmethod def values(self):
def validate(cls, value): return self._attributes.values()
try:
cls[value]
except KeyError:
raise Invalid('The given resource type (%s) is not valid' % value)
return True
def items(self):
return self._attributes.items()
class Instance(Resource): def __getitem__(self, item):
def __init__(self, *args, **kwargs): return self._attributes[item]
kwargs['type'] = Resource.Type.instance.name
super(Instance, self).__init__(*args, **kwargs)
def __add__(self, other):
return ResourceGroup(resources=[self, other])
@staticmethod
def json_encoder(data):
if isinstance(data, ResourceType):
return str(data)
return dict(data)
class ResourceGroup(Resource):
ADDITIONAL_SCHEMA = {
'resources': Any(list, set, tuple)
}
def __init__(self, *_, **__):
self.__resources = {}
if 'resources' in self._attributes.keys():
for resource in self._attributes['resources']:
self.add(resource)
self._attributes['resources'] = self.resources
def add(self, resource):
if not isinstance(resource, Resource):
try:
resource = Resource(resource)
except InvalidResourceException:
raise InvalidResourceException('ResourceGroup can only store resources')
self.__resources[resource.id] = resource
class ResourceGroup(object): def remove(self, resource):
def __init__(self, *args):
self.__resources = set()
if len(args):
for argument in args:
try: try:
self.__iadd__(argument) del self.__resources[Resource(resource).id]
except InvalidResourceException: except InvalidResourceException:
for resource in argument: del self.__resources[resource.id]
self.__iadd__(resource)
@property @property
def resources(self): def resources(self):
return list(self.__resources) return list(self.__resources.values())
def __add__(self, other):
if isinstance(other, dict):
return ResourceGroup(Resource(other), *self.resources)
if isinstance(other, Resource):
return ResourceGroup(other, *self.resources)
if isinstance(other, ResourceGroup):
resources = other.resources + self.resources
return ResourceGroup(*resources)
raise InvalidResourceException()
def __iadd__(self, other):
if isinstance(other, dict):
self.__resources.add(Resource(other))
return self
if isinstance(other, Resource):
self.__resources.add(other)
return self
if isinstance(other, ResourceGroup):
self.__resources.union(other.resources)
return self
raise InvalidResourceException()
def __iter__(self): class Instance(Resource):
return self.__resources.__iter__() ADDITIONAL_SCHEMA = {
'number_of_cores': int
}
def __eq__(self, other):
return self.__resources == set(other.resources) class ResourceType(Enum):
null = Resource
group = ResourceGroup
instance = Instance
@classmethod
def validate(cls, value):
if isinstance(value, cls):
return value
try:
return cls[value]
except KeyError:
return cls(value)
except ValueError:
raise Invalid('The given resource type (%s) is not valid' % value)
def __str__(self):
return str(self.name)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, str(self))
class InvalidResourceException(Exception): class InvalidResourceException(Exception):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment