structures.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. # -*- coding: utf-8 -*-
  2. """
  3. requests.structures
  4. ~~~~~~~~~~~~~~~~~~~
  5. Data structures that power Requests.
  6. """
  7. import os
  8. import collections
  9. from itertools import islice
  10. class IteratorProxy(object):
  11. """docstring for IteratorProxy"""
  12. def __init__(self, i):
  13. self.i = i
  14. # self.i = chain.from_iterable(i)
  15. def __iter__(self):
  16. return self.i
  17. def __len__(self):
  18. if hasattr(self.i, '__len__'):
  19. return len(self.i)
  20. if hasattr(self.i, 'len'):
  21. return self.i.len
  22. if hasattr(self.i, 'fileno'):
  23. return os.fstat(self.i.fileno()).st_size
  24. def read(self, n):
  25. return "".join(islice(self.i, None, n))
  26. class CaseInsensitiveDict(collections.MutableMapping):
  27. """
  28. A case-insensitive ``dict``-like object.
  29. Implements all methods and operations of
  30. ``collections.MutableMapping`` as well as dict's ``copy``. Also
  31. provides ``lower_items``.
  32. All keys are expected to be strings. The structure remembers the
  33. case of the last key to be set, and ``iter(instance)``,
  34. ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()``
  35. will contain case-sensitive keys. However, querying and contains
  36. testing is case insensitive:
  37. cid = CaseInsensitiveDict()
  38. cid['Accept'] = 'application/json'
  39. cid['aCCEPT'] == 'application/json' # True
  40. list(cid) == ['Accept'] # True
  41. For example, ``headers['content-encoding']`` will return the
  42. value of a ``'Content-Encoding'`` response header, regardless
  43. of how the header name was originally stored.
  44. If the constructor, ``.update``, or equality comparison
  45. operations are given keys that have equal ``.lower()``s, the
  46. behavior is undefined.
  47. """
  48. def __init__(self, data=None, **kwargs):
  49. self._store = dict()
  50. if data is None:
  51. data = {}
  52. self.update(data, **kwargs)
  53. def __setitem__(self, key, value):
  54. # Use the lowercased key for lookups, but store the actual
  55. # key alongside the value.
  56. self._store[key.lower()] = (key, value)
  57. def __getitem__(self, key):
  58. return self._store[key.lower()][1]
  59. def __delitem__(self, key):
  60. del self._store[key.lower()]
  61. def __iter__(self):
  62. return (casedkey for casedkey, mappedvalue in self._store.values())
  63. def __len__(self):
  64. return len(self._store)
  65. def lower_items(self):
  66. """Like iteritems(), but with all lowercase keys."""
  67. return (
  68. (lowerkey, keyval[1])
  69. for (lowerkey, keyval)
  70. in self._store.items()
  71. )
  72. def __eq__(self, other):
  73. if isinstance(other, collections.Mapping):
  74. other = CaseInsensitiveDict(other)
  75. else:
  76. return NotImplemented
  77. # Compare insensitively
  78. return dict(self.lower_items()) == dict(other.lower_items())
  79. # Copy is required
  80. def copy(self):
  81. return CaseInsensitiveDict(self._store.values())
  82. def __repr__(self):
  83. return '%s(%r)' % (self.__class__.__name__, dict(self.items()))
  84. class LookupDict(dict):
  85. """Dictionary lookup object."""
  86. def __init__(self, name=None):
  87. self.name = name
  88. super(LookupDict, self).__init__()
  89. def __repr__(self):
  90. return '<lookup \'%s\'>' % (self.name)
  91. def __getitem__(self, key):
  92. # We allow fall-through here, so values default to None
  93. return self.__dict__.get(key, None)
  94. def get(self, key, default=None):
  95. return self.__dict__.get(key, default)