Skip to content

Making conditional_data_trans better, and generally; recursively applying wrappers #10

@thorwhalen

Description

@thorwhalen

This theme comes up regularly.

When we wrap a store, only the first level is wrapped. If the store's nature is nested, one often expects that the wrap be applied to the nested levels, but one would be wrong to expect this, since it would be an undesirable effect in the general case.
Therefore we need to make it easy for the user to control this aspect if they need it.

To abstract? Here's an example with add_path_access (code to that function here).

The add_path_access does this: doesn't carry on to values.

>>> s = add_path_access({'a': {'b': {'c': 42}}})
>>> s['a', 'b', 'c']
42

But the wrapping doesn't carry on to values, so if you ask for s['a'], and then want to get ['b', 'c'] from it, it won't work:

>>> s['a']['b', 'c']
Traceback (most recent call last):
  ...
KeyError: ('b', 'c')

That's because add_path_access applies to the "top level" only.
Once you ask for the 'a' key, it gives you that value, but that value is a "pure dict", not one wrapped with add_path_access like s is. Of course we could do this:

>>> add_path_access(s['a'])['b', 'c']
42

The reason why we don't do this automatically is that it may not always be desirable.
If one wanted to though, one could use wrap_kvs(obj_of_data=...) to wrap
specific values with add_path_access.

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
... )
...     return v
>>> s = add_path_access_if_mapping({'a': {'b': {'c': 42}}})
>>> s['a', 'b', 'c']
42
>>> # But now this works:
>>> s['a']['b', 'c']
42

conditional_data_trans

This function:

def conditional_data_trans(store, *, condition, data_transform): ...

implements the pattern above. We can now do this:

>>> from typing import Mapping
>>> from dol.util import instance_checker
>>> add_path_access_if_mapping = conditional_data_trans(
...     condition=instance_checker(Mapping), data_trans=add_path_access
... )
>>> s = add_path_access_if_mapping({'a': {'b': {'c': 42}}})
>>> s['a', 'b', 'c']
42
>>> # But now this works:
>>> s['a']['b', 'c']
42

Tasks

wrapper does not work with types

Well, it just doesn't work at the first level:

from dol.trans import conditional_data_trans
from dol import instance_checker
from typing import Mapping
from dol import add_path_access
from dol import Pipe

# f = Pipe(add_path_access, conditional_data_trans(condition=instance_checker(Mapping), data_trans=add_path_access))

d = {'a': {'b': {'c': 42}}}

s = conditional_data_trans(d, condition=instance_checker(Mapping), data_trans=add_path_access)
assert s['a']['b', 'c'] == 42 == d['a']['b']['c']
assert s['a', 'b', 'c'] == 42 == d['a']['b']['c']

f = conditional_data_trans(condition=instance_checker(Mapping), data_trans=add_path_access)
s = f(d)
assert s['a']['b', 'c'] == 42 == d['a']['b']['c']
assert s['a', 'b', 'c'] == 42 == d['a']['b']['c']


S = conditional_data_trans(dict, condition=instance_checker(Mapping), data_trans=add_path_access)
s = S(d)
assert s['a']['b', 'c'] == 42 == d['a']['b']['c']
# assert s['a', 'b', 'c'] == 42 == d['a']['b']['c']  # doesn't work.

Generalize condition

Is checking the value general enough?

We could use the postget argument of wrap_kvs to condition on both value and key.
But in this case it's strange: We need to include the condition and the trans both in data_trans then.
We might want a condition: Callable[[Key, Value], bool].

We might also want to handle a condition: Callable[[Path, Key, Value], bool] for more (full?) control -- say if we wanted to condition on the nested depth.

And what about according to the path itself? Perhaps we'd like to

┆Issue is synchronized with this Asana task by Unito

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions