Skip to content

Commit

Permalink
lena.context.UpdateContext "update" keyword argument can be a format …
Browse files Browse the repository at this point in the history
…string. Add keyword "default".
  • Loading branch information
ynikitenko committed Apr 23, 2020
1 parent 4faf240 commit f88220f
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 19 deletions.
4 changes: 2 additions & 2 deletions docs/source/core.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
lena.core
=========
Core
====

**Sequences:**

Expand Down
5 changes: 3 additions & 2 deletions docs/source/math.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
math package
============
Math
====

**Functions of multidimensional arguments:**

.. currentmodule:: lena.math.meshes
Expand Down
7 changes: 5 additions & 2 deletions lena/context/functions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Functions to work with context (dictionary)."""
from __future__ import print_function

import copy

import lena.core
Expand Down Expand Up @@ -69,6 +71,7 @@ def format_context(format_str, *args, **kwargs):
arguments between *format_str* and context.
Example:
>>> from lena.context import format_context
>>> f = format_context("{y}", y="x.y")
>>> f({"x": {"y": 10}})
'10'
Expand All @@ -91,10 +94,10 @@ def format_context(format_str, *args, **kwargs):
>>> f({"x": 10})
'10'
If *format_str* is not a string, :exc:`LenaTypeError` is raised.
If *format_str* is not a string, :exc:`.LenaTypeError` is raised.
All other errors are raised only during formatting.
If context doesn't contain the needed key,
:exc:`LenaKeyError` is raised.
:exc:`.LenaKeyError` is raised.
Note that string formatting can also raise
a :exc:`KeyError` or an :exc:`IndexError`,
so it is recommended to test your formatters before using them.
Expand Down
55 changes: 43 additions & 12 deletions lena/context/update_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,30 @@
import copy

import lena.core
import lena.context
import lena.flow


_sentinel = object()


class UpdateContext():
"""Update context of passing values."""

def __init__(self, subcontext, update, recursively=True):
def __init__(self, subcontext, update, default=_sentinel, recursively=True):
"""*subcontext* is a string representing the part of context
to be updated (for example, *output.plot*).
*subcontext* must be non-empty.
*update* will become the value of *subcontext*.
If it is a string, *update* can contain arguments
to be got from context (for example: "{variable.name}").
In this case the result is always a string.
For formatting details see :func:`.format_context`.
If it is a string enclosed in braces,
*update* will get its value from context during :meth:`__call__`
(example: "{variable.name}").
If the string is missing in the context, :exc:`.LenaKeyError`
will be raised unless a *default* is set.
In this case *default* will be used for update.
If *update* is a string containing no curly braces,
it will be used for update itself.
If *recursively* is ``True`` (default), not overwritten
existing values of *subcontext* are preserved.
Expand All @@ -37,11 +45,11 @@ def __init__(self, subcontext, update, recursively=True):
If *subcontext* is not a string, :exc:`.LenaTypeError` is raised.
If it is empty, :exc:`.LenaValueError` is raised.
Braces can be only the first and the last symbols
of *update*, otherwise :exc:`.LenaValueError` is raised.
"""
# subcontext is a string, because it must have at most one value
# at each nesting level.
# todo. subcontext might be also created as a format string
# "{variable.name}", (think about it in the future).
if not isinstance(subcontext, str):
raise lena.core.LenaTypeError(
"subcontext must be a string, {} provided".format(subcontext)
Expand All @@ -51,9 +59,21 @@ def __init__(self, subcontext, update, recursively=True):
"subcontext must be non-empty"
)
self._subcontext = lena.context.str_to_list(subcontext)
if isinstance(update, str):
self._update = lena.context.format_context(update)
if not isinstance(update, str):
self._update = update
elif '{' in update or '}' in update:
braceless_str = update[1:-1]
if (update[0] != '{' or update[-1] != '}'
or '{' in braceless_str or '}' in braceless_str):
raise lena.core.LenaValueError(
"update can contain braces only as its first "
"and last symbols, {} provided".format(update)
)
self._get_value = True
self._update = braceless_str
self._default = default
else:
self._get_value = False
self._update = update
self._recursively = bool(recursively)

Expand All @@ -64,9 +84,20 @@ def __call__(self, value):
If the *value* contains no context, it is also created.
"""
data, context = lena.flow.get_data_context(value)
if callable(self._update):
# it is always a string, which is immutable
update = self._update(context)
if isinstance(self._update, str):
if self._get_value:
# formatting braces are present
if self._default is _sentinel:
# no default is provided
update = lena.context.get_recursively(context,
self._update)
else:
update = lena.context.get_recursively(
context, self._update, self._default
)
update = copy.deepcopy(update)
else:
update = self._update
else:
update = copy.deepcopy(self._update)
# now empty context is prohibited.
Expand Down
28 changes: 27 additions & 1 deletion tests/context/test_update_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,33 @@ def test_update_context():
0,
{
"data": {
"yes": {"Yes": "{'Yes': 'YES'}"}
"yes": {"Yes": {'Yes': 'YES'}}
}
}
)
# value missing in data
with pytest.raises(lena.core.LenaKeyError):
uc7((0, {}))
# default is set
uc71 = UpdateContext("data", "{data.yes}", default="", recursively=False)
assert uc71((0, {})) == (0, {"data": ""})

# strings without braces are treated as simple update values
uc8 = UpdateContext("data.yes.Yes", "data.yes", recursively=False)
assert uc8(copy.deepcopy(data)) == (
0,
{
"data": {
"yes": {"Yes": "data.yes"}
}
}
)
# braces can be only in the beginning and in the end of the string
with pytest.raises(lena.core.LenaValueError):
UpdateContext("data", "{data.yes")
with pytest.raises(lena.core.LenaValueError):
UpdateContext("data", "data.yes}")
with pytest.raises(lena.core.LenaValueError):
UpdateContext("data", "data.ye}s")
with pytest.raises(lena.core.LenaValueError):
UpdateContext("data", "{data.ye}s}")

0 comments on commit f88220f

Please sign in to comment.