Пример #1
0
def get_regex_padding(padmin=None, padmax=None):
    """
    Gets a regex pattern for enforcing padding size on other patterns. Defaults
    to an unlimited size (minimum 1) if neither value is provided.

    Args:
        padmin (int): Minimum number of characters required
        padmax (int): Maximum number of characters allowed

    Returns:
        str: Regex padding symbol(s) to append to a regex pattern
    """
    if padmin is not None and padmin < 0:
        raise exceptions.ResolverError(
            "Padmin cannot be less than 0: {}".format(padmin))
    if padmax is not None and padmax < 0:
        raise exceptions.ResolverError(
            "Padmax cannot be less than 0: {}".format(padmax))

    if padmin is not None and padmax is not None:
        if padmax < padmin:
            raise exceptions.ResolverError(
                "Padmax ({}) cannot be lower than padmin ({})".format(
                    padmax, padmin))
        padding_str = "{%d,%d}" % (padmin, padmax)
    elif padmin is not None:
        padding_str = "{%d,}" % padmin
    elif padmax is not None:
        padding_str = "{,%d}" % padmax
    else:
        padding_str = "+"
    return padding_str
Пример #2
0
    def create_token(self, token_name, token_config):
        """
        Raises:
            exceptions.ResolverError: If the token data is invalid

        Args:
            token_name (str): Name of the token to create
            token_config (dict): Dictionary of token data, with a minimum of a
                "type" key.

        Returns:
            token.Token: Created token object stored in the resolver
        """
        if token_name in self._tokens:
            raise exceptions.ResolverError(
                "Token '{}' already exists".format(token_name)
            )

        token_type = token_config[constants.KEY_TYPE]
        token_cls = self.get_token_cls(token_type)
        regex = token_cls.get_regex_from_config(token_config)
        format_spec = token_cls.get_format_spec_from_config(token_config)
        description = token_cls.get_description_from_config(token_config)
        default = token_config.get("default")
        token_obj = token_cls(
            token_name,
            regex=regex,
            format_spec=format_spec,
            description=description,
            default=default,
        )

        self._tokens[token_name] = token_obj
        return token_obj
Пример #3
0
    def get_token_cls(cls, token_type):
        """
        Args:
            token_type (str): String name of the token type

        Returns:
            Type[token.Token]: Token class the name represents
        """
        if token_type == constants.TokenType.Int:
            token_cls = token.IntToken
        elif token_type == constants.TokenType.String:
            token_cls = token.StringToken
        else:
            raise exceptions.ResolverError("Unknown token type: {}".format(token_type))

        return token_cls
Пример #4
0
    def token(self, name):
        """
        Raises:
            exceptions.ResolverError: If no token exists matching the name

        Args:
            name (str): Name of the token to get

        Returns:
            token.Token:
        """
        token_obj = self._tokens.get(name)
        if token_obj is None:
            raise exceptions.ResolverError(
                "Requested token name does not exist: {}".format(name)
            )
        return token_obj
Пример #5
0
    def template(self, name):
        """
        Raises:
            exceptions.ResolverError: If no template exists matching the name

        Args:
            name (str): Name of the template to get

        Returns:
            template.Template:
        """
        template_obj = self._templates.get(name)
        if template_obj is None:
            raise exceptions.ResolverError(
                "Requested template name does not exist: {}".format(name)
            )
        return template_obj
Пример #6
0
    def get_template_cls(cls, template_type):
        """
        Args:
            template_type (str): String name of the template type

        Returns:
            Type[token.Template]: Template class the name represents
        """
        if template_type == constants.TemplateType.Standard:
            token_cls = template.Template
        elif template_type == constants.TemplateType.Path:
            token_cls = pathtemplate.PathTemplate
        else:
            raise exceptions.ResolverError(
                "Unknown template type: {}".format(template_type)
            )

        return token_cls
Пример #7
0
    def get_regex_from_config(cls, config):
        """
        Args:
            config (dict): Dictionary of token configuration values

        Returns:
            str: Regex pattern for the token
        """
        regex = config.get("regex")
        choices = config.get("choices")
        padmin = config.get("padmin")
        padmax = config.get("padmax")
        if regex is None:
            if choices:
                regex = "|".join(map(str, choices))
            else:
                regex = cls.REGEX
                regex += util.get_regex_padding(padmin=padmin, padmax=padmax)
        elif choices or padmin or padmax:
            raise exceptions.ResolverError(
                "Cannot use construction keywords with explicit regex")

        return regex
