diff options
Diffstat (limited to 'netmodel/model/attribute.py')
-rw-r--r-- | netmodel/model/attribute.py | 120 |
1 files changed, 45 insertions, 75 deletions
diff --git a/netmodel/model/attribute.py b/netmodel/model/attribute.py index b69ee1bf..b2fa2331 100644 --- a/netmodel/model/attribute.py +++ b/netmodel/model/attribute.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python2 # -*- coding: utf-8 -*- # # Copyright (c) 2017 Cisco and/or its affiliates. @@ -22,11 +22,10 @@ import logging import operator import types -from netmodel.model.mapper import ObjectSpecification -from netmodel.model.type import is_type -from netmodel.util.meta import inheritors -from netmodel.util.misc import is_iterable -from vicn.core.sa_collections import InstrumentedList, _list_decorators +from netmodel.model.mapper import ObjectSpecification +from netmodel.model.type import Type, Self +from netmodel.util.misc import is_iterable +from netmodel.model.collection import Collection log = logging.getLogger(__name__) instance_dict = operator.attrgetter('__dict__') @@ -38,26 +37,25 @@ class NEVER_SET: None #------------------------------------------------------------------------------ class Multiplicity: - _1_1 = '1_1' - _1_N = '1_N' - _N_1 = 'N_1' - _N_N = 'N_N' - + OneToOne = '1_1' + OneToMany = '1_N' + ManyToOne = 'N_1' + ManyToMany = 'N_N' @staticmethod def reverse(value): reverse_map = { - Multiplicity._1_1: Multiplicity._1_1, - Multiplicity._1_N: Multiplicity._N_1, - Multiplicity._N_1: Multiplicity._1_N, - Multiplicity._N_N: Multiplicity._N_N, + Multiplicity.OneToOne: Multiplicity.OneToOne, + Multiplicity.OneToMany: Multiplicity.ManyToOne, + Multiplicity.ManyToOne: Multiplicity.OneToMany, + Multiplicity.ManyToMany: Multiplicity.ManyToMany, } return reverse_map[value] # Default attribute properties values (default to None) DEFAULT = { - 'multiplicity' : Multiplicity._1_1, + 'multiplicity' : Multiplicity.OneToOne, 'mandatory' : False, } @@ -71,33 +69,36 @@ class Attribute(abc.ABC, ObjectSpecification): 'type', 'description', 'default', - 'choices', 'mandatory', 'multiplicity', 'ro', 'auto', 'func', - 'requirements', 'reverse_name', 'reverse_description', 'reverse_auto' ] def __init__(self, *args, **kwargs): - for key in Attribute.properties: + for key in kwargs.keys(): + if not key in self.properties: + raise ValueError("Invalid attribute property {}".format(key)) + for key in self.properties: value = kwargs.pop(key, NEVER_SET) setattr(self, key, value) if len(args) == 1: self.type, = args - elif len(args) == 2: - self.name, self.type = args - assert is_type(self.type) + # self.type is optional since the type can be inherited. Although we + # will have to verify the attribute is complete at some point + if isinstance(self.type, str): + self.type = Type.from_string(self.type) + assert self.type is Self or Type.exists(self.type) self.is_aggregate = False self._reverse_attributes = list() - + #-------------------------------------------------------------------------- # Display #-------------------------------------------------------------------------- @@ -107,6 +108,15 @@ class Attribute(abc.ABC, ObjectSpecification): __str__ = __repr__ + # The following functions are required to allow comparing attributes, and + # using them as dict keys + + def __eq__(self, other): + return self.name == other.name + + def __hash__(self): + return hash(self.name) + #-------------------------------------------------------------------------- # Descriptor protocol # @@ -118,7 +128,7 @@ class Attribute(abc.ABC, ObjectSpecification): return self value = instance_dict(instance).get(self.name, NEVER_SET) - + # Case : collection attribute if self.is_collection: if value is NEVER_SET: @@ -126,12 +136,12 @@ class Attribute(abc.ABC, ObjectSpecification): default = self.default(instance) else: default = self.default - value = InstrumentedList(default) + value = Collection(default) value._attribute = self value._instance = instance self.__set__(instance, value) return value - return value + return value # Case : scalar attribute @@ -159,8 +169,8 @@ class Attribute(abc.ABC, ObjectSpecification): return if self.is_collection: - if not isinstance(value, InstrumentedList): - value = InstrumentedList(value) + if not isinstance(value, Collection): + value = Collection(value) value._attribute = self value._instance = instance @@ -172,6 +182,10 @@ class Attribute(abc.ABC, ObjectSpecification): def __delete__(self, instance): raise NotImplementedError + def __set_name__(self, owner, name): + self.name = name + self.owner = owner + #-------------------------------------------------------------------------- # Accessors #-------------------------------------------------------------------------- @@ -184,17 +198,16 @@ class Attribute(abc.ABC, ObjectSpecification): return DEFAULT.get(name, None) return value - # Shortcuts - def has_reverse_attribute(self): return self.reverse_name and self.multiplicity @property def is_collection(self): - return self.multiplicity in (Multiplicity._1_N, Multiplicity._N_N) + return self.multiplicity in (Multiplicity.OneToMany, + Multiplicity.ManyToMany) def is_set(self, instance): - return self.name in instance_dict(instance) + return instance.is_set(self.name) #-------------------------------------------------------------------------- # Operations @@ -217,46 +230,3 @@ class Attribute(abc.ABC, ObjectSpecification): value.extend(parent_value) else: setattr(self, prop, parent_value) - - #-------------------------------------------------------------------------- - # Attribute values - #-------------------------------------------------------------------------- - - def _handle_getitem(self, instance, item): - return item - - def _handle_add(self, instance, item): - instance._state.dirty = True - instance._state.attr_dirty.add(self.name) - print('marking', self.name, 'as dirty') - return item - - def _handle_remove(self, instance, item): - instance._state.dirty = True - instance._state.attr_dirty.add(self.name) - print('marking', self.name, 'as dirty') - - def _handle_before_remove(self, instance): - pass - - #-------------------------------------------------------------------------- - # Attribute values - #-------------------------------------------------------------------------- - -class Relation(Attribute): - properties = Attribute.properties[:] - properties.extend([ - 'reverse_name', - 'reverse_description', - 'multiplicity', - ]) - -class SelfRelation(Relation): - def __init__(self, *args, **kwargs): - if args: - if not len(args) == 1: - raise ValueError('Bad initialized for SelfRelation') - name, = args - super().__init__(name, None, *args, **kwargs) - else: - super().__init__(None, *args, **kwargs) |