from copy import deepcopy
import lena.core
import lena.context
import lena.flow
[документация]class MakeFilename(object):
"""Make file name, file extension and directory name."""
# We don't treat groups of plots specially, because
# one would have to create file name for them from the start.
def __init__(self, filename=None, dirname=None, fileext=None,
prefix=None, suffix=None, overwrite=False):
"""*filename* is a string, which will be
used as a file name without extension
(but it can contain a relative path).
The string can contain formatting arguments
enclosed in double braces.
These arguments will be filled from context
during :meth:`__call__`. Example:
MakeFilename("{{variable.type}}/{{variable.name}}")
*dirname* and *fileext* set directory name and file extension.
They are treated similarly to *filename* in most aspects.
It is possible to "postpone" file name creation, but to provide
a part of a future file name through *prefix* or *suffix*.
They will be appended to file name during its creation.
Existing file names are not affected.
It is not allowed to use *prefix* or *suffix* if *filename*
argument is given.
For example, if one creates logarithmic plots, but complete
file names will be made later,
one may use *MakeFilename(suffix="_log")*.
All these arguments must be strings,
otherwise :exc:`.LenaTypeError` is raised.
They may all contain formatting arguments.
By default, values with *context.output*
already containing *filename*, *dirname* or *fileext*
are not updated (pass unaltered).
This can be changed using a keyword argument *overwrite*.
For more options, use :class:`.lena.context.UpdateContext`.
At least one argument must be present,
or :exc:`.LenaTypeError` will be raised.
"""
self._overwrite = bool(overwrite)
args = [filename, dirname, fileext, prefix, suffix]
args_names = "filename, dirname, fileext, prefix, suffix"
for arg in args:
# we don't check Python 2 basestrings from here.
if arg is not None and not isinstance(arg, str):
raise lena.core.LenaTypeError(
"arguments must be strings, {} "
"provided".format(arg)
)
if filename is not None:
if prefix is not None or suffix is not None:
raise lena.core.LenaTypeError(
"filename is incompatible with "
"prefix and suffix. Provide them separately"
)
# since the ordering of options is not important,
# methods could be a dict.
methods = []
if prefix is not None:
methods.append(("prefix", lena.context.format_context(prefix)))
if suffix is not None:
methods.append(("suffix", lena.context.format_context(suffix)))
if filename is not None:
methods.append(("filename", lena.context.format_context(filename)))
if dirname is not None:
methods.append(("dirname", lena.context.format_context(dirname)))
if fileext is not None:
methods.append(("fileext", lena.context.format_context(fileext)))
if not methods:
# for wrong initialization it must be a TypeError,
# as if it was an obligatory argument.
raise lena.core.LenaTypeError(
"MakeFilename must be initialized with at least "
"one of {}".format(args_names)
)
self._methods = methods
def _set_context(self, context):
self._context = context
[документация] def __call__(self, value):
"""Add *output* keys to the *value*'s context.
Formatting context is retrieved from static context
and from the context part of the *value*.
The run-time context has higher precedence.
*filename*, *dirname*, *fileext*, if initialized,
set respectively *context.output.{filename,dirname,fileext}*
(if they didn't exist).
If this elements sets file name
and if context contains *output.prefix* or *output.suffix*,
they are prepended to or appended after the file name.
After that they are removed from *context.output*.
If this element adds a prefix or a suffix
and they exist in the context,
then *prefix* is prepended before the existing prefix,
and *suffix* is appended after the existing suffix,
unless *overwrite* is set to ``True``:
in that case they are overwritten.
*prefix* and *suffix* always update their existing keys
in the context if they could be formatted
(which is different for attributes like *filename*).
If current context can't be formatted
(doesn't contain all necessary keys for the format string),
a key is not updated.
"""
# data, context = lena.flow.get_data_context(value)
context = lena.flow.get_context(value)
modified = False
for key, meth in self._methods:
if key in ["filename", "fileext", "dirname"]:
if "output" in context and key in context["output"]:
if not self._overwrite:
continue
if hasattr(self, "_context"):
full_context = deepcopy(self._context)
# runtime context takes precedence over the static one
full_context.update(context)
else:
full_context = context
try:
res = meth(full_context)
except lena.core.LenaKeyError:
continue
else:
if key in ["prefix", "suffix"]:
existing = lena.context.get_recursively(
context, "output." + key, None
)
if existing and not self._overwrite:
# prefix is prepended to existing,
# suffix is appended after existing
if key == "prefix":
res = res + existing
else:
res = existing + res
elif key == "filename":
# append existing prefixes and suffixes to filename
prefix = lena.context.get_recursively(
context, "output.prefix", ""
)
suffix = lena.context.get_recursively(
context, "output.suffix", ""
)
res = prefix + res + suffix
# these parts must be deleted, because they are
# already conserved in the created filename,
# and otherwise they would be added
# during each MakeFilename.
if prefix:
del context["output"]["prefix"]
if suffix:
del context["output"]["suffix"]
update = {"output": {key: res}}
lena.context.update_recursively(context, update)
modified = True
# todo: simplify after understanding data-context.
# Just get_data_context in the beginning
# and don't check for modifications.
if modified:
# or created
data = lena.flow.get_data(value)
return (data, context)
else:
# will not create a tuple if we had only the data part
return value