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