No more proxied immutables. Add test for nested immutables.

This commit is contained in:
Pedro Algarvio 2014-03-20 23:59:38 +00:00
parent a87d4eeb68
commit 49bdbefb66
4 changed files with 65 additions and 137 deletions

View File

@ -30,9 +30,8 @@ import salt.pillar
import salt.fileclient
import salt.utils.event
import salt.syspaths as syspaths
from salt.utils import context
from salt.utils import context, immutabletypes
from salt._compat import string_types
from salt.utils.immutabletypes import ImmutableLazyProxy
from salt.template import compile_template, compile_template_str
from salt.exceptions import SaltRenderError, SaltReqTimeoutError, SaltException
from salt.utils.odict import OrderedDict, DefaultOrderedDict
@ -1335,9 +1334,9 @@ class State(object):
# the current state dictionaries.
# We pass deep copies here because we don't want any misbehaving
# state module to change these at runtime.
'__low__': ImmutableLazyProxy(low),
'__running__': ImmutableLazyProxy(running) if running else {},
'__lowstate__': ImmutableLazyProxy(chunks) if chunks else {}
'__low__': immutabletypes.freeze(low),
'__running__': immutabletypes.freeze(running) if running else {},
'__lowstate__': immutabletypes.freeze(chunks) if chunks else {}
}
if low.get('__prereq__'):

View File

