diff --git a/salt/state.py b/salt/state.py index 9390cb3a5f..7089b74eec 100644 --- a/salt/state.py +++ b/salt/state.py @@ -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__'): diff --git a/salt/utils/immutabletypes.py b/salt/utils/immutabletypes.py index c482f5013b..8136bfbbcd 100644 --- a/salt/utils/immutabletypes.py +++ b/salt/utils/immutabletypes.py @@ -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 diff --git a/salt/utils/lazyproxy.py b/salt/utils/lazyproxy.py deleted file mode 100644 index 09ff58ad52..0000000000 --- a/salt/utils/lazyproxy.py +++ /dev/null @@ -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) diff --git a/tests/unit/utils/immutabletypes.py b/tests/unit/utils/immutabletypes.py index 6aaf763d33..4ac83f6c34 100644 --- a/tests/unit/utils/immutabletypes.py +++ b/tests/unit/utils/immutabletypes.py @@ -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