Example #1
1
class BaseCache(object):
    """
    BaseCache is a class that saves and operates on an OrderedDict. It has a
    certain capacity, stored in the attribute `maxsize`. Whether this
    capacity is reached, can be checked by using the boolean property
    `is_full`. To implement a custom cache, inherit from this class and
    override the methods ``__getitem__`` and ``__setitem__``.
    Call the method `sunpy.database.caching.BaseCache.callback` as soon
    as an item from the cache is removed.
    """

    def __init__(self, maxsize=float("inf")):
        self.maxsize = maxsize
        self._dict = OrderedDict()

    def get(self, key, default=None):  # pragma: no cover
        """Return the corresponding value to `key` if `key` is in the cache,
        `default` otherwise. This method has no side-effects, multiple calls
        with the same cache and the same passed key must always return the same
        value.

        """
        try:
            return self._dict[key]
        except KeyError:
            return default

    @abstractmethod
    def __getitem__(self, key):
        """abstract method: this method must be overwritten by inheriting
        subclasses. It defines what happens if an item from the cache is
        attempted to be accessed.

        """
        return  # pragma: no cover

    @abstractmethod
    def __setitem__(self, key, value):
        """abstract method: this method must be overwritten by inheriting
        subclasses. It defines what happens if a new value should be assigned
        to the given key. If the given key does already exist in the cache or
        not must be checked by the person who implements this method.
        """

    @abstractproperty
    def to_be_removed(self):
        """The item that will be removed on the next
        :meth:`sunpy.database.caching.BaseCache.remove` call.

        """

    @abstractmethod
    def remove(self):
        """Call this method to manually remove one item from the cache. Which
        item is removed, depends on the implementation of the cache. After the
        item has been removed, the callback method is called.

        """

    def callback(self, key, value):
        """This method should be called (by convention) if an item is removed
        from the cache because it is full. The passed key and value are the
        ones that are removed. By default this method does nothing, but it
        can be customized in a custom cache that inherits from this base class.

        """

    @property
    def is_full(self):
        """True if the number of items in the cache equals :attr:`maxsize`,
        False otherwise.

        """
        return len(self._dict) == self.maxsize

    def __delitem__(self, key):
        self._dict.__delitem__(key)

    def __contains__(self, key):
        return key in self._dict.keys()

    def __len__(self):
        return len(self._dict)

    def __iter__(self):
        for key in self._dict.__iter__():
            yield key

    def __reversed__(self):  # pragma: no cover
        for key in self._dict.__reversed__():
            yield key

    def clear(self):  # pragma: no cover
        return self._dict.clear()

    def keys(self):  # pragma: no cover
        return list(self._dict.keys())

    def values(self):  # pragma: no cover
        return list(self._dict.values())

    def items(self):  # pragma: no cover
        return list(self._dict.items())

    def iterkeys(self):  # pragma: no cover
        return iter(self._dict.keys())

    def itervalues(self):  # pragma: no cover
        for value in self._dict.values():
            yield value

    def iteritems(self):  # pragma: no cover
        for key, value in six.iteritems(self._dict):
            yield key, value

    def update(self, *args, **kwds):  # pragma: no cover
        self._dict.update(*args, **kwds)

    def pop(self, key, default=MutableMapping._MutableMapping__marker):  # pragma: no cover
        return self._dict.pop(key, default)

    def setdefault(self, key, default=None):  # pragma: no cover
        return self._dict.setdefault(key, default)

    def popitem(self, last=True):  # pragma: no cover
        return self._dict.popitem(last)

    def __reduce__(self):  # pragma: no cover
        return self._dict.__reduce__()

    def copy(self):  # pragma: no cover
        return self._dict.copy()

    def __eq__(self, other):  # pragma: no cover
        return self._dict.__eq__(other)

    def __ne__(self, other):  # pragma: no cover
        return self._dict.__ne__(other)

    def viewkeys(self):  # pragma: no cover
        return self._dict.keys()

    def viewvalues(self):  # pragma: no cover
        return self._dict.values()

    def viewitems(self):  # pragma: no cover
        return self._dict.items()

    @classmethod
    def fromkeys(cls, iterable, value=None):  # pragma: no cover
        return OrderedDict.fromkeys(iterable, value)

    def __repr__(self):  # pragma: no cover
        return "{0}({1!r})".format(self.__class__.__name__, dict(self._dict))
