Example #1
0
def invoke(obj, path, *args, **kargs):
    """Invokes the method at path of object.

    Args:
        obj (dict): The object to query.
        path (list|str): The path of the method to invoke.
        args (optional): Arguments to pass to method call.
        kargs (optional): Keyword arguments to pass to method call.

    Returns:
        mixed: Result of the invoked method.

    Example:

        >>> obj = {'a': [{'b': {'c': [1, 2, 3, 4]}}]}
        >>> invoke(obj, 'a[0].b.c.pop', 1)
        2
        >>> obj
        {'a': [{'b': {'c': [1, 3, 4]}}]}

    .. versionadded:: 1.0.0
    """
    paths = to_path(path)
    target_path = pyd.initial(paths)
    method_name = pyd.last(paths)

    try:
        method = getattr(get(obj, target_path), method_name)
    except AttributeError:
        ret = None
    else:
        ret = method(*args, **kargs)

    return ret
Example #2
0
def update_path(obj, callback, keys, default=None):
    """Update the value of an object described by `keys` using `callback`. If
    any part of the object path doesn't exist, it will be created with
    `default`. The callback is invoked with the last key value of `obj`:
    ``(value)``

    Args:
        obj (list|dict): Object to modify.
        callback (callable): Function that returns updated value.
        keys (list): A list of string keys that describe the object path to
            modify.
        default (mixed, optional): Default value to assign if path part is not
            set. Defaults to ``{}`` if `obj` is a ``dict`` or ``[]`` if `obj`
            is a ``list``.

    Returns:
        mixed: Updated `obj`.

    Example:

        >>> update_path({}, lambda value: value, ['a', 'b'])
        {'a': {'b': None}}
        >>> update_path([], lambda value: value, [0, 0])
        [[None]]

    .. versionadded:: 2.0.0
    """
    # pylint: disable=redefined-outer-name
    if default is None:
        default = {} if isinstance(obj, dict) else []

    if not pyd.is_list(keys):
        keys = [keys]

    last_key = pyd.last(keys)
    obj = clone_deep(obj)
    target = obj

    for key in pyd.initial(keys):
        set_item(target, key, clone_deep(default), allow_override=False)

        try:
            target = target[key]
        except TypeError:
            target = target[int(key)]

    set_item(target, last_key, callback(get_item(target,
                                                 last_key,
                                                 default=None)))

    return obj
Example #3
0
def unset(obj, path):
    """Removes the property at `path` of `obj`.

    Note:
        Only ``list``, ``dict``, or objects with a ``pop()`` method can be
        unset by this function.

    Args:
        obj (mixed): The object to modify.
        path (mixed): The path of the property to unset.

    Returns:
        bool: Whether the property was deleted.

    Warning:
        `obj` is modified in place.

    Example:

        >>> obj = {'a': [{'b': {'c': 7}}]}
        >>> unset(obj, 'a[0].b.c')
        True
        >>> obj
        {'a': [{'b': {}}]}
        >>> unset(obj, 'a[0].b.c')
        False
    """
    tokens = to_path_tokens(path)

    if not pyd.is_list(tokens):  # pragma: no cover
        tokens = [tokens]

    last_key = pyd.last(tokens)

    if isinstance(last_key, PathToken):
        last_key = last_key.key

    target = obj

    for idx, token in enumerate(pyd.initial(tokens)):
        if isinstance(token, PathToken):
            key = token.key
        else:
            key = token

        try:
            try:
                target = target[key]
            except TypeError:
                target = target[int(key)]
        except Exception:
            target = NoValue

        if target is NoValue:
            break

    did_unset = False

    if target is not NoValue:
        try:
            try:
                target.pop(last_key)
                did_unset = True
            except TypeError:
                target.pop(int(last_key))
                did_unset = True
        except Exception:
            pass

    return did_unset
Example #4
0
def update_with(obj, path, updater, customizer=None):
    """This method is like :func:`update` except that it accepts customizer
    which is invoked to produce the objects of path. If customizer returns
    ``None``, path creation is handled by the method instead. The customizer is
    invoked with three arguments: ``(nested_value, key, nested_object)``.

    Args:
        obj (list|dict): Object to modify.
        path (str|list): A string or list of keys that describe the object path
            to modify.
        updater (function): Function that returns updated value.
        customizer (function, optional): The function to customize assigned
            values.

    Returns:
        mixed: Updated `obj`.

    Warning:
        `obj` is modified in place.

    Example:

        >>> update_with({}, '[0][1]', lambda: 'a', lambda: {})
        {0: {1: 'a'}}

    .. versionadded:: 4.0.0
    """
    if not callable(updater):
        updater = pyd.constant(updater)

    if customizer is not None and not callable(customizer):
        call_customizer = partial(callit, clone, customizer, argcount=1)
    elif customizer:
        call_customizer = partial(callit, customizer,
                                  argcount=getargcount(customizer, maxargs=3))
    else:
        call_customizer = None

    default_type = dict if isinstance(obj, dict) else list
    tokens = to_path_tokens(path)

    if not pyd.is_list(tokens):  # pragma: no cover
        tokens = [tokens]

    last_key = pyd.last(tokens)

    if isinstance(last_key, PathToken):
        last_key = last_key.key

    target = obj

    for idx, token in enumerate(pyd.initial(tokens)):
        if isinstance(token, PathToken):
            key = token.key
            default_factory = pyd.get(tokens,
                                      [idx + 1, 'default_factory'],
                                      default=default_type)
        else:
            key = token
            default_factory = default_type

        obj_val = base_get(target, key, default=None)
        path_obj = None

        if call_customizer:
            path_obj = call_customizer(obj_val, key, target)

        if path_obj is None:
            path_obj = default_factory()

        base_set(target, key, path_obj, allow_override=False)

        try:
            target = target[key]
        except TypeError as exc:  # pragma: no cover
            try:
                target = target[int(key)]
                _failed = False
            except Exception:
                _failed = True

            if _failed:
                raise TypeError('Unable to update object at index {!r}. {}'
                                .format(key, exc))

    value = base_get(target, last_key, default=None)
    base_set(target, last_key, callit(updater, value))

    return obj
Example #5
0
def test_initial(case, expected):
    assert _.initial(case) == expected
Example #6
0
def test_dash_method_call():
    value = [1, 2, 3, 4, 5]
    assert _._.initial(value) == _.initial(value)
Example #7
0
def test_dash_method_call():
    value = [1, 2, 3, 4, 5]
    assert _._.initial(value) == _.initial(value)