diff --git a/dol/__init__.py b/dol/__init__.py index 414b1dac..6c9462d6 100644 --- a/dol/__init__.py +++ b/dol/__init__.py @@ -41,6 +41,7 @@ def ihead(store, n=1): regroupby, igroupby, not_a_mac_junk_path, + instance_checker, ) from dol.trans import ( diff --git a/dol/trans.py b/dol/trans.py index 7ca4b14c..82ab9b91 100644 --- a/dol/trans.py +++ b/dol/trans.py @@ -2069,6 +2069,22 @@ class for it's purpose of transforming stores more conveniently. } +def _conditional_data_trans(v, condition, data_trans): + if condition(v): + return data_trans(v) + return v + + +from dol.util import Pipe + +@store_decorator +def conditional_data_trans(store=None, *, condition, data_trans): + _data_trans = partial( + _conditional_data_trans, condition=condition, data_trans=data_trans + ) + return Pipe(data_trans, wrap_kvs(store, obj_of_data=_data_trans)) + + @store_decorator def add_path_get(store=None, *, name=None, path_type: type = tuple): """ @@ -2181,9 +2197,7 @@ def __getitem__(self, k): return store_cls # TODO: Should we keep add_path_get, or add "read_only" flag to add_path_access? -# TODO: store['a','b','c'] works, but store['a']['b','c'] doesn't because the "path -# accessibility" doesn't carry on to values by default. See doctest on how to make it -# happen an integrate here with one argument only: value condition. +# TODO: See https://github.com/i2mint/dol/issues/10 @store_decorator def add_path_access(store=None, *, name=None, path_type: type = tuple): """Make nested stores (read/write) accessible through key paths (iterable of keys). @@ -2317,14 +2331,10 @@ def add_path_access(store=None, *, name=None, path_type: type = tuple): For example, if you wanted to wrap all mappings recursively, you could: >>> from typing import Mapping - >>> from dol.trans import wrap_kvs - >>> def add_path_access_if_mapping(v): - ... if isinstance(v, Mapping): - ... return wrap_kvs( - ... add_path_access(v), - ... obj_of_data=add_path_access_if_mapping + >>> from dol.util import instance_checker + >>> add_path_access_if_mapping = conditional_data_trans( + ... condition=instance_checker(Mapping), data_trans=add_path_access ... ) - ... return v >>> s = add_path_access_if_mapping({'a': {'b': {'c': 42}}}) >>> s['a', 'b', 'c'] 42 diff --git a/dol/util.py b/dol/util.py index c02fd655..f8526368 100644 --- a/dol/util.py +++ b/dol/util.py @@ -10,6 +10,7 @@ from functools import partialmethod, partial, WRAPPER_ASSIGNMENTS from types import MethodType + # monkey patching WRAPPER_ASSIGNMENTS to get "proper" wrapping (adding defaults and kwdefaults wrapper_assignments = (*WRAPPER_ASSIGNMENTS, '__defaults__', '__kwdefaults__') @@ -17,6 +18,27 @@ wraps = partial(_wraps, assigned=wrapper_assignments) +def _isinstance(obj, class_or_tuple): + """The same as the builtin isinstance, but without the position only restriction, + allowing us to use partial to define filter functions for specific types + """ + return isinstance(obj, class_or_tuple) + + +def instance_checker(*types): + """Makes a filter function that checks the type of an object. + + >>> f = instance_checker(int, float) + >>> f(1) + True + >>> f(1.0) + True + >>> f('1.0') + False + """ + return partial(_isinstance, class_or_tuple=types) + + def not_a_mac_junk_path(path: str): """A function that will tell you if the path is not a mac junk path/ More precisely, doesn't end with '.DS_Store' or have a `__MACOSX` folder somewhere