Exemple #1
0
def encode_coerce(arg):
    """Encode string objects."""
    import locale
    from . import Invalid
    from xoutil.eight import callable

    encoding = locale.getpreferredencoding() or "UTF-8"
    encode = getattr(arg, "encode", None)
    if callable(encode):
        try:
            res = encode(encoding, "replace")
            if not isinstance(res, bytes):
                res = None
        except BaseException:
            res = None
    else:
        res = None
    if res is None:
        try:
            import codecs

            res = codecs.encode(arg, encoding, "replace")
        except BaseException:
            res = Invalid
    return res
Exemple #2
0
def decode_coerce(arg):
    """Decode objects implementing the buffer protocol."""
    import locale
    from . import Invalid
    from xoutil.eight import text_type, callable

    encoding = locale.getpreferredencoding() or "UTF-8"
    decode = getattr(arg, "decode", None)
    if callable(decode):
        try:
            res = decode(encoding, "replace")
            if not isinstance(res, text_type):
                res = None
        except BaseException:
            res = None
    else:
        res = None
    if res is None:
        try:
            # TODO: All arrays are decoded, and not only those containing
            # valid byte or unicode characters.
            import codecs

            res = codecs.decode(arg, encoding, "replace")
        except BaseException:
            res = Invalid
    return res
Exemple #3
0
def fix_class_documentation(cls, ignore=None, min_length=10, deep=1,
                            default=None):
    '''Fix the documentation for the given class using its super-classes.

    This function may be useful for shells or Python Command Line Interfaces
    (CLI).

    If `cls` has an invalid documentation, super-classes are recursed
    in MRO until a documentation definition was made at any level.

    :param ignore: could be used to specify which classes to ignore by
                   specifying its name in this list.

    :param min_length: specify that documentations with less that a number of
                       characters, also are ignored.

    '''
    assert isinstance(cls, type), _INVALID_CLASS_TYPE_MSG
    if _len(cls.__doc__) < min_length:
        ignore = ignore or ()

        def get_doc(c):
            if (c.__name__ not in ignore) and _len(c.__doc__) >= min_length:
                return c.__doc__
            else:
                return None

        doc = build_documentation(cls, get_doc, deep)
        if doc:
            cls.__doc__ = doc
        elif default:
            cls.__doc__ = default(cls) if callable(default) else default
Exemple #4
0
 def valid(chk, stack=True):
     if isinstance(chk, (bool, Logical)):
         res = bool(chk)
     elif isinstance(chk, type):
         res = isinstance(obj, chk)
     elif isinstance(chk, tuple):
         if not chk:
             res = False
         elif all(isinstance(c, type) for c in chk):
             res = isinstance(obj, chk)
         else:
             res = any(valid(c, stack=False) for c in chk)
     elif isinstance(chk, list):
         res = all(valid(c) for c in chk)
     elif isinstance(chk, Set):
         res = obj in chk
     elif isinstance(chk, Mapping):
         res = chk.get(obj, False)
     elif callable(chk):
         res = chk(obj)
     else:
         res = False
     if not res and stack:
         self.failed_stack.append(chk)
     return res
def power(*args):
    '''Returns the "power" composition of several functions.

    Examples::

       >>> import operator
       >>> f = power(partial(operator.mul, 3), 3)
       >>> f(23) == 3*(3*(3*23))
       True

       >>> power(operator.neg)
       Traceback (most recent call last):
       ...
       TypeError: Function `power` requires at least two arguments
    '''
    try:
        funcs, times = args[:-1], args[-1]
    except IndexError:
        msg = "Function `power` requires at least two arguments"
        raise TypeError(msg)
    if not funcs:
        raise TypeError('Function `power` requires at least two arguments')
    if any(not callable(func) for func in funcs):
        raise TypeError('First arguments of `power` must be callables')
    if not isinstance(times, int):
        raise TypeError('Last argument of `power` must be int')
    if len(funcs) > 1:
        base = (compose(funcs), )
    else:
        base = (funcs[0], )
    return compose(*(base * times))
Exemple #6
0
def is_file_like(obj):
    '''Return if `obj` is a valid file type or not.'''
    from xoutil.eight import _py2, callable
    types = (file, IOBase) if _py2 else (IOBase, )
    if isinstance(obj, types):
        return True
    else:
        methods = ('close', 'write', 'read')
        return all(callable(getattr(obj, name, None)) for name in methods)
Exemple #7
0
 def get_doc(c):
     if (c.__name__ not in ignore):
         method = c.__dict__.get(method_name)
         if callable(method) and _len(method.__doc__) >= min_length:
             return method.__doc__
         else:
             return None
     else:
         return None
