Small library to work with dependencies in Python

lazy #

A lazy object is a deferred computation, lambda: 2+2 is a deferred computation for example, nothing will be called until we execute

Lazy #

Lazy object representation

Convert a deferred computation into a lazy object using make_lazy.

Examples:

>>> isinstance(make_lazy(lambda: 1), Lazy)
True
>>> isinstance(lambda: 1, Lazy)
True
>>> x = make_lazy(lambda: 1)
>>> y = lambda: 2
>>> z = x + y
>>> z()
3

lazy(f) #

Creates a lazy callable from a function

Examples:

>>> from collections import namedtuple
>>> X = lazy(namedtuple('X', 'a b'))
>>> a = lambda: 1
>>> b = lambda: 'foo'

The reference is the same after constructing the object

>>> c = X(a, b=b)
>>> c() is c()
True
>>> c1 = X(a, b=b)
>>> c2 = X(a, b=b)
>>> c1() is c2()
False

You can also use non lazy objects

>>> c = X(a, b=2)
>>> c()
X(a=1, b=2)

Be careful with mutations

>>> c = X(a=make_lazy(lambda: [1]), b=2)
>>> d = c()
>>> d.a[0] = 3
>>> c()
X(a=[3], b=2)
Source code in vacuna/lazy.py
def lazy(f):
    """Creates a lazy callable from a function

    Examples:


    ```python
    >>> from collections import namedtuple
    >>> X = lazy(namedtuple('X', 'a b'))
    >>> a = lambda: 1
    >>> b = lambda: 'foo'

    ```

    The reference is the same after constructing the object

    ```python
    >>> c = X(a, b=b)
    >>> c() is c()
    True

    ```

    ```python
    >>> c1 = X(a, b=b)
    >>> c2 = X(a, b=b)
    >>> c1() is c2()
    False

    ```

    You can also use non lazy objects

    ```python
    >>> c = X(a, b=2)
    >>> c()
    X(a=1, b=2)

    ```

    Be careful with mutations

    ```python
    >>> c = X(a=make_lazy(lambda: [1]), b=2)
    >>> d = c()
    >>> d.a[0] = 3
    >>> c()
    X(a=[3], b=2)

    ```
    """
    @wraps(f)
    def _lazy(*args, **kwargs):
        return make_lazy(
            lambda: f(
                *map_list(args, call_if_lazy),
                **map_dict(kwargs, call_if_lazy)
            ),
            name=f.__name__,
        )

    return _lazy

make_lazy(lazy_obj, name = '') #

Creates a Lazy object that lets you inspect the properties of the object in a lazy fashion.

Examples:

>>> x = make_lazy(lambda: {'a': [{'b': True}]})
>>> y = x['a'][0]['b']
>>> y()
True
>>> from collections import namedtuple
>>> X = namedtuple('X', 'a b')
>>> x = make_lazy(lambda: X(a=[{'b': True}], b=False))
>>> y = x.a[0]['b']
>>> y()
True
>>> y = x.a[1]['b']  # this line does not break
>>> y()
Traceback (most recent call last):
  ...
vacuna.lazy.LazyError: error when evaluating `.a[1]`
>>> x = make_lazy(lambda: 2)
>>> y = make_lazy(lambda: 1)
>>> z = x + y
>>> z()
3
>>> x = make_lazy(lambda: [])
>>> x()
[]
>>> x() is x()
True
Source code in vacuna/lazy.py
def make_lazy(lazy_obj, name=''):
    """Creates a Lazy object that lets you inspect the properties of the object
    in a lazy fashion.

    Examples:

    ```python
    >>> x = make_lazy(lambda: {'a': [{'b': True}]})
    >>> y = x['a'][0]['b']
    >>> y()
    True

    ```

    ```python
    >>> from collections import namedtuple
    >>> X = namedtuple('X', 'a b')
    >>> x = make_lazy(lambda: X(a=[{'b': True}], b=False))
    >>> y = x.a[0]['b']
    >>> y()
    True

    ```

    ```python
    >>> y = x.a[1]['b']  # this line does not break
    >>> y()
    Traceback (most recent call last):
      ...
    vacuna.lazy.LazyError: error when evaluating `.a[1]`

    ```

    ```python
    >>> x = make_lazy(lambda: 2)
    >>> y = make_lazy(lambda: 1)
    >>> z = x + y
    >>> z()
    3

    ```

    ```python
    >>> x = make_lazy(lambda: [])
    >>> x()
    []
    >>> x() is x()
    True

    ```
    """
    return _make_lazy(once(lazy_obj), name)

once(f) #

Execute the given function only once and cache the result

Examples:

Without once

>>> def f(x=[]):
...     x.append(1)
...     return x
>>> f()
[1]
>>> f()
[1, 1]

With once

>>> @once
... def f(x=[]):
...     x.append(1)
...     return x
>>> f()
[1]
>>> f()
[1]
Source code in vacuna/lazy.py
def once(f):
    """Execute the given function only once and cache the result

    Examples:

    Without `once`

    ```python
    >>> def f(x=[]):
    ...     x.append(1)
    ...     return x
    >>> f()
    [1]
    >>> f()
    [1, 1]

    ```

    With `once`

    ```python
    >>> @once
    ... def f(x=[]):
    ...     x.append(1)
    ...     return x
    >>> f()
    [1]
    >>> f()
    [1]

    ```

    """

    x = None
    executed = False

    def _once():
        nonlocal x, executed

        if not executed:
            x = f()
            executed = True

        return x

    return _once