aboutsummaryrefslogtreecommitdiffstats
path: root/vicn/core/sa_collections.py
diff options
context:
space:
mode:
Diffstat (limited to 'vicn/core/sa_collections.py')
-rw-r--r--vicn/core/sa_collections.py249
1 files changed, 249 insertions, 0 deletions
diff --git a/vicn/core/sa_collections.py b/vicn/core/sa_collections.py
new file mode 100644
index 00000000..e627caa5
--- /dev/null
+++ b/vicn/core/sa_collections.py
@@ -0,0 +1,249 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# This module is derived from code from SQLAlchemy
+#
+# orm/collections.py
+# Copyright (C) 2005-2016 the SQLAlchemy authors and contributors
+#
+# This module is part of SQLAlchemy and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+#
+
+import logging
+
+from vicn.core.sa_compat import py2k
+from vicn.core.exception import VICNListException
+
+log = logging.getLogger(__name__)
+
+def _list_decorators():
+ """Tailored instrumentation wrappers for any list-like class."""
+
+ def _tidy(fn):
+ fn._sa_instrumented = True
+ fn.__doc__ = getattr(list, fn.__name__).__doc__
+
+ def append(fn):
+ def append(self, item):
+ try:
+ item = self._attribute.do_list_add(self._instance, item)
+ fn(self, item)
+ except VICNListException as e:
+ pass
+ _tidy(append)
+ return append
+
+ def remove(fn):
+ def remove(self, value):
+ # testlib.pragma exempt:__eq__
+ try:
+ self._attribute.do_list_remove(self._instance, value)
+ fn(self, value)
+ except : pass
+ _tidy(remove)
+ return remove
+
+ def insert(fn):
+ def insert(self, index, value):
+ try:
+ value = self._attribute.do_list_add(self._instance, item)
+ fn(self, index, value)
+ except : pass
+ _tidy(insert)
+ return insert
+
+ def __getitem__(fn):
+ def __getitem__(self, index):
+ item = fn(self, index)
+ return self._attribute.handle_getitem(self._instance, item)
+ _tidy(__getitem__)
+ return __getitem__
+
+ def __setitem__(fn):
+ def __setitem__(self, index, value):
+ if not isinstance(index, slice):
+ existing = self[index]
+ if existing is not None:
+ try:
+ self._attribute.do_list_remove(self._instance, existing)
+ except: pass
+ try:
+ value = self._attribute.do_list_add(self._instance, value)
+ fn(self, index, value)
+ except: pass
+ else:
+ # slice assignment requires __delitem__, insert, __len__
+ step = index.step or 1
+ start = index.start or 0
+ if start < 0:
+ start += len(self)
+ if index.stop is not None:
+ stop = index.stop
+ else:
+ stop = len(self)
+ if stop < 0:
+ stop += len(self)
+
+ if step == 1:
+ for i in range(start, stop, step):
+ if len(self) > start:
+ del self[start]
+
+ for i, item in enumerate(value):
+ self.insert(i + start, item)
+ else:
+ rng = list(range(start, stop, step))
+ if len(value) != len(rng):
+ raise ValueError(
+ "attempt to assign sequence of size %s to "
+ "extended slice of size %s" % (len(value),
+ len(rng)))
+ for i, item in zip(rng, value):
+ self.__setitem__(i, item)
+ _tidy(__setitem__)
+ return __setitem__
+
+ def __delitem__(fn):
+ def __delitem__(self, index):
+ if not isinstance(index, slice):
+ item = self[index]
+ try:
+ self._attribute.do_list_remove(self._instance, item)
+ fn(self, index)
+ except : pass
+ else:
+ # slice deletion requires __getslice__ and a slice-groking
+ # __getitem__ for stepped deletion
+ # note: not breaking this into atomic dels
+ has_except = False
+ for item in self[index]:
+ try:
+ self._attribute.do_list_remove(self._instance, item)
+ except : has_except = True
+ if not has_except:
+ fn(self, index)
+ _tidy(__delitem__)
+ return __delitem__
+
+ if py2k:
+ def __setslice__(fn):
+ def __setslice__(self, start, end, values):
+ has_except = False
+ for value in self[start:end]:
+ try:
+ self._attribute.do_list_remove(self._instance, value)
+ except : has_except = True
+ #values = [self._attribute.do_list_add(self._instance, value) for value in values]
+ _values = list()
+ for value in values:
+ try:
+ _values.append(self._attribute.do_list_add(self._instance, value))
+ except: has_except = True
+ if not has_except:
+ fn(self, start, end, _values)
+ _tidy(__setslice__)
+ return __setslice__
+
+ def __delslice__(fn):
+ def __delslice__(self, start, end):
+ has_except = False
+ for value in self[start:end]:
+ try:
+ self._attribute.do_list_remove(self._instance, value)
+ except : has_except = True
+ if not has_except:
+ fn(self, start, end)
+ _tidy(__delslice__)
+ return __delslice__
+
+ def extend(fn):
+ def extend(self, iterable):
+ for value in iterable:
+ self.append(value)
+ _tidy(extend)
+ return extend
+
+ def __iadd__(fn):
+ def __iadd__(self, iterable):
+ # list.__iadd__ takes any iterable and seems to let TypeError
+ # raise as-is instead of returning NotImplemented
+ for value in iterable:
+ self.append(value)
+ return self
+ _tidy(__iadd__)
+ return __iadd__
+
+ def pop(fn):
+ def pop(self, index=-1):
+ try:
+ self._attribute.do_list_remove(self._instance, item)
+ item = fn(self, index)
+ return item
+ except : return None
+ _tidy(pop)
+ return pop
+
+ def __iter__(fn):
+ def __iter__(self):
+ for item in fn(self):
+ yield self._attribute.handle_getitem(self._instance, item)
+ _tidy(__iter__)
+ return __iter__
+
+ def __repr__(fn):
+ def __repr__(self):
+ return '<Collection {} {}>'.format(id(self), list.__repr__(self))
+ _tidy(__repr__)
+ return __repr__
+
+ __str__ = __repr__
+ #def __str__(fn):
+ # def __str__(self):
+ # return str(list(self))
+ # _tidy(__str__)
+ # return __str__
+
+ if not py2k:
+ def clear(fn):
+ def clear(self, index=-1):
+ has_except = False
+ for item in self:
+ try:
+ self._attribute.do_list_remove(self._instance, item)
+ except : has_except = True
+ if not has_except:
+ fn(self)
+ _tidy(clear)
+ return clear
+
+ # __imul__ : not wrapping this. all members of the collection are already
+ # present, so no need to fire appends... wrapping it with an explicit
+ # decorator is still possible, so events on *= can be had if they're
+ # desired. hard to imagine a use case for __imul__, though.
+
+ l = locals().copy()
+ l.pop('_tidy')
+ return l
+
+def _instrument_list(cls):
+ # inspired by sqlalchemy
+ for method, decorator in _list_decorators().items():
+ fn = getattr(cls, method, None)
+ if fn:
+ #if (fn and method not in methods and
+ # not hasattr(fn, '_sa_instrumented')):
+ setattr(cls, method, decorator(fn))
+
+class InstrumentedList(list):
+
+ def __contains__(self, key):
+ from vicn.core.resource import Resource
+ if isinstance(key, Resource):
+ key = key.get_uuid()
+ return list.__contains__(self, key)
+
+ def __lshift__(self, item):
+ self.append(item)
+
+_instrument_list(InstrumentedList)