dbt-selly/dbt-env/lib/python3.8/site-packages/agate/mapped_sequence.py

171 lines
4.4 KiB
Python

#!/usr/bin/env python
"""
This module contains the :class:`MappedSequence` class that forms the foundation
for agate's :class:`.Row` and :class:`.Column` as well as for named sequences of
rows and columns.
"""
from collections import OrderedDict, Sequence
import six
from six.moves import range # pylint: disable=W0622
from agate.utils import memoize
class MappedSequence(Sequence):
"""
A generic container for immutable data that can be accessed either by
numeric index or by key. This is similar to an
:class:`collections.OrderedDict` except that the keys are optional and
iteration over it returns the values instead of keys.
This is the base class for both :class:`.Column` and :class:`.Row`.
:param values:
A sequence of values.
:param keys:
A sequence of keys.
"""
__slots__ = ['_values', '_keys']
def __init__(self, values, keys=None):
self._values = tuple(values)
if keys is not None:
self._keys = keys
else:
self._keys = None
def __getstate__(self):
"""
Return state values to be pickled.
This is necessary on Python2.7 when using :code:`__slots__`.
"""
return {
'_values': self._values,
'_keys': self._keys
}
def __setstate__(self, data):
"""
Restore pickled state.
This is necessary on Python2.7 when using :code:`__slots__`.
"""
self._values = data['_values']
self._keys = data['_keys']
def __unicode__(self):
"""
Print a unicode sample of the contents of this sequence.
"""
sample = u', '.join(repr(d) for d in self.values()[:5])
if len(self) > 5:
sample = u'%s, ...' % sample
return u'<agate.%s: (%s)>' % (type(self).__name__, sample)
def __str__(self):
"""
Print an ascii sample of the contents of this sequence.
"""
if six.PY2: # pragma: no cover
return str(self.__unicode__().encode('utf8'))
return str(self.__unicode__())
def __getitem__(self, key):
"""
Retrieve values from this array by index, slice or key.
"""
if isinstance(key, slice):
indices = range(*key.indices(len(self)))
values = self.values()
return tuple(values[i] for i in indices)
# Note: can't use isinstance because bool is a subclass of int
elif type(key) is int:
return self.values()[key]
else:
return self.dict()[key]
def __setitem__(self, key, value):
"""
Set values by index, which we want to fail loudly.
"""
raise TypeError('Rows and columns can not be modified directly. You probably need to compute a new column.')
def __iter__(self):
"""
Iterate over values.
"""
return iter(self.values())
@memoize
def __len__(self):
return len(self.values())
def __eq__(self, other):
"""
Equality test with other sequences.
"""
if not isinstance(other, Sequence):
return False
return self.values() == tuple(other)
def __ne__(self, other):
"""
Inequality test with other sequences.
"""
return not self.__eq__(other)
def __contains__(self, value):
return self.values().__contains__(value)
def keys(self):
"""
Equivalent to :meth:`collections.OrderedDict.keys`.
"""
return self._keys
def values(self):
"""
Equivalent to :meth:`collections.OrderedDict.values`.
"""
return self._values
@memoize
def items(self):
"""
Equivalent to :meth:`collections.OrderedDict.items`.
"""
return tuple(zip(self.keys(), self.values()))
def get(self, key, default=None):
"""
Equivalent to :meth:`collections.OrderedDict.get`.
"""
try:
return self.dict()[key]
except KeyError:
if default:
return default
else:
return None
@memoize
def dict(self):
"""
Retrieve the contents of this sequence as an
:class:`collections.OrderedDict`.
"""
if self.keys() is None:
raise KeyError
return OrderedDict(self.items())