Exemple #8
0
 def __parse_arguments(self, *args, **kwargs):
     '''Assign parsed arguments to the just created instance.'''
     from xoutil.validators import (is_valid_identifier, predicate)
     self.attr_name = Unset
     self.init = Unset
     self.default = Unset
     self.do_assigning = True
     self.validator = True
     for i, arg in enumerate(args):
         if self.attr_name is Unset and is_valid_identifier(arg):
             self.attr_name = arg
         elif self.init is Unset and callable(arg):
             self.init = arg
         else:
             msg = ('Invalid positional arguments: %s at %s\n'
                    'Valid arguments are the attribute name and a '
                    'callable constructor for initial value.')
             raise ValueError(msg % (args[i:], i))
     bads = {}
     for key in kwargs:
         value = kwargs[key]
         if (self.default is Unset and self.init is Unset and
                 key in ('default', 'value', 'initial_value')):
             self.default = value
         elif (self.validator is True and
               key in ('validator', 'checker', 'check')):
             self.validator = value
         elif (self.do_assigning is True and key == 'do_assigning' and
               value is False):
             self.do_assigning = False
         else:
             bads[key] = value
     self.validator = predicate(self.validator)
     if bads:
         msg = ('Invalid keyword arguments: %s\n'
                'See constructor documentation for more info.')
         raise ValueError(msg % bads)
     if self.attr_name is Unset:
         from xoutil.names import nameof
         if self.init is not Unset:
             if isinstance(self.init, type):
                 self.attr_name = str('_%s' % self.init.__name__)
             else:
                 self.attr_name = nameof(self.init, safe=True)
         else:
             self.attr_name = self._unique_name()
     self.inner_name = str('__%s__' % self.attr_name.strip('_'))
Exemple #9
0
def get_method_function(cls, method_name):
    '''Get definition function given in its `method_name`.

    There is a difference between the result of this function and
    ``getattr(cls, method_name)`` because the last one return the unbound
    method and this a python function.

    '''
    if not isinstance(cls, type):
        cls = cls.__class__
    mro = cls.mro()
    i, res = 0, None
    while not res and (i < len(mro)):
        sc = mro[i]
        method = sc.__dict__.get(method_name)
        if callable(method):
            res = method
        else:
            i += 1
    return res
def compose(*callables, **kwargs):
    '''Returns a function that is the composition of several `callables`.

    By default `compose` behaves like mathematical function composition: this
    is to say that ``compose(f1, ... fn)`` is equivalent to ``lambda _x:
    fn(...(f1(_x))...)``.

    If any "intermediate" function returns a :class:`ctuple` it is expanded as
    several positional arguments to the next function.

    .. versionchanged:: 1.5.5 At least a callable must be passed, otherwise a
                        TypeError is raised.  If a single callable is passed
                        it is returned without change.

    :param math: Indicates if `compose` should behave like mathematical
                 function composition: last function in `funcs` is applied
                 last. If False, then the last function in `func` is applied
                 first.

    '''
    if not callables:
        raise TypeError('At least a function must be provided')
    if not all(callable(func) for func in callables):
        raise TypeError('Every func must a callable')
    if len(callables) == 1:
        return callables[0]
    math = kwargs.get('math', True)
    if not math:
        callables = list(reversed(callables))

    def _inner(*args):
        f, functions = callables[0], callables[1:]
        result = f(*args)
        for f in functions:
            if isinstance(result, ctuple):
                result = f(*result)
            else:
                result = f(result)
        return result
    return _inner
Exemple #11
0
 def __call__(self):
     res = self.value
     if callable(res):
         return res(*self.args, **self.kwargs)
     else:
         return res
