Revision 4884f187 lib/serializer.py
b/lib/serializer.py | ||
---|---|---|
166 | 166 |
Load = LoadJson |
167 | 167 |
DumpSigned = DumpSignedJson |
168 | 168 |
LoadSigned = LoadSignedJson |
169 |
|
|
170 |
|
|
171 |
class Private(object): |
|
172 |
"""Wrap a value so it is hard to leak it accidentally. |
|
173 |
|
|
174 |
>>> x = Private("foo") |
|
175 |
>>> print "Value: %s" % x |
|
176 |
Value: <redacted> |
|
177 |
>>> print "Value: {0}".format(x) |
|
178 |
Value: <redacted> |
|
179 |
>>> x.upper() == "FOO" |
|
180 |
True |
|
181 |
|
|
182 |
""" |
|
183 |
def __init__(self, item, descr="redacted"): |
|
184 |
if isinstance(item, Private): |
|
185 |
raise ValueError("Attempted to nest Private values.") |
|
186 |
self._item = item |
|
187 |
self._descr = descr |
|
188 |
|
|
189 |
def Get(self): |
|
190 |
"Return the wrapped value." |
|
191 |
return self._item |
|
192 |
|
|
193 |
def __str__(self): |
|
194 |
return "<{._descr}>".format(self) |
|
195 |
|
|
196 |
def __repr__(self): |
|
197 |
return "Private(?, descr='{._descr}')".format(self) |
|
198 |
|
|
199 |
# pylint: disable=W0212 |
|
200 |
# If it doesn't access _item directly, the call will go through __getattr__ |
|
201 |
# because this class defines __slots__ and "item" is not in it. |
|
202 |
# OTOH, if we do add it there, we'd risk shadowing an "item" attribute. |
|
203 |
def __eq__(self, other): |
|
204 |
if isinstance(other, Private): |
|
205 |
return self._item == other._item |
|
206 |
else: |
|
207 |
return self._item == other |
|
208 |
|
|
209 |
def __hash__(self): |
|
210 |
return hash(self._item) |
|
211 |
|
|
212 |
def __format__(self, *_1, **_2): |
|
213 |
return self.__str__() |
|
214 |
|
|
215 |
def __getattr__(self, attr): |
|
216 |
return Private(getattr(self._item, attr), |
|
217 |
descr="%s.%s" % (self._descr, attr)) |
|
218 |
|
|
219 |
def __call__(self, *args, **kwargs): |
|
220 |
return Private(self._item(*args, **kwargs), |
|
221 |
descr="%s()" % self._descr) |
|
222 |
|
|
223 |
# pylint: disable=R0201 |
|
224 |
# While this could get away with being a function, it needs to be a method. |
|
225 |
# Required by the copy.deepcopy function used by FillDict. |
|
226 |
def __getnewargs__(self): |
|
227 |
return tuple() |
|
228 |
|
|
229 |
def __nonzero__(self): |
|
230 |
return bool(self._item) |
|
231 |
|
|
232 |
# Get in the way of Pickle by implementing __slots__ but not __getstate__ |
|
233 |
# ...and get a performance boost, too. |
|
234 |
__slots__ = ["_item", "_descr"] |
|
235 |
|
|
236 |
|
|
237 |
class PrivateDict(dict): |
|
238 |
"""A dictionary that turns its values to private fields. |
|
239 |
|
|
240 |
>>> PrivateDict() |
|
241 |
{} |
|
242 |
>>> supersekkrit = PrivateDict({"password": "foobar"}) |
|
243 |
>>> print supersekkrit["password"] |
|
244 |
<password> |
|
245 |
>>> supersekkrit["password"].Get() |
|
246 |
'foobar' |
|
247 |
>>> supersekkrit.GetPrivate("password") |
|
248 |
'foobar' |
|
249 |
>>> supersekkrit["user"] = "eggspam" |
|
250 |
>>> supersekkrit.Unprivate() |
|
251 |
{'password': 'foobar', 'user': 'eggspam'} |
|
252 |
|
|
253 |
""" |
|
254 |
def __init__(self, data=None): |
|
255 |
dict.__init__(self) |
|
256 |
self.update(data) |
|
257 |
|
|
258 |
def __setitem__(self, item, value): |
|
259 |
if not isinstance(value, Private): |
|
260 |
if not isinstance(item, dict): |
|
261 |
value = Private(value, descr=item) |
|
262 |
else: |
|
263 |
value = PrivateDict(value) |
|
264 |
dict.__setitem__(self, item, value) |
|
265 |
|
|
266 |
# The actual conversion to Private containers is done by __setitem__ |
|
267 |
|
|
268 |
# copied straight from cpython/Lib/UserDict.py |
|
269 |
# Copyright (c) 2001-2014 Python Software Foundation; All Rights Reserved |
|
270 |
def update(self, other=None, **kwargs): |
|
271 |
# Make progressively weaker assumptions about "other" |
|
272 |
if other is None: |
|
273 |
pass |
|
274 |
elif hasattr(other, 'iteritems'): # iteritems saves memory and lookups |
|
275 |
for k, v in other.iteritems(): |
|
276 |
self[k] = v |
|
277 |
elif hasattr(other, 'keys'): |
|
278 |
for k in other.keys(): |
|
279 |
self[k] = other[k] |
|
280 |
else: |
|
281 |
for k, v in other: |
|
282 |
self[k] = v |
|
283 |
if kwargs: |
|
284 |
self.update(kwargs) |
|
285 |
|
|
286 |
def GetPrivate(self, *args): |
|
287 |
"""Like dict.get, but extracting the value in the process. |
|
288 |
|
|
289 |
Arguments are semantically equivalent to ``dict.get`` |
|
290 |
|
|
291 |
>>> PrivateDict({"foo": "bar"}).GetPrivate("foo") |
|
292 |
'bar' |
|
293 |
>>> PrivateDict({"foo": "bar"}).GetPrivate("baz", "spam") |
|
294 |
'spam' |
|
295 |
|
|
296 |
""" |
|
297 |
if len(args) == 1: |
|
298 |
key, = args |
|
299 |
return self[key].Get() |
|
300 |
elif len(args) == 2: |
|
301 |
key, default = args |
|
302 |
if key not in self: |
|
303 |
return default |
|
304 |
else: |
|
305 |
return self[key].Get() |
|
306 |
else: |
|
307 |
raise TypeError("GetPrivate() takes 2 arguments (%d given)" % len(args)) |
|
308 |
|
|
309 |
def Unprivate(self): |
|
310 |
"""Turn this dict of Private() values to a dict of values. |
|
311 |
|
|
312 |
>>> PrivateDict({"foo": "bar"}).Unprivate() |
|
313 |
{'foo': 'bar'} |
|
314 |
|
|
315 |
@rtype: dict |
|
316 |
|
|
317 |
""" |
|
318 |
returndict = {} |
|
319 |
for key in self: |
|
320 |
returndict[key] = self[key].Get() |
|
321 |
return returndict |
Also available in: Unified diff