예제 #1
0
def simple_name(item, join=True):
    '''Returns the simple name for the given object.

    :param join: If False, only the object inner name is returned; if it is a
           callable is used similar to a string join receiving a tuple of
           (module-name, inner-name) as argument; True means (is equivalent
           to)::

             join = lambda arg: '.'.join(arg).strip('.')

           For example, use ``lambda arg: arg`` to return the 2-tuple itself.

           See `module_name`:func: for more information when a not False value
           is used.

    Examples::

       >>> simple_name(simple_name)
       'xoutil.names.simple_name'

       >>> from xoutil import Unset
       >>> simple_name(Unset)
       'xoutil.logical.Unset'

    This function is intended for named objects (those with the `__name__`
    attribute), if an object without standard name is used, the type name is
    returned instead; for example::

        >>> simple_name(0)
        'int'

    To get a name in a more precise way, use `nameof`:func:.

    '''
    # TODO: Use this function in `nameof`
    from xoutil.inspect import type_name
    singletons = (None, True, False, Ellipsis, NotImplemented)
    res = next((str(s) for s in singletons if s is item), None)
    if res is None:
        res = type_name(item)
        if res is None:
            item = type(item)
            res = type_name(item)
        if join:
            if join is True:
                join = lambda arg: str('.'.join(arg).strip('.'))
            res = join((module_name(item), res))
    return res
예제 #2
0
def check(value, validator, msg=None):
    '''Check a `value` with a `validator`.

    Argument `validator` could be a callable, a type, or a tuple of types.

    Return True if the value is valid.

    Examples::

      >>> check(1, int)
      True

      >>> check(10, lambda x: x <= 100, 'must be less than or equal to 100')
      True

      >>> check(11/2, (int, float))
      True

    '''
    if isinstance(validator, (type, tuple)):
        checker = is_type(validator)
    else:
        checker = validator
    if checker(value):
        return True
    else:
        from xoutil.inspect import type_name
        if not msg:
            # TODO: Use the name of validator with `inspect.getattr_static`
            # when `xoutil.future` is ready
            msg = 'Invalid value "%s" of type "%s"'
        msg = msg.format(value=value, type=type_name(value, affirm=True))
        raise ValueError(msg)
예제 #3
0
def _get_checker_name(checker):
    '''Return a nice name for a `checker`.

    A `checker` could be a type, a tuple of types, a callable or a list of
    other checkers.

    '''
    l = lambda o: str('(%s)' % o.join(_get_checker_name(c) for c in checker))
    if isinstance(checker, list):
        return l('_AND_')
    elif isinstance(checker, tuple):
        return l('_OR_')
    else:
        from xoutil.inspect import type_name
        res = type_name(checker, affirm=True)
        if not isinstance(checker, type):
            assert callable(checker)
            if 'lambda' in res:
                from inspect import getargspec
                args = getargspec(checker).args
                assert len(args) == 1
                res = str('%s(%s)' % (res, args[0]))
        return res
예제 #4
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))
예제 #5
0
def ok(value, *checkers, **kwargs):
    '''Validate a value with several checkers.

    Return the value if it is Ok, or raises an `ValueError` exception if not.

    Arguments:

    :param value: the value to validate

    :param checkers: a variable number of checkers (at least one), each one
        could be a type, a tuple of types of a callable that receives the
        value and returns if the value is valid or not.  In order the value is
        considered valid, all checkers must validate the value.

    :param message: keyword argument to be used in case of error; will be the
           argument of `ValueError` exception; could contain the placeholders
           ``{value}`` and ``{type}``; a default value is used if this
           argument is not given.

    :param msg: an alias for "message"

    :param extra_checkers: In order to create validators using `partial`.
           Must be a tuple.

    Keyword arguments are not validated to be correct.

    This function could be used with type-definitions for arguments, see
    :class:`TypeChecker`.

    Examples::

      >>> ok(1, int)
      1

      >>> ok(10, int, lambda x: x < 100, message='Must be integer under 100')
      10

      >>> ok(11/2, (int, float))
      5.5

      >>> ok(11/2, int, float)
      5.5

      >>> try:
      ...     res = ok(11/2, int)
      ... except ValueError:
      ...     res = '---'
      >>> res
      '---'

    '''
    extra_checkers = kwargs.get('extra_checkers', ())
    pred = predicate(*(checkers + extra_checkers))
    if pred(value):
        return value
    else:
        from xoutil.iterators import multi_get as get
        from xoutil.inspect import type_name
        msg = next(get(kwargs, 'message', 'msg'), 'Invalid {type}: {value}!')
        msg = msg.format(value=value, type=type_name(value, affirm=True))
        raise ValueError(msg)