Пример #8
0
    def create_template(self, template_name, template_config, reference_config=None):
        """
        Raises:
            exceptions.ResolverError: If the template string references a
                non-existent value

        Args:
            template_name (str): Name of the template to create
            template_config (dict): Dictionary of template data with a minimum
                of a "type" and "string" key

        Keyword Args:
            reference_config (dict): Dictionary of template names mapped to
                template configs. If creating a template which references
                additional templates that does not exist yet, the resolver will
                try to recursively construct templates from this config.

        Returns:
            template.Template: Created template object stored in the resolver
        """
        if template_name in self._templates:
            raise exceptions.ResolverError(
                "Template '{}' already exists".format(template_name)
            )

        template_type = template_config[constants.KEY_TYPE]
        template_string = template_config[constants.KEY_STRING]

        index = 0
        segments = []
        for match in re.finditer(constants.TOKEN_PATTERN, template_string):
            # Extract fixed string segments between token/templates
            start, end = match.span()
            if start != index:
                segments.append(template_string[index:start])
            index = end

            # Find the matching referenced object
            symbol, name = match.groups()
            if symbol == constants.SYMBOL_TEMPLATE:
                try:
                    template_obj = self.template(name)
                except exceptions.ResolverError:
                    if reference_config is None or name not in reference_config:
                        raise
                    template_obj = self.create_template(
                        name, reference_config[name], reference_config=reference_config
                    )
                segments.append(template_obj)
            elif not symbol:
                token_obj = self.token(name)
                segments.append(token_obj)
            else:
                raise exceptions.ResolverError(
                    "Unknown token symbol: {}".format(symbol)
                )

        # If it ends with a fixed string, ensure the remainder is added
        last_string_segment = template_string[index:]
        if last_string_segment:
            segments.append(last_string_segment)

        template_cls = self.get_template_cls(template_type)
        template_obj = template_cls(template_name, segments)
        self._templates[template_name] = template_obj
        return template_obj
Пример #9
0
class StringToken(Token):
    PADALIGN = constants.DEFAULT_PADALIGN_STR
    PADCHAR = constants.DEFAULT_PADCHAR_STR
    REGEX = constants.REGEX_STR

    @classmethod
    def get_description_from_config(cls, config):
        """
        Args:
            config (dict): Dictionary of token configuration values

        Returns:
            str: Description message for the token
        """
        description = super(StringToken,
                            cls).get_description_from_config(config)
        if description is None:
            padmin = config.get("padmin")
            padmax = config.get("padmax")
            case = config.get("case")
            case = "{} case ".format(case) if case else ""

            if padmin is not None and padmin == padmax:
                description = "Must be a {}-character {}string".format(
                    padmin, case)
            elif padmin is not None:
                description = "Must be a minimum {}-character {}string".format(
                    padmin, case)
            elif padmax is not None:
                description = "Must be a maximum {}-character {}string".format(
                    padmax, case)
            else:
                description = "Must be a {}string".format(case)
        return description

    @classmethod
    def get_format_spec_from_config(cls, config):
        """
        Args:
            config (dict): Dictionary of token configuration values

        Returns:
            str: Format spec for the token with the string "s" appended
        """
        # Enable strict padding by default unless set
        config.setdefault("padstrict", True)
        format_spec = super(StringToken,
                            cls).get_format_spec_from_config(config)
        return format_spec + "s"

    @classmethod
    def get_regex_from_config(cls, config):
        """
        Args:
            config (dict): Dictionary of token configuration values

        Returns:
            str: Regex pattern for the token
        """
        case = config.get("case")
        regex = config.get("regex")
        if regex is None and case:
            regex = util.get_case_regex(case)
            padmin = config.get("padmin")
            padmax = config.get("padmax")
            # Camel cases add a fixed starting character, modify padding accordingly
            if case in (constants.Case.LowerCamel, constants.Case.UpperCamel):
                padmin = (padmin - 1) if padmin else None
                padmax = (padmax - 1) if padmax else None
            regex += util.get_regex_padding(padmin=padmin, padmax=padmax)
        elif case is not None:
            raise exceptions.ResolverError(
                "Cannot use construction keywords with explicit regex")