blob: 1032a8744c97fd5687b714259fa622c910b6ee13 [file] [log] [blame]
# -*- coding: utf-8 -*-
"""
Provides an extended :class:`parse.Parser` class that supports the
cardinality fields in (user-defined) types.
"""
from __future__ import absolute_import
import logging
import parse
from .cardinality_field import CardinalityField, CardinalityFieldTypeBuilder
from .parse_util import FieldParser
log = logging.getLogger(__name__) # pylint: disable=invalid-name
class Parser(parse.Parser):
"""Provides an extended :class:`parse.Parser` with cardinality field support.
A cardinality field is a type suffix for parse format expression, ala:
"... {person:Person?} ..." -- OPTIONAL: Cardinality zero or one, 0..1
"... {persons:Person*} ..." -- MANY0: Cardinality zero or more, 0..
"... {persons:Person+} ..." -- MANY: Cardinality one or more, 1..
When the primary type converter for cardinality=1 is provided,
the type variants for the other cardinality cases can be derived from it.
This parser class automatically creates missing type variants for types
with a cardinality field and passes the extended type dictionary
to its base class.
"""
# -- TYPE-BUILDER: For missing types in Fields with CardinalityField part.
type_builder = CardinalityFieldTypeBuilder
def __init__(self, schema, extra_types=None, case_sensitive=False,
type_builder=None):
"""Creates a parser with CardinalityField part support.
:param schema: Parse schema (or format) for parser (as string).
:param extra_types: Type dictionary with type converters (or None).
:param case_sensitive: Indicates if case-sensitive regexp are used.
:param type_builder: Type builder to use for missing types.
"""
if extra_types is None:
extra_types = {}
missing = self.create_missing_types(schema, extra_types, type_builder)
if missing:
# pylint: disable=logging-not-lazy
log.debug("MISSING TYPES: %s" % ",".join(missing.keys()))
extra_types.update(missing)
# -- FINALLY: Delegate to base class.
super(Parser, self).__init__(schema, extra_types,
case_sensitive=case_sensitive)
@classmethod
def create_missing_types(cls, schema, type_dict, type_builder=None):
"""Creates missing types for fields with a CardinalityField part.
It is assumed that the primary type converter for cardinality=1
is registered in the type dictionary.
:param schema: Parse schema (or format) for parser (as string).
:param type_dict: Type dictionary with type converters.
:param type_builder: Type builder to use for missing types.
:return: Type dictionary with missing types. Empty, if none.
:raises: MissingTypeError,
if a primary type converter with cardinality=1 is missing.
"""
if not type_builder:
type_builder = cls.type_builder
missing = cls.extract_missing_special_type_names(schema, type_dict)
return type_builder.create_type_variants(missing, type_dict)
@staticmethod
def extract_missing_special_type_names(schema, type_dict):
# pylint: disable=invalid-name
"""Extract the type names for fields with CardinalityField part.
Selects only the missing type names that are not in the type dictionary.
:param schema: Parse schema to use (as string).
:param type_dict: Type dictionary with type converters.
:return: Generator with missing type names (as string).
"""
for name in FieldParser.extract_types(schema):
if CardinalityField.matches_type(name) and (name not in type_dict):
yield name