Пример #1
0
    def source(self):
        '''The source of objects from this token.

        If then token is a top-level one and is related with a
        ``is_instance(token, SomeClass)`` filter and
        ``SomeClass.this_instances`` is a collection, this collection will be
        the source.

        If the token is a top-level token but the test described above fails,
        the we use the `gc` module to get every possible object in the Python's
        memory.

        If the token is not a top-level token, we simply use the attribute's
        name from the parent token current value.

        '''
        from xoutil.types import is_collection
        cls = self._token_class
        this_instances = getattr(cls, 'this_instances', None)
        if is_collection(this_instances):
            if not self.only or defined(cls, self.only):
                return iter(this_instances)
            else:
                return []
        else:
            return self._build_source()
Пример #2
0
def getargvalues(frame):
    '''Inspects the given frame for arguments and returns a dictionary that
    maps parameters names to arguments values. If an `*` argument was passed
    then the key on the returning dictionary would be formatted as
    `<name-of-*-param>[index]`.

    For example in the function::

        >>> def autocontained(a, limit, *margs, **ks):
        ...    import sys
        ...    return getargvalues(sys._getframe())

        >>> autocontained(1, 12)['limit']
        12

        >>> autocontained(1, 2, -10, -11)['margs[0]']
        -10

    '''
    from xoutil.types import is_collection
    from xoutil.iterators import flatten
    pos, args, kwds, values = inspect.getargvalues(frame)
    res = {}
    for keys in pos:
        if not is_collection(keys):
            keys = (keys,)
        res.update({key: values[key] for key in flatten(keys)})
    if args:
        i = 0
        for item in values[args]:
            res['%s[%s]' % (args, i)] = item
            i += 1
    if kwds:
        res.update(values[kwds])
    return res
Пример #3
0
def iterate_over(source, *keys):
    '''Yields pairs of (key, value) for of all `keys` in `source`.

    If any `key` is missing from `source` is ignored (not yielded).

    If `source` is a `collection <xoutil.types.is_collection>`:func:, iterate
    over each of the items searching for any of keys.  This is not recursive.

    If no `keys` are provided, return an "empty" iterator -- i.e will raise
    StopIteration upon calling `next`.

    .. versionadded:: 1.5.2

    '''
    from xoutil.types import is_collection

    def inner(source):
        get = smart_getter(source)
        for key in keys:
            val = get(key, Unset)
            if val is not Unset:
                yield key, val

    def when_collection(source):
        from xoutil.iterators import map
        for generator in map(inner, source):
            for key, val in generator:
                yield key, val

    if is_collection(source):
        res = when_collection(source)
    else:
        res = inner(source)
    return res
Пример #4
0
 def push(queues, items):
     from xoutil.types import is_collection
     is_queryobject = IQueryObject.providedBy
     if not is_collection(items):
         items = (items, )
     for item in items:
         if is_expression(item):
             queues.append([item])
         elif is_queryobject(item):
             queues.extend([f] for f in item.filters)
Пример #5
0
def pop_first_of(source, *keys, **kwargs):
    '''Similar to :func:`get_first_of` using as `source` either an object or a
    mapping and deleting the first attribute or key.

    Examples::

        >>> somedict = dict(bar='bar-dict', eggs='eggs-dict')

        >>> class Foo(object): pass
        >>> foo = Foo()
        >>> foo.bar = 'bar-obj'
        >>> foo.eggs = 'eggs-obj'

        >>> pop_first_of((somedict, foo), 'eggs')
        'eggs-dict'

        >>> pop_first_of((somedict, foo), 'eggs')
        'eggs-obj'

        >>> pop_first_of((somedict, foo), 'eggs') is None
        True

        >>> pop_first_of((foo, somedict), 'bar')
        'bar-obj'

        >>> pop_first_of((foo, somedict), 'bar')
        'bar-dict'

        >>> pop_first_of((foo, somedict), 'bar') is None
        True

    '''
    from xoutil.types import is_collection

    def inner(source):
        get = smart_getter_and_deleter(source)
        res, i = Unset, 0
        while (res is Unset) and (i < len(keys)):
            res = get(keys[i], Unset)
            i += 1
        return res

    if is_collection(source):
        res = Unset
        source = iter(source)
        probe = next(source, None)
        while res is Unset and probe:
            res = inner(probe)
            probe = next(source, None)
    else:
        res = inner(source)
    return res if res is not Unset else kwargs.get('default', None)
