Source code for pyts.io.formatter

from string import Formatter


[docs]class SuperFormatter(Formatter): r""" SuperFormatter adds the following capabilities: 1. Initialize with a template string, and the :meth:`__call__` method uses this string. Thus, example usage of this formatter looks like:: template = SuperFormatter(template_string) out_string = template(*args, **kwargs) 2. White space at the end of a format specifier is stripped. This allows for aligning text within the template. 3. Multiple format strings separated by ``|`` can be specified within a template. This formatter will loop over the format strings until it finds one that doesn't throw a ValueError. For example, the format string ``6d|<6.3f|8s`` will format the following objects as: +----------------+------------------+ | input | output string | +================+==================+ | ``3.74583754`` | ``'3.746 '`` | +----------------+------------------+ | ``384`` | ``' 384'`` | +----------------+------------------+ | ``None`` | ``'None '``| +----------------+------------------+ 4. Default values may be specified after a ``/`` at the end of the format string. For example if the container is ``{show_data:s/False}``, and there is no key ``show_data`` in ``**kwargs``, then ``False`` will fill that location. 5. The :attr:`format_prfx` attribute allows the user to define a default container prefix. This will be prepended to all format specifiers that are a single-character `type` specifier. For example if ``format_prfx = '<20'``, then the format specifier ``'f'`` will be changed to ``'<20f'``, but ``'>08.3f'`` will be unchanged. This is applied to each specifier within a multiple specification, thus ``'d|f|s'`` would actually be ``'<20d|<20f|<20s'``. 6. Custom format specifiers have been implemented by adding a hook that searches for a `_format_<specifier>` method prior to running the normal formatting routines. That method takes the value to be formatted as input (in addition to *self*), and should return the fully-formatted string (no further formatting is applied). For example, a custom format specifier `pet` (specified as ``{my_dog:pet}`` in the template) could be defined as:: class MyNewFormatter(SuperFormatter): def _format_pet(self, value): return value.upper() Note that this will throw an ``AttributeError`` if *my_dog* is an object without an ``upper`` method (i.e. not a string), but you could add to the method to handle all of the different types that ``value`` might be. 7. Custom format specifiers with arguments can be specified as ``{my_dogs:pets(10s,10s)}``. In this case the string inside the parenthesis is supplied as the second argument to the ``_format_pets`` method. The method that implements this format could be defined as:: class MyNewFormatter(SuperFormatter): def _format_pets(self, value, form2): out = '' for v,f in zip(value, form2.split(',')): out += format(v, f) return out """ format_prfx = '' default_format_prfx = '' allow_sloppy = False def __init__(self, template): # Override the base methods to initialize the formatter with # the template string. self.template = template
[docs] def __call__(self, *args, **kwargs): r""" Format the template string with `*args` and `**kwargs`. """ return self.format(self.template, *args, **kwargs)
def __iter__(self,): return self.parse(self.template)
[docs] def get_value(self, key, args, kwargs): key = key.rstrip() self._current_name = key if isinstance(key, (int, long)): return args[key] else: try: return kwargs[key] except KeyError: return None
def _fail(self): if self.allow_sloppy: return '??SOME JUNK??' else: # This _current_name business is a DIRTY HACK. raise KeyError("'%s' not specified and no default " "value found in template." % self._current_name) def _format_default(self, default_val): return format(default_val, self.default_format_prfx + 's')
[docs] def format_field(self, value, format_spec): format_spec = format_spec.rstrip() # Strip trailing spaces default_val = None if '/' in format_spec: format_spec, default_val = format_spec.split('/', 1) # set the default value if there is no input if value is None: return self._format_default(default_val) elif value is None: return self._fail() if '|' in format_spec: format_spec = format_spec.split('|') else: format_spec = [format_spec] for form in format_spec: formtail = None if '(' in form and form.endswith(')'): form, formtail = form.split('(', 1) formtail = formtail[:-1] try: if hasattr(self, '_format_' + form): if formtail is None: return getattr(self, '_format_' + form)(value) else: return getattr(self, '_format_' + form)(value, formtail) if form in ["b", "c", "d", "e", "E", "f", "F", "g", "G", "n", "o", "s", "x", "X", "%", '']: form = self.format_prfx + form return format(value, form) except ValueError: pass except TypeError: pass # Finally, try the default again: if default_val is None: raise ValueError('Invalid conversion specification') return self._format_default(default_val)