Package: parse-type
parse_typeextends theparsemodule (opposite ofstring.format())
with the following features:build type converters for common use cases (enum/mapping, choice)build a type converter with a cardinality constraint (0..1, 0..*, 1..*)from the type converter with cardinality=1.compose a type converter from other type convertersan extended parser that supports the CardinalityField naming schemaand creates missing type variants (0..1, 0..*, 1..*) from the
primary type converterDefinitionstype converterA type converter function that converts a textual representation
of a value type into instance of this value type.
In addition, a type converter function is often annotated with attributes
that allows theparsemodule to use it in a generic way.
A type converter is also called aparse_type(a definition used here).cardinality fieldA naming convention for related types that differ in cardinality.
A cardinality field is a type name suffix in the format of a field.
It allows parse format expression, ala:"{person:Person}"     #< Cardinality: 1    (one; the normal case)
"{person:Person?}"    #< Cardinality: 0..1 (zero or one  = optional)
"{persons:Person*}"   #< Cardinality: 0..* (zero or more = many0)
"{persons:Person+}"   #< Cardinality: 1..* (one  or more = many)This naming convention mimics the relationship descriptions in UML diagrams.Basic ExampleDefine an own type converter for numbers (integers):# -- USE CASE:defparse_number(text):returnint(text)parse_number.pattern=r"\d+"# -- REGULAR EXPRESSION pattern for type.This is equivalent to:importparse@parse.with_pattern(r"\d+")defparse_number(text):returnint(text)asserthasattr(parse_number,"pattern")assertparse_number.pattern==r"\d+"# -- USE CASE: Use the type converter with the parse module.schema="Hello {number:Number}"parser=parse.Parser(schema,dict(Number=parse_number))result=parser.parse("Hello 42")assertresultisnotNone,"REQUIRE: text matches the schema."assertresult["number"]==42result=parser.parse("Hello XXX")assertresultisNone,"MISMATCH: text does not match the schema."HintThe described functionality above is standard functionality
of theparsemodule. It serves as introduction for the remaining cases.CardinalityCreate an type converter for “ManyNumbers” (List, separated with commas)
with cardinality “1..* = 1+” (many) from the type converter for a “Number”.# -- USE CASE: Create new type converter with a cardinality constraint.# CARDINALITY: many := one or more (1..*)fromparseimportParserfromparse_typeimportTypeBuilderparse_numbers=TypeBuilder.with_many(parse_number,listsep=",")schema="List: {numbers:ManyNumbers}"parser=Parser(schema,dict(ManyNumbers=parse_numbers))result=parser.parse("List: 1, 2, 3")assertresult["numbers"]==[1,2,3]Create an type converter for an “OptionalNumbers” with cardinality “0..1 = ?”
(optional) from the type converter for a “Number”.# -- USE CASE: Create new type converter with cardinality constraint.# CARDINALITY: optional := zero or one (0..1)fromparseimportParserfromparse_typeimportTypeBuilderparse_optional_number=TypeBuilder.with_optional(parse_number)schema="Optional: {number:OptionalNumber}"parser=Parser(schema,dict(OptionalNumber=parse_optional_number))result=parser.parse("Optional: 42")assertresult["number"]==42result=parser.parse("Optional: ")assertresult["number"]==NoneEnumeration (Name-to-Value Mapping)Create an type converter for an “Enumeration” from the description of
the mapping as dictionary.# -- USE CASE: Create a type converter for an enumeration.fromparseimportParserfromparse_typeimportTypeBuilderparse_enum_yesno=TypeBuilder.make_enum({"yes":True,"no":False})parser=Parser("Answer: {answer:YesNo}",dict(YesNo=parse_enum_yesno))result=parser.parse("Answer: yes")assertresult["answer"]==TrueCreate an type converter for an “Enumeration” from the description of
the mapping as an enumeration class (Python 3.4 enumor theenum34backport; see also:PEP-0435).# -- USE CASE: Create a type converter for enum34 enumeration class.# NOTE: Use Python 3.4 or enum34 backport.fromparseimportParserfromparse_typeimportTypeBuilderfromenumimportEnumclassColor(Enum):red=1green=2blue=3parse_enum_color=TypeBuilder.make_enum(Color)parser=Parser("Select: {color:Color}",dict(Color=parse_enum_color))result=parser.parse("Select: red")assertresult["color"]isColor.redChoice (Name Enumeration)A Choice data type allows to select one of several strings.Create an type converter for an “Choice” list, a list of unique names
(as string).fromparseimportParserfromparse_typeimportTypeBuilderparse_choice_yesno=TypeBuilder.make_choice(["yes","no"])schema="Answer: {answer:ChoiceYesNo}"parser=Parser(schema,dict(ChoiceYesNo=parse_choice_yesno))result=parser.parse("Answer: yes")assertresult["answer"]=="yes"Variant (Type Alternatives)Sometimes you need a type converter that can accept text for multiple
type converter alternatives. This is normally called a “variant” (or: union).Create an type converter for an “Variant” type that accepts:Numbers (positive numbers, as integer)Color enum values (by name)fromparseimportParser,with_patternfromparse_typeimportTypeBuilderfromenumimportEnumclassColor(Enum):red=1green=2blue=3@with_pattern(r"\d+")defparse_number(text):returnint(text)# -- MAKE VARIANT: Alternatives of different type converters.parse_color=TypeBuilder.make_enum(Color)parse_variant=TypeBuilder.make_variant([parse_number,parse_color])schema="Variant: {variant:Number_or_Color}"parser=Parser(schema,dict(Number_or_Color=parse_variant))# -- TEST VARIANT: With number, color and mismatch.result=parser.parse("Variant: 42")assertresult["variant"]==42result=parser.parse("Variant: blue")assertresult["variant"]isColor.blueresult=parser.parse("Variant: __MISMATCH__")assertnotresultExtended Parser with CardinalityField supportThe parser extends theparse.Parserand adds the following functionality:supports the CardinalityField naming schemeautomatically creates missing type variants for types with
a CardinalityField by using the primary type converter for cardinality=1extends the provide type converter dictionary with new type variants.Example:# -- USE CASE: Parser with CardinalityField support.# NOTE: Automatically adds missing type variants with CardinalityField part.# USE:  parse_number() type converter from above.fromparse_type.cfparseimportParser# -- PREPARE: parser, adds missing type variant for cardinality 1..* (many)type_dict=dict(Number=parse_number)schema="List: {numbers:Number+}"parser=Parser(schema,type_dict)assert"Number+"intype_dict,"Created missing type variant based on: Number"# -- USE: parser.result=parser.parse("List: 1, 2, 3")assertresult["numbers"]==[1,2,3]