Source code for lena.output.make_filename

from __future__ import print_function

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


[docs]class MakeFilename(object): """Make file name, file extension and directory name.""" 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
[docs] def __call__(self, value): """Add *output* keys to the *value*'s context. *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. """ 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 try: res = meth(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 # todo: do I need an option *remove_used*? # I think not. This deletion is natural, # used parts are conserved in created filename. # If filename was not created, # they remain in the context. 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: probably write somewhere how good it was and delete. # 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: return value