@ -14,9 +14,6 @@
# Import python libs
import collections
# Import salt libs
from salt.utils.lazyproxy import LazyLoadProxy
class ImmutableDict(collections.Mapping):
'''
@ -33,7 +30,7 @@ class ImmutableDict(collections.Mapping):
return iter(self.__obj)
def __getitem__(self, key):
return ImmutableLazyProxy(self.__obj[key])
return freeze(self.__obj[key])
def __repr__(self):
return '<{0} {1}>'.format(self.__class__.__name__, repr(self.__obj))
@ -60,7 +57,7 @@ class ImmutableList(collections.Sequence):
return other + self.__obj
def __getitem__(self, key):
return ImmutableLazyProxy(self.__obj[key])
return freeze(self.__obj[key])
def __repr__(self):
return '<{0} {1}>'.format(self.__class__.__name__, repr(self.__obj))
@ -87,21 +84,14 @@ class ImmutableSet(collections.Set):
return '<{0} {1}>'.format(self.__class__.__name__, repr(self.__obj))
class ImmutableLazyProxy(LazyLoadProxy):
def freeze(obj):
'''
LazyProxy which will return an immutable type when requested
Freeze python types by turning them into immutable structures.
'''
def __init__(self, obj):
def __immutable_selection(obj):
if isinstance(obj, dict):
return ImmutableDict(obj)
if isinstance(obj, list):
return ImmutableList(obj)
if isinstance(obj, set):
return ImmutableSet(obj)
return obj
super(ImmutableLazyProxy, self).__init__(
lambda: __immutable_selection(obj)
)
if isinstance(obj, dict):
return ImmutableDict(obj)
if isinstance(obj, list):
return ImmutableList(obj)
if isinstance(obj, set):
return ImmutableSet(obj)
return obj

View File

@ -1,111 +0,0 @@
# -*- coding: utf-8 -*-
'''
:copyright: © 2006 Tomer Filiba
:copyright: © 2012 Cory Virok
:copyright: © 2014 by the SaltStack Team, see AUTHORS for more details.
:license: PSF
salt.utils.lazyproxy
~~~~~~~~~~~~~~~~~~~~
Lazy object proxying, from the recipe found at:
http://code.activestate.com/recipes/578014-lazy-load-object-proxying/
'''
class LazyLoadProxy(object):
__slots__ = ['_obj_fn', '__weakref__', '__proxy_storage']
def __init__(self, fn, storage=None):
object.__setattr__(self, '_obj_fn', fn)
object.__setattr__(self, '__proxy_storage', storage)
#
# proxying (special cases)
#
def __getattribute__(self, name):
return getattr(object.__getattribute__(self, '_obj_fn')(), name)
def __delattr__(self, name):
delattr(object.__getattribute__(self, '_obj_fn')(), name)
def __setattr__(self, name, value):
setattr(object.__getattribute__(self, '_obj_fn')(), name, value)
def __getitem__(self, index):
return object.__getattribute__(self, '_obj_fn')().__getitem__(index)
def __nonzero__(self):
return bool(object.__getattribute__(self, '_obj_fn')())
def __str__(self):
return str(object.__getattribute__(self, '_obj_fn')())
def __repr__(self):
return repr(object.__getattribute__(self, '_obj_fn')())
def __len__(self):
return len(object.__getattribute__(self, '_obj_fn')())
#
# factories
#
_special_names = [
'__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__',
'__contains__', '__delitem__', '__delslice__', '__div__', '__divmod__',
'__eq__', '__float__', '__floordiv__', '__ge__', # '__getitem__',
'__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__',
'__idiv__', '__idivmod__', '__ifloordiv__', '__ilshift__', '__imod__',
'__imul__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__',
'__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', # '__len__',
'__long__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__',
'__neg__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__',
'__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__',
'__repr__', '__reversed__', '__rfloorfiv__', '__rlshift__', '__rmod__',
'__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__',
'__rtruediv__', '__rxor__', '__setitem__', '__setslice__', '__sub__',
'__truediv__', '__xor__', 'next',
]
@classmethod
def _create_class_proxy(cls, theclass):
'''creates a proxy for the given class'''
def make_method(name):
def method(self, *args, **kw):
return getattr(object.__getattribute__(self, '_obj_fn')(), name)(*args, **kw)
return method
namespace = {}
for name in cls._special_names:
if hasattr(theclass, name):
namespace[name] = make_method(name)
return type('%s(%s)' % (cls.__name__, theclass.__name__), (cls,), namespace)
def __new__(cls, obj, *args, **kwargs):
'''
creates an proxy instance referencing `obj`. (obj, *args, **kwargs) are
passed to this class' __init__, so deriving classes can define an
__init__ method of their own.
note: _class_proxy_cache is unique per deriving class (each deriving
class must hold its own cache)
'''
try:
cache = cls.__dict__['_class_proxy_cache']
except KeyError:
cls._class_proxy_cache = cache = {}
try:
theclass = cache[obj.__class__]
except KeyError:
cache[obj.__class__] = theclass = cls._create_class_proxy(obj.__class__)
ins = object.__new__(theclass)
theclass.__init__(ins, obj, *args, **kwargs)
return ins
class Proxy(LazyLoadProxy):
def __init__(self, obj):
super(Proxy, self).__init__(lambda: obj)

View File

@ -30,6 +30,56 @@ class ImmutableTypesTestCase(TestCase):
__radd__ = lst + imt
self.assertEqual(__radd__, [4, 5, 6, 1, 2, 3])
def test_freeze_list_sum(self):
lst = [4, 5, 6]
imt = immutabletypes.freeze([1, 2, 3])
__add__ = imt + lst
self.assertEqual(__add__, [1, 2, 3, 4, 5, 6])
__radd__ = lst + imt
self.assertEqual(__radd__, [4, 5, 6, 1, 2, 3])
def test_immutablelist_imutability(self):
frozen = immutabletypes.freeze([1, 2, 3])
with self.assertRaises(TypeError):
frozen[1] = 2
def test_immutabledict_imutability(self):
data = {
1: 1,
2: 2,
3: {
3.1: 3.1,
3.2: 3.2,
3.3: {
3.31: 3.33,
3.32: 3.34,
3.33: [3.331, 3.332, 3.333]
}
},
4: [4.1, 4.2, 4.3]
}
frozen = immutabletypes.freeze(data)
with self.assertRaises(TypeError):
frozen[1] = 2
with self.assertRaises(TypeError):
fdict = frozen[3]
fdict[3.1] = 5
with self.assertRaises(TypeError):
fdict = frozen[3]
fdict[3.4] = 3.4
with self.assertRaises(TypeError):
frozen[3][3.3][3.32] = 3.99
with self.assertRaises(TypeError):
frozen[3][3.3][3.33][0] = 5
with self.assertRaises(TypeError):
flist = frozen[4]
flist[0] = 5
if __name__ == '__main__':
from integration import run_tests