Exemple #12
0
def smart_copy(*args, **kwargs):
    '''Copies the first apparition of attributes (or keys) from `sources` to
    `target`.

    :param sources: The objects from which to extract keys or attributes.

    :param target: The object to fill.

    :param defaults: Default values for the attributes to be copied as
                     explained below. Defaults to False.

    :type defaults: Either a bool, a dictionary, an iterable or a callable.

    Every `sources` and `target` are always positional arguments. There should
    be at least one source. `target` will always be the last positional
    argument.

    If `defaults` is a dictionary or an iterable then only the names provided
    by itering over `defaults` will be copied. If `defaults` is a dictionary,
    and one of its key is not found in any of the `sources`, then the value of
    the key in the dictionary is copied to `target` unless:

    - It's the value :class:`xoutil.types.Required` or an instance of Required.

    - An exception object

    - A sequence with is first value being a subclass of Exception. In which
      case :class:`xoutil.data.adapt_exception` is used.

    In these cases a KeyError is raised if the key is not found in the
    sources.

    If `default` is an iterable and a key is not found in any of the sources,
    None is copied to `target`.

    If `defaults` is a callable then it should receive one positional
    arguments for the current `attribute name` and several keyword arguments
    (we pass ``source``) and return either True or False if the attribute
    should be copied.

    If `defaults` is False (or None) only the attributes that do not start
    with a "_" are copied, if it's True all attributes are copied.

    When `target` is not a mapping only valid Python identifiers will be
    copied.

    Each `source` is considered a mapping if it's an instance of
    `collections.Mapping` or a `MappingProxyType`.

    The `target` is considered a mapping if it's an instance of
    `collections.MutableMapping`.

    :returns: `target`.

    .. versionchanged:: 1.7.0 `defaults` is now keyword only.

    '''
    from collections import MutableMapping
    from xoutil.types import is_collection, is_mapping, Required
    from xoutil.data import adapt_exception
    from xoutil.validators.identifiers import is_valid_identifier
    defaults = kwargs.pop('defaults', False)
    if kwargs:
        raise TypeError('smart_copy does not accept a "%s" keyword argument'
                        % kwargs.keys()[0])
    sources, target = args[:-1], args[-1]
    if not sources:
        raise TypeError('smart_copy requires at least one source')
    if isinstance(target, (bool, type(None), int, float, str_base)):
        raise TypeError('target should be a mutable object, not %s' %
                        type(target))
    if isinstance(target, MutableMapping):
        def setter(key, val):
            target[key] = val
    else:
        def setter(key, val):
            if is_valid_identifier(key):
                setattr(target, key, val)
    _mapping = is_mapping(defaults)
    if _mapping or is_collection(defaults):
        for key, val in ((key, get_first_of(sources, key, default=Unset))
                         for key in defaults):
            if val is Unset:
                if _mapping:
                    val = defaults.get(key, None)
                else:
                    val = None
                exc = adapt_exception(val, key=key)
                if exc or val is Required or isinstance(val, Required):
                    raise KeyError(key)
            setter(key, val)
    else:
        keys = []
        for source in sources:
            get = smart_getter(source)
            if is_mapping(source):
                items = (name for name in source)
            else:
                items = dir(source)
            for key in items:
                private = isinstance(key, str_base) and key.startswith('_')
                if (defaults is False or defaults is None) and private:
                    copy = False
                elif callable(defaults):
                    copy = defaults(key, source=source)
                else:
                    copy = True
                if key not in keys:
                    keys.append(key)
                    if copy:
                        setter(key, get(key))
    return target
Exemple #13
0
 def _get_ignored(what):
     if callable(what):
         return what
     else:
         return lambda s: s == what
Exemple #14
0
def _get_checker_name(checker, full=False):
    '''Return a nice name for a `checker`.

    :param full: If True, return a detailed representation of the checker.

    See :class:`Predicate` for possible checker kinds.

    '''
    from xoutil.logical import Logical
    from xoutil.collections import Set, Mapping, PascalSet
    from xoutil.eight import callable
    from xoutil.inspect import type_name
    from xoutil.string import safe_str as sstr    # , safe_repr as srepr
    srepr = repr
    if isinstance(checker, (bool, Logical)):
        return str(checker)
    elif isinstance(checker, Predicate):
        return str('p(%s)') % checker.get_name()
    elif isinstance(checker, type):
        return type_name(checker, affirm=True)
    elif isinstance(checker, list):
        if not checker:
            return str(True)
        else:
            return str(' & ').join(_get_checker_name(c) for c in checker)
    elif isinstance(checker, tuple):
        if not checker:
            return str(False)
        elif all(isinstance(c, type) for c in checker):
            return type_name(checker, affirm=True)
        else:
            res = str(' | ').join(_get_checker_name(c) for c in checker)
            return str('(%s)' % res)
    elif isinstance(checker, PascalSet):
        return str(checker)
    elif isinstance(checker, Set):
        if checker:
            aux = srepr(next(iter(checker)))
            if len(checker) > 1:
                aux += str(', ...')
        else:
            aux = str()
        return str('{%s}') % aux
    elif isinstance(checker, Mapping):
        if checker:
            key = next(iter(checker))
            aux = str('%s: %s') % (srepr(key), srepr(checker[key]))
            if len(checker) > 1:
                aux += str(', ...')
        else:
            aux = str()
        return str('{%s}') % aux
    elif callable(checker):
        res = type_name(checker, affirm=True)
        if 'lambda' in res:
            from inspect import getargspec
            res = res.replace('<lambda>', '<λ>')
            args = getargspec(checker).args
            res = sstr('%s(%s)' % (res, ', '.join(args)))
        return res
    else:
        return str('False(%)' % srepr(checker))
Exemple #15
0
def callable_coerce(arg):
    """Check if `arg` is a callable object."""
    from xoutil.eight import callable

    return arg if callable(arg) else Invalid