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
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
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
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
def test_initial(case, expected): assert _.initial(case) == expected
def test_dash_method_call(): value = [1, 2, 3, 4, 5] assert _._.initial(value) == _.initial(value)