예제 #6
0
def nameof(*args, **kwargs):
    '''Obtain the name of each one of a set of objects.

    .. versionadded:: 1.4.0

    .. versionchanged:: 1.6.0

       - Keyword arguments are now keyword-only arguments.

       - Support for several objects

       - Improved the semantics of parameter `full`.

       - Added the `safe` keyword argument.

    If no object is given, None is returned; if only one object is given, a
    single string is returned; otherwise a list of strings is returned.

    The name of an object is normally the variable name in the calling stack.

    If the object is not present calling frame, up to five frame levels are
    searched.  Use the `depth` keyword argument to specify a different
    starting point and the search will proceed five levels from this frame up.

    If the same object has several good names a single one is arbitrarily
    chosen.

    Good names candidates are retrieved based on the keywords arguments
    `full`, `inner`, `safe` and `typed`.

    If `typed` is True and the object is not a type name or a callable (see
    `xoutil.inspect.type_name`:func:), then the `type` of the object is used
    instead.

    If `inner` is True we try to extract the name by introspection instead of
    looking for the object in the frame stack::

    If `full` is True the full identifier of the object is preferred.  In this
    case if `inner` is False the local-name for the object is found.  If
    `inner` is True, find the import-name.

    If `safe` is True, returned value is converted -if it is not- into a valid
    Python identifier, though you should not trust this identifier resolves to
    the value.

    See `the examples in the documentation <name-of-narrative>`:ref:.

    '''
    # XXX: The examples are stripped from here.  Go the documentation page.
    from numbers import Number
    from xoutil.eight import range
    from xoutil.inspect import type_name
    arg_count = len(args)
    names = [[] for i in range(arg_count)]

    class vars:
        '`nonlocal` simulation'
        params = kwargs
        idx = 0

    def grant(name=None, **again):
        if name:
            names[vars.idx].append(name)
            assert len(names[vars.idx]) < 5
        if again:
            vars.params = dict(kwargs, **again)
        else:
            vars.params = kwargs
            vars.idx += 1

    def param(name, default=False):
        return vars.params.get(name, default)

    while vars.idx < arg_count:
        item = args[vars.idx]
        if param('typed') and not type_name(item):
            item = type(item)
        if param('inner'):
            res = type_name(item)
            if res:
                if param('full'):
                    head = module_name(item)
                    if head:
                        res = '.'.join((head, res))
                grant(res)
            elif isinstance(item, (base_string, Number)):
                grant(str(item))
            else:
                grant('@'.join(('%(next)s', hex(id(item)))), typed=True)
        else:
            import sys
            sf = sys._getframe(param('depth', 1))
            try:
                i, LIMIT, res = 0, 5, _undef
                _full = param('full')
                while not res and sf and (i < LIMIT):
                    key, mapping = _key_for_value(sf, item)
                    if key and _full:
                        head = module_name(_get_value(mapping, '__name__'))
                        if not head:
                            head = module_name(sf.f_code.co_name)
                        if not head:
                            head = module_name(item) or None
                    else:
                        head = None
                    if key:
                        res = key
                    else:
                        sf = sf.f_back
                        i += 1
            finally:
                # TODO: on "del sf" Python says "SyntaxError: can not delete
                # variable 'sf' referenced in nested scope".
                sf = None
            if res:
                grant('.'.join((head, res)) if head else res)
            else:
                res = type_name(item)
                if res:
                    grant(res)
                else:
                    grant(None, inner=True)
    for i in range(arg_count):
        names[i] = _get_best_name(names[i], safe=param('safe'))
    if arg_count == 0:
        return None
    elif arg_count == 1:
        return names[0]
    else:
        return names