def deprecated(msg=''):
    """This is a decorator which can be used to mark functions
    as deprecated. It will result in a warning being emitted
    when the function is used.

    :param msg:

        Additional message added to the warning.

    """

    def wrapper(func):
        @functools.wraps(func)
        def new_func(*args, **kwargs):
            warning_string = "Call to deprecated function or property `%s`." % func.__name__
            warning_string = warning_string + ' ' + msg
            warnings.warn(
                warning_string,
                category=DeprecationWarning,
            )
            return func(*args, **kwargs)

        return new_func

    return wrapper def kwargs_api_change(old_name, new_name=None):
    """This is a decorator which can be used if a kwarg has changed
    its name over versions to also support the old argument name.

    Issues a warning if the old keyword argument is detected and
    converts call to new API.

    :param old_name:

        Old name of the keyword argument

    :param new_name:

        New name of keyword argument

    """

    def wrapper(func):
        @functools.wraps(func)
        def new_func(*args, **kwargs):

            if old_name in kwargs:
                if new_name is None:
                    warning_string = 'Using deprecated keyword argument `%s` in function `%s`. ' \
                                 'This keyword is no longer supported, please don`t use it ' \
                                 'anymore.' % (old_name, func.__name__)
                else:
                    warning_string = 'Using deprecated keyword argument `%s` in function `%s`, ' \
                                 'please use keyword `%s` instead.' % \
                                 (old_name, func.__name__, new_name)
                warnings.warn(warning_string, category=DeprecationWarning)
                value = kwargs.pop(old_name)
                if new_name is not None:
                    kwargs[new_name] = value

            return func(*args, **kwargs)

        return new_func

    return wrapper class WithAnnotations(HasLogger):

    __slots__ = ('_annotations',)

    def __init__(self):
        self._annotations = Annotations()  # The annotation object to handle annotations

    @property
    def v_annotations(self):
        """ Annotation feature of a trajectory node.

        Store some short additional information about your nodes here.
        If you use the standard HDF5 storage service, they will be stored as hdf5 node
        attributes_.

        .. _attributes: http://pytables.github.io/usersguide/libref/declarative_classes.html#the-attributeset-class

        """
        return self._annotations

    def f_set_annotations(self, *args, **kwargs):
        """Sets annotations

        Equivalent to calling `v_annotations.f_set(*args,**kwargs)`

        """
        self._annotations.f_set(*args, **kwargs)

    def f_get_annotations(self, *args):
        """Returns annotations

        Equivalent to `v_annotations.f_get(*args)`

        """
        return self._annotations.f_get(*args)

    def f_ann_to_str(self):
        """Returns annotations as string

        Equivalent to `v_annotations.f_ann_to_str()`

        """
        return self._annotations.f_ann_to_str() class ChainMap(object):
    """Combine multiple mappings for sequential lookup.
    """

    def __init__(self, *maps):
        self._maps = maps

    def __getitem__(self, key):
        for mapping in self._maps:
            try:
                return mapping[key]
            except KeyError:
                pass
        raise KeyError(key)

    def __len__(self):
        length = 0

        for mapping in self._maps:
            length += len(mapping)

        return length

    def __iter__(self):
        iter_list = [mapping.keys() for mapping in self._maps]
        return itools.chain(*iter_list) class IteratorChain(object):
    """Helper class that chains arbitrary generators and iterators and iterables.

    Preferably used over itertools.chain to avoid recursive calls.

    You can already pass some `iterators` on creation.

    """
    def __init__(self, *iterables):
        # Deque containing the iterators to come
        self._chain = deque()
        # The current iterator providing the next elements
        self._current = iter([])

        self.add(*iterables)

    def add(self, *iterables):
        """Adds `iterables` to the chain"""
        self._chain.extend(iterables)

    def next(self):
        """Returns next element from chain.

        More precisely, it returns the next element of the
        foremost iterator. If this iterator is empty it moves iteratively
        along the chain of available iterators to pick the new foremost one.

        Raises StopIteration if there are no elements left.

        """
        while True:
            # We need this loop because some iterators may already be empty.
            # We keep on popping from the left until next succeeds and as long
            # as there are iterators available
            try:
                return next(self._current)
            except StopIteration:
                try:
                    self._current = iter(self._chain.popleft())
                except IndexError:
                    # If we run out of iterators we are sure that
                    # there can be no more element
                    raise StopIteration('Reached end of iterator chain')

    def __next__(self):
        """For python 3 compatibility"""
        return self.next()

    def __iter__(self):
        while True:
            try:
                yield self.next()
            except StopIteration:
                # new behavior since PEP479
                # one should return to stop iteration
                return def is_debug():
    """Checks if user is currently debugging.

    Debugging is checked via ``'pydevd' in sys.modules``.

    :return: True of False

    """
    return 'pydevd' in sys.modules def nest_dictionary(flat_dict, separator):
    """ Nests a given flat dictionary.

    Nested keys are created by splitting given keys around the `separator`.

    """
    nested_dict = {}
    for key, val in flat_dict.items():
        split_key = key.split(separator)
        act_dict = nested_dict
        final_key = split_key.pop()
        for new_key in split_key:
            if not new_key in act_dict:
                act_dict[new_key] = {}

            act_dict = act_dict[new_key]

        act_dict[final_key] = val
    return nested_dict class HasLogger(HasSlots):
    """Abstract super class that automatically adds a logger to a class.

    To add a logger to a sub-class of yours simply call ``myobj._set_logger(name)``.
    If ``name=None`` the logger is chosen as follows:

        ``self._logger = logging.getLogger(self.__class.__.__module__ + '.' + self.__class__.__name__)``

    The logger can be accessed via ``myobj._logger``.

    """

    __slots__ = ('_logger',)

    def __getstate__(self):
        """Called for pickling.

        Removes the logger to allow pickling and returns a copy of `__dict__`.

        """
        state_dict = super(HasLogger, self).__getstate__()
        if '_logger' in state_dict:
            # Pickling does not work with loggers objects,
            # so we just keep the logger's name:
            state_dict['_logger'] = self._logger.name
        return state_dict

    def __setstate__(self, statedict):
        """Called after loading a pickle dump.

        Restores `__dict__` from `statedict` and adds a new logger.

        """
        super(HasLogger, self).__setstate__(statedict)
        if '_logger' in statedict:
            # If we re-instantiate the component the
            # logger attribute only contains a name,
            # so we also need to re-create the logger:
            self._set_logger(statedict['_logger'])

    def _set_logger(self, name=None):
        """Adds a logger with a given `name`.

        If no name is given, name is constructed as
        `type(self).__name__`.

        """
        if name is None:
            cls = self.__class__
            name = '%s.%s' % (cls.__module__, cls.__name__)
        self._logger = logging.getLogger(name) class DisableAllLogging(object):
    """Context Manager that disables logging"""

    def __enter__(self):
        logging.disable(logging.CRITICAL)

    def __exit__(self, exc_type, exc_val, exc_tb):
        logging.disable(logging.NOTSET) class HasSlots(object, metaclass=MetaSlotMachine):
    """Top-class that allows mixing of classes with and without slots.

    Takes care that instances can still be pickled with the lowest
    protocol. Moreover, provides a generic `__dir__` method that
    lists all slots.

    """
    __slots__ = ('__weakref__',)

    def __getstate__(self):
        if hasattr(self, '__dict__'):
            # We don't require that all sub-classes also define slots,
            # so they may provide a dictionary
            statedict = self.__dict__.copy()
        else:
            statedict = {}
        # Get all slots of potential parent classes
        for slot in self.__all_slots__:
            try:
                value = getattr(self, slot)
                statedict[slot] = value
            except AttributeError:
                pass
        # Pop slots that cannot or should not be pickled
        statedict.pop('__dict__', None)
        statedict.pop('__weakref__', None)
        return statedict

    def __setstate__(self, state):
        """Recalls state for items with slots"""
        for key in state:
            setattr(self, key, state[key])

    def __dir__(self):
        """Includes all slots in the `dir` method"""
        result = set()
        result.update(dir(self.__class__), self.__all_slots__)
        if hasattr(self, '__dict__'):
            result.update(self.__dict__.keys())
        return list(result)