Example #2
0
class Stack(object):
    """ Base class to store pandas objects, with special operations to
    return as 3d data (eg panel) and to apply functions itemwise.  Items are
    stored in an ordered dict."""

    itemlabel = "Item"

    _magic = ["__len__", "__iter__", "__reversed__", "__contains__"]

    def __init__(self, data, keys=None, name="", sort_items=False):
        self.name = name

        # Dictionary input
        if isinstance(data, dict):
            logger.debug('Initializing "%s" from dictionary.' % self.full_name)
            if sort_items:
                logger.debug("Sorting keys")
                self._data = OrderedDict(sorted(data.keys(), key=lambda t: t[0]))

            else:
                self._data = OrderedDict(data)

        else:

            if not isinstance(data, Iterable):
                logger.info("%s constructed from non-iterable... converting " "data to an iterable" % self.full_name)
                data = [data]

            if keys:
                if not isinstance(keys, Iterable):
                    logger.info(
                        "%s constructed from non-iterable... converting " "keys to an iterable" % self.full_name
                    )
                    keys = [keys]

                if len(keys) != len(data):
                    raise ValueError("Length mistmatch: keys and data (%s,%s)" % (len(keys), len(data)))

            # If keys not passed, generate them
            else:
                # Zipped data ((key, df), (key, df))
                try:
                    keys, data = zip(*data)
                except Exception:
                    keys = self._gen_keys(len(data))
                    if len(keys) > 1:
                        logger.warn("Generating keys %s-%s" % (keys[0], keys[-1]))
                    else:
                        logger.warn("Generating key %s" % keys[0])

            self._data = OrderedDict([(key, data[i]) for (i, key) in enumerate(keys)])

    @property
    def _address(self):
        """ Property to make easily accesible by multicanvas """
        return mem_address(super(Stack, self).__repr__())

    def _gen_keys(self, length):
        """ Return a list of itemlables (item0, item1 etc...) using
            self.itemlabel and a length"""

        logger.debug("Items not found on %s: generating item list" % self.full_name)
        return [self.itemlabel + str(i) for i in range(length)]

    # --------------------
    # Dictionary Interface
    def __getitem__(self, keyslice):
        """ If single name, used dict interface.  If slice or integer, uses 
        list interface.  All results parameterized to key, data pairs, passed
        directly into a new Stack.
        """
        # Slice as list of strings or int [0, 'foo', 2, 'bar']
        if hasattr(keyslice, "__iter__"):

            tuples_out = []
            for item in keyslice:
                if isinstance(item, str):
                    item = self._data.keys().index(item)
                tuples_out.append(self._data.items()[item])

        else:
            if isinstance(keyslice, int) or isinstance(keyslice, slice):
                tuples_out = self._data.items()[keyslice]
            else:
                tuples_out = [(keyslice, self._data[keyslice])]  # keyslice is name

        # If single item, return TimeSpectra, else, return new Stack
        # Canonical slicing implementaiton; don't change unless good reason
        # Because len() wonky with nested tuples (eg (x,y) and [(x1,y1),(x2,y2)]
        # are both length two, this will work:

        if sum(1 for x in tuples_out) == 2:
            return tuples_out[1]  # Return timespectra
        else:
            return self.__class__(tuples_out)

    def __delitem__(self, keyslice):
        """ Delete a single name, or a keyslice from names/canvas """

        if isinstance(keyslice, str):
            idx = self.names.index(keyslice)
            self.pop(idx)
        else:
            raise NotImplementedError("Deletion only supports single entry")

    def __setitem__(self, name, canvas):
        """ """
        if name in self.names:
            idx = self.names.index(name)
            self.pop(idx)
            self.insert(idx, name, canvas)

        else:
            self.names.append(name)

    def __getattr__(self, attr):
        """ If attribute not found, try attribute lookup in dictionary.  If
        that is not found, try finding attribute on self._data.
        
        For example, self.keys() will first look for self['keys'].  Since
        this isn't found, it calls self._data.keys().  But if I do 
        self.Item1, then it returns self['Item1'].  The very rare conflict
        case that a user has named the items a method that may already exist
        in the dictionary (eg items=['a','b','keys'] is addressed.
        """

        if attr in self._data.keys():
            if hasattr(self._data, attr):
                raise AttributeError(
                    '"%s attribute" found in both the items\
                and as a method of the underlying dictionary object.'
                    % (attr)
                )
            else:
                return self[attr]
        return getattr(self._data, attr)

    # Attributes deferred to self.data /dictionary
    def __len__(self):
        return self._data.__len__()

    def __iter__(self):
        return self._data.__iter__()

    def __reversed__(self):
        return self._data.__reversed__()

    def __contains__(self):
        return self._data.__contains__()

    def as_3d(self):
        """ Return 3d structure of data.  Default is panel."""
        raise Panel(data=self._data)

        ### Data types without labels

    # Is this realy necessary?  See pyparty.ParticleManger for possibly more consistent implementation
    def get_all(self, attr, astype=tuple):
        """Generator/tuple etc.. of (item, attribute) pairs. """

        return put._parse_generator(((item[0], getattr(item[1], attr)) for item in self.items()), astype)

    def _get_unique(self, attr):
        """ Inspects Stack itemwise for an attribute for unique values.
        If non-unique value for the attributes are found, returns
        "mixed". 
        """
        unique = set(self.get_all(attr, astype=dict).values())
        if len(unique) > 1:
            return "mixed"
        else:
            return tuple(unique)[0]  # set doesn't support indexing

    def set_all(self, attr, val, inplace=False):
        """ Set attributes itemwise.  
            If not inplace, returns new instance of self"""
        if inplace:
            for (key, item) in self.items():
                try:
                    setattr(item, attr, val)
                except Exception as E:
                    raise Exception(
                        'Could not set %s in "%s".  Received the following \
                     exception:\n "%s"'
                        % (attr, key, E)
                    )
        else:
            out = deepcopy(self._data)  # DEEPCOPY
            for item in out:
                setattr(out[item], attr, val)
            return self.__class__(out)

    def apply(self, func, *args, **kwargs):
        """ Applies a user-passed function, or calls an instance method itemwise.
        
        
        Parameters:
        -----------
        func: str or function
            If string, must correspond to a method on the object stored 
            itemwise in the stack.  If a function, appliked itemwise to
            objects stored.  
              
        inplace: False 
            Special kwarg.  If true, self._data modified inplace, 
            otherwise new specstack is returned.
                         
        *args, **kwargs: 
            func arguments.
          
        Returns:
        --------
        If not inplace, returns SpecStack after itemwise application.
            
        """

        inplace = kwargs.pop("inplace", False)

        if isinstance(func, basestring):
            if inplace:
                for item in self:
                    self[item] = getattr(self[item], func)(*args, **kwargs)

            else:
                return self.__class__(OrderedDict([(k, getattr(v, func)(*args, **kwargs)) for k, v in self.items()]))

        # function, numpyfunction etc...
        else:
            if inplace:
                for item in self:
                    self[item] = self[item].apply(func)(*args, **kwargs)

            else:
                return self.__class__(OrderedDict([(k, v.apply(func, *args, **kwargs)) for k, v in self.items()]))

    @property
    def full_name(self):
        """ Timespectra:name or Timespectra:unnamed.  Useful for scripts mostly """
        outname = getattr(self, "name", "unnamed")
        return "%s:%s" % (self.__class__.__name__, self.name)
Example #3
0
 def __reversed__(self):
     for k in OrderedDict.__reversed__(self):
         yield self.__map[k]