Ordereddict exploses with python2.6 ...

Use the official backport
This commit is contained in:
Jerome Kieffer 2013-10-17 20:19:30 +02:00
parent f6ed67fc33
commit 4052c3e9d1
2 changed files with 208 additions and 147 deletions

127
pyqtgraph/ordereddict.py Normal file
View File

@ -0,0 +1,127 @@
# Copyright (c) 2009 Raymond Hettinger
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
from UserDict import DictMixin
class OrderedDict(dict, DictMixin):
def __init__(self, *args, **kwds):
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
try:
self.__end
except AttributeError:
self.clear()
self.update(*args, **kwds)
def clear(self):
self.__end = end = []
end += [None, end, end] # sentinel node for doubly linked list
self.__map = {} # key --> [key, prev, next]
dict.clear(self)
def __setitem__(self, key, value):
if key not in self:
end = self.__end
curr = end[1]
curr[2] = end[1] = self.__map[key] = [key, curr, end]
dict.__setitem__(self, key, value)
def __delitem__(self, key):
dict.__delitem__(self, key)
key, prev, next = self.__map.pop(key)
prev[2] = next
next[1] = prev
def __iter__(self):
end = self.__end
curr = end[2]
while curr is not end:
yield curr[0]
curr = curr[2]
def __reversed__(self):
end = self.__end
curr = end[1]
while curr is not end:
yield curr[0]
curr = curr[1]
def popitem(self, last=True):
if not self:
raise KeyError('dictionary is empty')
if last:
key = reversed(self).next()
else:
key = iter(self).next()
value = self.pop(key)
return key, value
def __reduce__(self):
items = [[k, self[k]] for k in self]
tmp = self.__map, self.__end
del self.__map, self.__end
inst_dict = vars(self).copy()
self.__map, self.__end = tmp
if inst_dict:
return (self.__class__, (items,), inst_dict)
return self.__class__, (items,)
def keys(self):
return list(self)
setdefault = DictMixin.setdefault
update = DictMixin.update
pop = DictMixin.pop
values = DictMixin.values
items = DictMixin.items
iterkeys = DictMixin.iterkeys
itervalues = DictMixin.itervalues
iteritems = DictMixin.iteritems
def __repr__(self):
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, self.items())
def copy(self):
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
d = cls()
for key in iterable:
d[key] = value
return d
def __eq__(self, other):
if isinstance(other, OrderedDict):
if len(self) != len(other):
return False
for p, q in zip(self.items(), other.items()):
if p != q:
return False
return True
return dict.__eq__(self, other)
def __ne__(self, other):
return not self == other

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
advancedTypes.py - Basic data structures not included with python advancedTypes.py - Basic data structures not included with python
Copyright 2010 Luke Campagnola Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more infomation. Distributed under MIT/X11 license. See license.txt for more infomation.
@ -15,76 +15,10 @@ import threading, sys, copy, collections
try: try:
from collections import OrderedDict from collections import OrderedDict
except: except ImportError:
# Deprecated; this class is now present in Python 2.7 as collections.OrderedDict
# Only keeping this around for python2.6 support. # Only keeping this around for python2.6 support.
class OrderedDict(dict): from ordereddict import OrderedDict
"""extends dict so that elements are iterated in the order that they were added.
Since this class can not be instantiated with regular dict notation, it instead uses
a list of tuples:
od = OrderedDict([(key1, value1), (key2, value2), ...])
items set using __setattr__ are added to the end of the key list.
"""
def __init__(self, data=None):
self.order = []
if data is not None:
for i in data:
self[i[0]] = i[1]
def __setitem__(self, k, v):
if not self.has_key(k):
self.order.append(k)
dict.__setitem__(self, k, v)
def __delitem__(self, k):
self.order.remove(k)
dict.__delitem__(self, k)
def keys(self):
return self.order[:]
def items(self):
it = []
for k in self.keys():
it.append((k, self[k]))
return it
def values(self):
return [self[k] for k in self.order]
def remove(self, key):
del self[key]
#self.order.remove(key)
def __iter__(self):
for k in self.order:
yield k
def update(self, data):
"""Works like dict.update, but accepts list-of-tuples as well as dict."""
if isinstance(data, dict):
for k, v in data.iteritems():
self[k] = v
else:
for k,v in data:
self[k] = v
def copy(self):
return OrderedDict(self.items())
def itervalues(self):
for k in self.order:
yield self[k]
def iteritems(self):
for k in self.order:
yield (k, self[k])
def __deepcopy__(self, memo):
return OrderedDict([(k, copy.deepcopy(v, memo)) for k, v in self.iteritems()])
class ReverseDict(dict): class ReverseDict(dict):
"""extends dict so that reverse lookups are possible by requesting the key as a list of length 1: """extends dict so that reverse lookups are possible by requesting the key as a list of length 1:
@ -101,7 +35,7 @@ class ReverseDict(dict):
for k in data: for k in data:
self.reverse[data[k]] = k self.reverse[data[k]] = k
dict.__init__(self, data) dict.__init__(self, data)
def __getitem__(self, item): def __getitem__(self, item):
if type(item) is list: if type(item) is list:
return self.reverse[item[0]] return self.reverse[item[0]]
@ -114,8 +48,8 @@ class ReverseDict(dict):
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
raise Exception("deepcopy not implemented") raise Exception("deepcopy not implemented")
class BiDict(dict): class BiDict(dict):
"""extends dict so that reverse lookups are possible by adding each reverse combination to the dict. """extends dict so that reverse lookups are possible by adding each reverse combination to the dict.
This only works if all values and keys are unique.""" This only works if all values and keys are unique."""
@ -125,11 +59,11 @@ class BiDict(dict):
dict.__init__(self) dict.__init__(self)
for k in data: for k in data:
self[data[k]] = k self[data[k]] = k
def __setitem__(self, item, value): def __setitem__(self, item, value):
dict.__setitem__(self, item, value) dict.__setitem__(self, item, value)
dict.__setitem__(self, value, item) dict.__setitem__(self, value, item)
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
raise Exception("deepcopy not implemented") raise Exception("deepcopy not implemented")
@ -138,7 +72,7 @@ class ThreadsafeDict(dict):
Also adds lock/unlock functions for extended exclusive operations Also adds lock/unlock functions for extended exclusive operations
Converts all sub-dicts and lists to threadsafe as well. Converts all sub-dicts and lists to threadsafe as well.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.mutex = threading.RLock() self.mutex = threading.RLock()
dict.__init__(self, *args, **kwargs) dict.__init__(self, *args, **kwargs)
@ -162,7 +96,7 @@ class ThreadsafeDict(dict):
dict.__setitem__(self, attr, val) dict.__setitem__(self, attr, val)
finally: finally:
self.unlock() self.unlock()
def __contains__(self, attr): def __contains__(self, attr):
self.lock() self.lock()
try: try:
@ -188,19 +122,19 @@ class ThreadsafeDict(dict):
def lock(self): def lock(self):
self.mutex.acquire() self.mutex.acquire()
def unlock(self): def unlock(self):
self.mutex.release() self.mutex.release()
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
raise Exception("deepcopy not implemented") raise Exception("deepcopy not implemented")
class ThreadsafeList(list): class ThreadsafeList(list):
"""Extends list so that getitem, setitem, and contains are all thread-safe. """Extends list so that getitem, setitem, and contains are all thread-safe.
Also adds lock/unlock functions for extended exclusive operations Also adds lock/unlock functions for extended exclusive operations
Converts all sub-lists and dicts to threadsafe as well. Converts all sub-lists and dicts to threadsafe as well.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.mutex = threading.RLock() self.mutex = threading.RLock()
list.__init__(self, *args, **kwargs) list.__init__(self, *args, **kwargs)
@ -222,7 +156,7 @@ class ThreadsafeList(list):
list.__setitem__(self, attr, val) list.__setitem__(self, attr, val)
finally: finally:
self.unlock() self.unlock()
def __contains__(self, attr): def __contains__(self, attr):
self.lock() self.lock()
try: try:
@ -238,17 +172,17 @@ class ThreadsafeList(list):
finally: finally:
self.unlock() self.unlock()
return val return val
def lock(self): def lock(self):
self.mutex.acquire() self.mutex.acquire()
def unlock(self): def unlock(self):
self.mutex.release() self.mutex.release()
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
raise Exception("deepcopy not implemented") raise Exception("deepcopy not implemented")
def makeThreadsafe(obj): def makeThreadsafe(obj):
if type(obj) is dict: if type(obj) is dict:
return ThreadsafeDict(obj) return ThreadsafeDict(obj)
@ -258,8 +192,8 @@ def makeThreadsafe(obj):
return obj return obj
else: else:
raise Exception("Not sure how to make object of type %s thread-safe" % str(type(obj))) raise Exception("Not sure how to make object of type %s thread-safe" % str(type(obj)))
class Locker(object): class Locker(object):
def __init__(self, lock): def __init__(self, lock):
self.lock = lock self.lock = lock
@ -283,10 +217,10 @@ class CaselessDict(OrderedDict):
self[k] = args[0][k] self[k] = args[0][k]
else: else:
raise Exception("CaselessDict may only be instantiated with a single dict.") raise Exception("CaselessDict may only be instantiated with a single dict.")
#def keys(self): #def keys(self):
#return self.keyMap.values() #return self.keyMap.values()
def __setitem__(self, key, val): def __setitem__(self, key, val):
kl = key.lower() kl = key.lower()
if kl in self.keyMap: if kl in self.keyMap:
@ -294,30 +228,30 @@ class CaselessDict(OrderedDict):
else: else:
OrderedDict.__setitem__(self, key, val) OrderedDict.__setitem__(self, key, val)
self.keyMap[kl] = key self.keyMap[kl] = key
def __getitem__(self, key): def __getitem__(self, key):
kl = key.lower() kl = key.lower()
if kl not in self.keyMap: if kl not in self.keyMap:
raise KeyError(key) raise KeyError(key)
return OrderedDict.__getitem__(self, self.keyMap[kl]) return OrderedDict.__getitem__(self, self.keyMap[kl])
def __contains__(self, key): def __contains__(self, key):
return key.lower() in self.keyMap return key.lower() in self.keyMap
def update(self, d): def update(self, d):
for k, v in d.iteritems(): for k, v in d.iteritems():
self[k] = v self[k] = v
def copy(self): def copy(self):
return CaselessDict(OrderedDict.copy(self)) return CaselessDict(OrderedDict.copy(self))
def __delitem__(self, key): def __delitem__(self, key):
kl = key.lower() kl = key.lower()
if kl not in self.keyMap: if kl not in self.keyMap:
raise KeyError(key) raise KeyError(key)
OrderedDict.__delitem__(self, self.keyMap[kl]) OrderedDict.__delitem__(self, self.keyMap[kl])
del self.keyMap[kl] del self.keyMap[kl]
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
raise Exception("deepcopy not implemented") raise Exception("deepcopy not implemented")
@ -329,34 +263,34 @@ class CaselessDict(OrderedDict):
class ProtectedDict(dict): class ProtectedDict(dict):
""" """
A class allowing read-only 'view' of a dict. A class allowing read-only 'view' of a dict.
The object can be treated like a normal dict, but will never modify the original dict it points to. The object can be treated like a normal dict, but will never modify the original dict it points to.
Any values accessed from the dict will also be read-only. Any values accessed from the dict will also be read-only.
""" """
def __init__(self, data): def __init__(self, data):
self._data_ = data self._data_ = data
## List of methods to directly wrap from _data_ ## List of methods to directly wrap from _data_
wrapMethods = ['_cmp_', '__contains__', '__eq__', '__format__', '__ge__', '__gt__', '__le__', '__len__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__repr__', '__str__', 'count', 'has_key', 'iterkeys', 'keys', ] wrapMethods = ['_cmp_', '__contains__', '__eq__', '__format__', '__ge__', '__gt__', '__le__', '__len__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__repr__', '__str__', 'count', 'has_key', 'iterkeys', 'keys', ]
## List of methods which wrap from _data_ but return protected results ## List of methods which wrap from _data_ but return protected results
protectMethods = ['__getitem__', '__iter__', 'get', 'items', 'values'] protectMethods = ['__getitem__', '__iter__', 'get', 'items', 'values']
## List of methods to disable ## List of methods to disable
disableMethods = ['__delitem__', '__setitem__', 'clear', 'pop', 'popitem', 'setdefault', 'update'] disableMethods = ['__delitem__', '__setitem__', 'clear', 'pop', 'popitem', 'setdefault', 'update']
## Template methods # # Template methods
def wrapMethod(methodName): def wrapMethod(methodName):
return lambda self, *a, **k: getattr(self._data_, methodName)(*a, **k) return lambda self, *a, **k: getattr(self._data_, methodName)(*a, **k)
def protectMethod(methodName): def protectMethod(methodName):
return lambda self, *a, **k: protect(getattr(self._data_, methodName)(*a, **k)) return lambda self, *a, **k: protect(getattr(self._data_, methodName)(*a, **k))
def error(self, *args, **kargs): def error(self, *args, **kargs):
raise Exception("Can not modify read-only list.") raise Exception("Can not modify read-only list.")
## Directly (and explicitly) wrap some methods from _data_ ## Directly (and explicitly) wrap some methods from _data_
## Many of these methods can not be intercepted using __getattribute__, so they ## Many of these methods can not be intercepted using __getattribute__, so they
## must be implemented explicitly ## must be implemented explicitly
@ -371,33 +305,33 @@ class ProtectedDict(dict):
for methodName in disableMethods: for methodName in disableMethods:
locals()[methodName] = error locals()[methodName] = error
## Add a few extra methods. ## Add a few extra methods.
def copy(self): def copy(self):
raise Exception("It is not safe to copy protected dicts! (instead try deepcopy, but be careful.)") raise Exception("It is not safe to copy protected dicts! (instead try deepcopy, but be careful.)")
def itervalues(self): def itervalues(self):
for v in self._data_.itervalues(): for v in self._data_.itervalues():
yield protect(v) yield protect(v)
def iteritems(self): def iteritems(self):
for k, v in self._data_.iteritems(): for k, v in self._data_.iteritems():
yield (k, protect(v)) yield (k, protect(v))
def deepcopy(self): def deepcopy(self):
return copy.deepcopy(self._data_) return copy.deepcopy(self._data_)
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
return copy.deepcopy(self._data_, memo) return copy.deepcopy(self._data_, memo)
class ProtectedList(collections.Sequence): class ProtectedList(collections.Sequence):
""" """
A class allowing read-only 'view' of a list or dict. A class allowing read-only 'view' of a list or dict.
The object can be treated like a normal list, but will never modify the original list it points to. The object can be treated like a normal list, but will never modify the original list it points to.
Any values accessed from the list will also be read-only. Any values accessed from the list will also be read-only.
Note: It would be nice if we could inherit from list or tuple so that isinstance checks would work. Note: It would be nice if we could inherit from list or tuple so that isinstance checks would work.
However, doing this causes tuple(obj) to return unprotected results (importantly, this means However, doing this causes tuple(obj) to return unprotected results (importantly, this means
unpacking into function arguments will also fail) unpacking into function arguments will also fail)
@ -405,28 +339,28 @@ class ProtectedList(collections.Sequence):
def __init__(self, data): def __init__(self, data):
self._data_ = data self._data_ = data
#self.__mro__ = (ProtectedList, object) #self.__mro__ = (ProtectedList, object)
## List of methods to directly wrap from _data_ ## List of methods to directly wrap from _data_
wrapMethods = ['__contains__', '__eq__', '__format__', '__ge__', '__gt__', '__le__', '__len__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__repr__', '__str__', 'count', 'index'] wrapMethods = ['__contains__', '__eq__', '__format__', '__ge__', '__gt__', '__le__', '__len__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__repr__', '__str__', 'count', 'index']
## List of methods which wrap from _data_ but return protected results ## List of methods which wrap from _data_ but return protected results
protectMethods = ['__getitem__', '__getslice__', '__mul__', '__reversed__', '__rmul__'] protectMethods = ['__getitem__', '__getslice__', '__mul__', '__reversed__', '__rmul__']
## List of methods to disable ## List of methods to disable
disableMethods = ['__delitem__', '__delslice__', '__iadd__', '__imul__', '__setitem__', '__setslice__', 'append', 'extend', 'insert', 'pop', 'remove', 'reverse', 'sort'] disableMethods = ['__delitem__', '__delslice__', '__iadd__', '__imul__', '__setitem__', '__setslice__', 'append', 'extend', 'insert', 'pop', 'remove', 'reverse', 'sort']
## Template methods # # Template methods
def wrapMethod(methodName): def wrapMethod(methodName):
return lambda self, *a, **k: getattr(self._data_, methodName)(*a, **k) return lambda self, *a, **k: getattr(self._data_, methodName)(*a, **k)
def protectMethod(methodName): def protectMethod(methodName):
return lambda self, *a, **k: protect(getattr(self._data_, methodName)(*a, **k)) return lambda self, *a, **k: protect(getattr(self._data_, methodName)(*a, **k))
def error(self, *args, **kargs): def error(self, *args, **kargs):
raise Exception("Can not modify read-only list.") raise Exception("Can not modify read-only list.")
## Directly (and explicitly) wrap some methods from _data_ ## Directly (and explicitly) wrap some methods from _data_
## Many of these methods can not be intercepted using __getattribute__, so they ## Many of these methods can not be intercepted using __getattribute__, so they
## must be implemented explicitly ## must be implemented explicitly
@ -441,13 +375,13 @@ class ProtectedList(collections.Sequence):
for methodName in disableMethods: for methodName in disableMethods:
locals()[methodName] = error locals()[methodName] = error
## Add a few extra methods. ## Add a few extra methods.
def __iter__(self): def __iter__(self):
for item in self._data_: for item in self._data_:
yield protect(item) yield protect(item)
def __add__(self, op): def __add__(self, op):
if isinstance(op, ProtectedList): if isinstance(op, ProtectedList):
return protect(self._data_.__add__(op._data_)) return protect(self._data_.__add__(op._data_))
@ -455,7 +389,7 @@ class ProtectedList(collections.Sequence):
return protect(self._data_.__add__(op)) return protect(self._data_.__add__(op))
else: else:
raise TypeError("Argument must be a list.") raise TypeError("Argument must be a list.")
def __radd__(self, op): def __radd__(self, op):
if isinstance(op, ProtectedList): if isinstance(op, ProtectedList):
return protect(op._data_.__add__(self._data_)) return protect(op._data_.__add__(self._data_))
@ -463,13 +397,13 @@ class ProtectedList(collections.Sequence):
return protect(op.__add__(self._data_)) return protect(op.__add__(self._data_))
else: else:
raise TypeError("Argument must be a list.") raise TypeError("Argument must be a list.")
def deepcopy(self): def deepcopy(self):
return copy.deepcopy(self._data_) return copy.deepcopy(self._data_)
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
return copy.deepcopy(self._data_, memo) return copy.deepcopy(self._data_, memo)
def poop(self): def poop(self):
raise Exception("This is a list. It does not poop.") raise Exception("This is a list. It does not poop.")
@ -478,29 +412,29 @@ class ProtectedTuple(collections.Sequence):
""" """
A class allowing read-only 'view' of a tuple. A class allowing read-only 'view' of a tuple.
The object can be treated like a normal tuple, but its contents will be returned as protected objects. The object can be treated like a normal tuple, but its contents will be returned as protected objects.
Note: It would be nice if we could inherit from list or tuple so that isinstance checks would work. Note: It would be nice if we could inherit from list or tuple so that isinstance checks would work.
However, doing this causes tuple(obj) to return unprotected results (importantly, this means However, doing this causes tuple(obj) to return unprotected results (importantly, this means
unpacking into function arguments will also fail) unpacking into function arguments will also fail)
""" """
def __init__(self, data): def __init__(self, data):
self._data_ = data self._data_ = data
## List of methods to directly wrap from _data_ ## List of methods to directly wrap from _data_
wrapMethods = ['__contains__', '__eq__', '__format__', '__ge__', '__getnewargs__', '__gt__', '__hash__', '__le__', '__len__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__repr__', '__str__', 'count', 'index'] wrapMethods = ['__contains__', '__eq__', '__format__', '__ge__', '__getnewargs__', '__gt__', '__hash__', '__le__', '__len__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__repr__', '__str__', 'count', 'index']
## List of methods which wrap from _data_ but return protected results ## List of methods which wrap from _data_ but return protected results
protectMethods = ['__getitem__', '__getslice__', '__iter__', '__add__', '__mul__', '__reversed__', '__rmul__'] protectMethods = ['__getitem__', '__getslice__', '__iter__', '__add__', '__mul__', '__reversed__', '__rmul__']
## Template methods # # Template methods
def wrapMethod(methodName): def wrapMethod(methodName):
return lambda self, *a, **k: getattr(self._data_, methodName)(*a, **k) return lambda self, *a, **k: getattr(self._data_, methodName)(*a, **k)
def protectMethod(methodName): def protectMethod(methodName):
return lambda self, *a, **k: protect(getattr(self._data_, methodName)(*a, **k)) return lambda self, *a, **k: protect(getattr(self._data_, methodName)(*a, **k))
## Directly (and explicitly) wrap some methods from _data_ ## Directly (and explicitly) wrap some methods from _data_
## Many of these methods can not be intercepted using __getattribute__, so they ## Many of these methods can not be intercepted using __getattribute__, so they
## must be implemented explicitly ## must be implemented explicitly
@ -511,14 +445,14 @@ class ProtectedTuple(collections.Sequence):
for methodName in protectMethods: for methodName in protectMethods:
locals()[methodName] = protectMethod(methodName) locals()[methodName] = protectMethod(methodName)
## Add a few extra methods. ## Add a few extra methods.
def deepcopy(self): def deepcopy(self):
return copy.deepcopy(self._data_) return copy.deepcopy(self._data_)
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
return copy.deepcopy(self._data_, memo) return copy.deepcopy(self._data_, memo)
def protect(obj): def protect(obj):
@ -530,14 +464,14 @@ def protect(obj):
return ProtectedTuple(obj) return ProtectedTuple(obj)
else: else:
return obj return obj
if __name__ == '__main__': if __name__ == '__main__':
d = {'x': 1, 'y': [1,2], 'z': ({'a': 2, 'b': [3,4], 'c': (5,6)}, 1, 2)} d = {'x': 1, 'y': [1,2], 'z': ({'a': 2, 'b': [3,4], 'c': (5,6)}, 1, 2)}
dp = protect(d) dp = protect(d)
l = [1, 'x', ['a', 'b'], ('c', 'd'), {'x': 1, 'y': 2}] l = [1, 'x', ['a', 'b'], ('c', 'd'), {'x': 1, 'y': 2}]
lp = protect(l) lp = protect(l)
t = (1, 'x', ['a', 'b'], ('c', 'd'), {'x': 1, 'y': 2}) t = (1, 'x', ['a', 'b'], ('c', 'd'), {'x': 1, 'y': 2})
tp = protect(t) tp = protect(t)