Source code for lena.meta.elements
from copy import deepcopy
from lena.core import LenaKeyError
from lena.context import format_update_with, update_recursively
[docs]
class SetContext(object):
"""Set static context for this sequence.
Static context does not automatically update runtime context.
Use :class:`UpdateContextFromStatic` for that.
Static context can be used during the initialisation phase
to set output directories, :class:`.Cache` names, etc.
There is no way to update static context from runtime one.
"""
def __init__(self, key, value):
"""*key* is a string representing a
(possibly nested) dictionary key. *value* is its value.
*value* can be a formatting string.
See :func:`.format_context` for details.
If *value* could not be formatted,
:exc:`.LenaKeyError` is raised.
"""
# todo: key could be an entire dictionary
# self._subcontext = lena.context.str_to_list(key)
# self._value = value
self._key = key
self._value = value
self._has_no_data = True
try:
self._set_context({})
# may also raise formatting errors, checking early here.
except LenaKeyError:
# formatting keys missing
pass
def _get_context(self):
try:
sc = self._static_context
except AttributeError:
# KeyError stored during _set_context
# in order to assist the user in finding the error.
raise self._exc
# we want to keep our context safe, therefore deepcopy.
# It makes little difference to optimise in a Sequence.
return deepcopy(sc)
def _set_context(self, context):
# we assume that context was already deeply copied if needed
try:
format_update_with(self._key, self._value, context)
except LenaKeyError as exc:
self._exc = exc
raise exc
else:
self._static_context = context
def __eq__(self, other):
if not isinstance(other, SetContext):
return NotImplemented
# we compare key and value, not the formatted value,
# because we are comparing elements, not their environment.
return (self._key == other._key and self._value == other._value)
def __repr__(self):
val = self._value
if isinstance(val, str):
val = '"' + val + '"'
# representation like it was during the initialisation,
# not with the set context.
return 'SetContext("{}", {})'.format(self._key, val)
[docs]
class StoreContext():
"""Store static context. Use for debugging."""
def __init__(self):
# todo: make context a read-only property.
# todo: add to docstring.
self.context = {}
# no need to print during setting context.
# Patch this code manually if needed.
# self._verbose = verbose
self._has_no_data = True
def _set_context(self, context):
# since there is no _get_context, we need to make
# a deep copy here, otherwise further elements
# might influence our context.
self.context = deepcopy(context)
def __eq__(self, other):
if not isinstance(other, StoreContext):
return NotImplemented
return (
# will be set in the sequence, not during the initialisation
self.context == other.context
)
def __repr__(self):
# we want our element to be able to be initialised
# by copying its representation,
# and Python has no inline commentaries
return 'StoreContext() or "{}"'.format(repr(self.context))
[docs]
class UpdateContextFromStatic(object):
"""Update runtime context with the static one.
Note that for runtime context later elements
update previous values, but for static context
it is the opposite (external and previous elements
take precedence).
"""
def __init__(self):
self._context = {}
def __eq__(self, other):
if not isinstance(other, UpdateContextFromStatic):
return NotImplemented
return self._context == other._context
def _set_context(self, context):
self._context = context
def run(self, flow):
for val in flow:
data, context = val
# no template substitutions are done,
# that would be too complicated, fragile and wrong
update_recursively(context, deepcopy(self._context))
yield (data, context)