Пример #6
0
def test_iscollection():
    from xoutil.eight import range
    from xoutil.types import is_collection
    from xoutil.collections import UserList, UserDict
    assert is_collection('all strings are iterable') is False
    assert is_collection(1) is False
    assert is_collection(range(1)) is True
    assert is_collection({}) is False
    assert is_collection(tuple()) is True
    assert is_collection(set()) is True
    assert is_collection(a for a in range(100)) is True

    class Foobar(UserList):
        pass

    assert is_collection(Foobar()) is True

    class Foobar(UserDict):
        pass

    assert is_collection(Foobar()) is False
Пример #7
0
def concatfiles(*files):
    '''Concat several files to a single one.

    Each positional argument must be either:

    - a file-like object (ready to be passed to :func:`shutil.copyfileobj`)

    - a string, the file path.

    The last positional argument is the target.  If it's file-like object it
    must be open for writing, and the caller is the responsible for closing
    it.

    Alternatively if there are only two positional arguments and the first is
    a collection, the sources will be the members of the first argument.

    '''
    import shutil
    from xoutil.eight import string_types
    from xoutil.types import is_collection
    if len(files) < 2:
        raise TypeError('At least 2 files must be passed to concatfiles.')
    elif len(files) == 2:
        files, target = files[0], files[1]
        if not is_collection(files):
            files = [files]
    else:
        files, target = files[:-1], files[-1]
    if isinstance(target, string_types):
        target, opened = open(target, 'wb'), True
    else:
        opened = False
    try:
        for f in files:
            if isinstance(f, string_types):
                fh = open(f, 'rb')
                closefh = True
            else:
                fh = f
                closefh = False
            try:
                shutil.copyfileobj(fh, target)
            finally:
                if closefh:
                    fh.close()
    finally:
        if opened:
            target.close()
Пример #8
0
def first_n(iterable, n=1, fill=Unset):
    '''Takes the first `n` items from iterable.

    If there are less than `n` items in the iterable and `fill` is
    :class:`~xoutil.types.Unset`, a StopIteration exception is raised;
    otherwise it's used as a filling pattern as explained below.

    :param iterable: An iterable from which the first `n` items should be
                     collected.

    :param n: The number of items to collect
    :type n: int

    :param fill: The filling pattern to use. It may be:

                 - a collection, in which case `first_n` fills the last items
                   by cycling over `fill`.

                 - anything else is used as the filling pattern by repeating.

    :returns: The first `n` items from `iterable`, probably with a filling
              pattern at the end.
    :rtype: generator object

    .. versionadded:: 1.2.0

    .. versionchanged:: 1.4.0 The notion of collection for the `fill`
                        argument uses :class:`xoutil.types.is_collection`
                        instead of probing for the ``__iter__`` method.

    '''
    if fill is not Unset:
        from xoutil.types import is_collection
        from itertools import cycle, repeat, chain
        if is_collection(fill):
            fill = cycle(fill)
        else:
            fill = repeat(fill)
        seq = chain(iterable, fill)
    else:
        seq = iter(iterable)
    while n > 0:
        yield next(seq)
        n -= 1
Пример #9
0
def slides(iterable, width=2, fill=None):
    '''Creates a sliding window of a given `width` over an iterable::

        >>> list(slides(range(1, 11)))
        [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]

    If the iterator does not yield a width-aligned number of items, the last
    slice returned is filled with `fill` (by default None)::

        >>> list(slides(range(1, 11), width=3))   # doctest: +ELLIPSIS
        [(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, None, None)]

    .. versionchanged:: 1.4.0 If the `fill` argument is a collection is cycled
                        over to get the filling, just like in :func:`first_n`.

    .. versionchanged:: 1.4.2 The `fill` argument now defaults to None,
                        instead of Unset.

    '''
    from itertools import cycle, repeat
    from xoutil.types import is_collection
    pos = 0
    res = []
    iterator = iter(iterable)
    current = next(iterator, Unset)
    while current is not Unset:
        if pos < width:
            res.append(current)
            current = next(iterator, Unset)
            pos = pos + 1
        else:
            yield tuple(res)
            res = []
            pos = 0
    if res:
        if is_collection(fill):
            fill = cycle(fill)
        else:
            fill = repeat(fill)
        while pos < width:
            res.append(next(fill))
            pos += 1
        yield tuple(res)
Пример #10
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