import inspect
import warnings
import keyword
import itertools as itools
import re
import pypet.pypetexceptions as pex
import pypet.pypetconstants as pypetconstants
from collections import deque
from pypet.utils.decorators import deprecated, kwargs_api_change
from pypet.annotations import WithAnnotations
from pypet.utils.helpful_classes import ChainMap, IteratorChain
from pypet.utils.helpful_functions import is_debug, nest_dictionary
from pypet.pypetlogging import HasLogger, DisableAllLogging
from pypet.slots import HasSlots
    @property
    def v_is_parameter(self):
        """Whether the node is a parameter or not (i.e. a result)"""
        return self._is_parameter

    def f_val_to_str(self):
        """ Returns a string summarizing the data handled by the parameter or result

        ABSTRACT: Needs to be implemented by subclass, otherwise the empty string is returned.

        """
        return ''

    def __str__(self):
        """ String representation of the parameter or result.

        If not specified in subclass this is simply the full name.

        """
        return self.v_full_name

    def _store_flags(self):
        """ Currently not used because I let the storage service infer how to store
        stuff from the data itself.

        If you write your own parameter or result you can implement this function
        to make specifications on how to store data,
        see also :func:`pypet.storageservice.HDF5StorageService.store`.

        :returns: {} (Empty dictionary)

        """
        return {}

    def _load_flags(self):
        """ Currently not used because I let the storage service infer how to load
        stuff from the data itself.

        If you write your own parameter or result you can implement this function
        to make specifications on how to load data,
        see also :func:`pypet.storageservice.HDF5StorageService.store`.

        :returns: {} (Empty dictionary)

        """
        return {}

    def _store(self):
        """Method called by the storage service for serialization.

        The method converts the parameter's or result's value(s) into  simple
        data structures that can be stored to disk.
        Returns a dictionary containing these simple structures.

        Understood basic structures are

        * python natives (int, long, str,bool,float,complex)

        * python lists and tuples

        * numpy natives arrays, and matrices of type
          np.int8-64, np.uint8-64, np.float32-64, np.complex, np.str

        * python dictionaries of the previous types (flat not nested!)

        * pandas data frames

        * object tables (see :class:`~pypet.parameter.ObjectTable`)

        :return: A dictionary containing basic data structures.

        ABSTRACT: Needs to be implemented by subclass

        """
        raise NotImplementedError('Implement this!')

    def _load(self, load_dict):
        """Method called by the storage service to reconstruct the original result.

        Data contained in the load_dict is equal to the data provided by the result or parameter
        when previously called with _store().

        :param load_dict:

            The dictionary containing basic data structures, see also
            :func:`~pypet.naturalnaming.NNLeafNode._store`.


        ABSTRACT: Needs to be implemented by subclass

        """
        raise NotImplementedError('Implement this!')

    def f_is_empty(self):
        """Returns true if no data is handled by a result or parameter.

        ABSTRACT: Needs to be implemented by subclass

        """
        raise NotImplementedError('You should implement this!')

    def f_empty(self):
        """Removes all data from the result or parameter.

        If the result has already been stored to disk via a trajectory and a storage service,
        the data on disk is not affected by `f_empty`.

        Yet, this function is particularly useful if you have stored very large data to disk
        and you want to free some memory on RAM but still keep the skeleton of your result or
        parameter.

        Note that freeing RAM requires that all references to the data are deleted. If you
        reference the data somewhere else in your code, the data is not erased from RAM.

        ABSTRACT: Needs to be implemented by subclass

        """
        raise NotImplementedError('You should implement this!')



class NaturalNamingInterface(HasLogger):