Source code for yaml_config.scalars

import pathlib
import re

import yc_yaml as yaml
from .elements import ConfigElement


class ScalarElem(ConfigElement):
    def __init__(self, name=None, **kwargs):
        super().__init__(name=name, _sub_elem=None, **kwargs)

    def _represent(self, value):
        """ We use the built in yaml representation system to 'serialize'
        scalars.
        :returns (value, tag)"""
        # Use the representer from yaml to handle this properly.
        node = self._representer.represent_data(value)
        return node.value, node.tag

    def yaml_events(self, value, show_comments, show_choices):
        # Get our serialized representation of value, and return it as a
        # ScalarEvent.

        tag = None
        if value is not None:
            value, tag = self._represent(value)

        return [yaml.ScalarEvent(value=value, anchor=None, tag=tag,
                                 implicit=(True, True))]

    def find(self, dotted_key):
        if dotted_key != '':
            raise KeyError("Scalars don't have sub-elements, so the only "
                           "valid find  string is '' for them.")

        return self

    def set_default(self, dotted_key, value):
        # We call this just to throw the error for an invalid key.
        self.find(dotted_key)
        self.validate(value)
        self._default = value


[docs]class IntElem(ScalarElem): """An integer configuration element.""" type = int
[docs]class FloatElem(ScalarElem): """A float configuration element.""" type = float
class RangeElem(ScalarElem): def __init__(self, name=None, vmin=None, vmax=None, **kwargs): """ :param vmin: The minimum value for this element, inclusive. :param vmax: The max value, inclusive. """ super(RangeElem, self).__init__(name=name, choices=[vmin, vmax], **kwargs) def _choices_doc(self): if self._choices == (None, None): return None elif self._choices[0] is None: return 'Valid Range: < {}'.format(self._choices[1]) elif self._choices[1] is None: return 'Valid Range: > {}'.format(self._choices[0]) else: return 'Valid Range: {} - {}'.format(*self._choices) def _check_range(self, value): if self._choices[0] is not None and value <= self._choices[0]: raise ValueError("Value {} in {} below minimum ({}).".format( value, self.name, self._choices[0] )) if self._choices[1] is not None and value >= self._choices[1]: raise ValueError("Value {} in {} above maximum ({}).".format( value, self.name, self._choices[1] ))
[docs]class IntRangeElem(IntElem, RangeElem): """An int element with range validation.""" pass
[docs]class FloatRangeElem(FloatElem, RangeElem): """A float with range validation.""" pass
[docs]class BoolElem(ScalarElem): """A boolean element. YAML automatically translates many strings into boolean values.""" type = bool
[docs]class StrElem(ScalarElem): """A basic string element.""" type = str
class PathElem(ScalarElem): """An element that always expects a filesystem path.""" type = pathlib.Path
[docs]class RegexElem(StrElem): """Just like a string item, but validates against a regex."""
[docs] def __init__(self, name=None, regex='', **kwargs): """ :param regex: A regular expression string to match against. """ self.regex = re.compile(regex) super(RegexElem, self).__init__(name=name, choices=[regex], **kwargs)
def _check_range(self, value): if self.regex.match(value) is None: raise ValueError( "Value {} does not match regex '{}' for {} called '{}'".format( value, self._choices[0], self.__class__.__name__, self.name )) def _choices_doc(self): return "Values must match: r'{regex}'".format(regex=self._choices[0])