Source code for vdat.command_interpreter.utils

# Virus Data Analysis Tool: a data reduction GUI for HETDEX/VIRUS data
# Copyright (C) 2015, 2016  "The HETDEX collaboration"
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""Utility functionalities
"""
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import abc
import functools
import re

import six
from six.moves import range

from vdat.command_interpreter import exceptions


[docs]def id_(*args): """Identity function: returns the input unchanged If the input is one element only, extract it from ``args`` Examples -------- >>> id_() () >>> id_(5) 5 >>> id_(3, 4) (3, 4) >>> a, b, c = id_(2, 3, 4) >>> print(a, b, c) 2 3 4 """ if len(args) == 1: return args[0] else: return args
[docs]def flip(func): """Flip the order of the arguments in the function Can also be used a decorator Parameters ---------- func : callable function to replace Returns ------- callable """ @functools.wraps(func) def wrap(*args, **kwargs): return func(*args[::-1], **kwargs) return wrap
[docs]def multiwrap(*decorators): """Create a decorator combining the given decorators""" def wrapper(func): for d in decorators[::-1]: func = d(func) return func return wrapper
[docs]class SliceLike(object): """A slice like object, but it's not usable as :class:`slice`. If the input argument is a string of ``:`` or ``,`` separated integers parse it assuming ``[start]:[stop][:step]``. ``start``, ``stop``, and ``step`` must be integers or ``None``; the latter is optional and cannot be zero. It's possible to use the ``in`` statement to check if a value is in the slice with the following logic: * if ``start`` is not given, it checks that the value is less than ``stop`` * if ``stop`` is not given, it checks that the value is greater or equal than ``start`` * if neither ``start`` not ``stop`` are given and ``step`` is not given or one, returns ``True`` * if ``step`` is not 1, it checks that the value is equal to ``start + n*step`` with integer ``n``. * if ``start`` is not given and ``step`` is not 1, returns ``False``; undefined behaviour. Parameters ---------- str_or_int, args : string or one/two/three integers if the first argument is a string is parsed as if it's a ``:`` or ``,`` separated one and all the elements are converted to None (if empty) or to integers. In this case no other arguments are allowed. If the arguments are integers are interpreted as: ``SliceLike(stop)`` ``SliceLike(start, stop[, step])`` Raises ------ CISliceError if the initialisation fails Attributes ---------- start stop step """ def __init__(self, str_or_int, *args): if isinstance(str_or_int, six.string_types) and not args: str_ = [i.strip() for i in re.split(r'[:,]', str_or_int, maxsplit=2)] if len(str_) < 2: msg = ("At least one `:` or `,` must be present in the input" " string") raise exceptions.CISliceError(msg) str_ = (str_ + [None, ])[:3] # make sure is three elements try: self._start = int(str_[0]) if str_[0] else None self._stop = int(str_[1]) if str_[1] else None self._step = int(str_[2]) if str_[2] else None except ValueError as e: msg = ("It's not possible to convert the input string to" "integers because of: {}") raise exceptions.CISliceError(msg.format(e)) elif all(isinstance(i, (int, type(None))) for i in [str_or_int, ] + list(args)) and len(args) <= 2: self._start = None self._stop = str_or_int self._step = None if args: self._start = self._stop self._stop = args[0] if len(args) == 2: self._step = args[1] else: raise exceptions.CISliceError('The input must be one single string' ' or one to three integers') if self._step == 0: raise exceptions.CISliceError('The step size cannot be zero') @property def start(self): "Start of the slice (read only)" return self._start @property def stop(self): "Stop of the slice (read only)" return self._stop @property def step(self): "Step of the slice (read only)" return self._step def __contains__(self, item): """item in the slice""" _greater = item >= self.start if self.start else True _less = item < self.stop if self.stop else True _step = False if _greater and _less: if not self.step or self.step == 1: _step = True elif self.step >= 2: if self.start: n = (item - self.start) // self.step _step = item == self.start + n * self.step return _greater and _less and _step def __str__(self): return "{}({}, {}, {})".format(self.__class__.__name__, self.start, self.stop, self.step) def __repr__(self): return self.__str__() def __format__(self, format_spec): return self.__str__()
[docs] def range(self): """Create and returns a range using the elements of the slice. If :attr:`start` is ``None``, set it to 0, if :attr:`stop` is ``None``, it fails and when :attr:`step` is ``None`` set it to 1. Returns ------- a range or a xrange object (python 3 / python 2) """ return range(self.start or 0, self.stop, self.step or 1)
if six.PY2: abstractproperty = abc.abstractproperty else: abstractproperty = multiwrap(property, abc.abstractmethod) """* Python 3: decorator from combining :class:`property` and :func:`abc.abstractmethod` * Python 2: alias of :func:`abc.abstractproperty` """