| =============================================================================== |
| parse_type |
| =============================================================================== |
| |
| .. image:: https://img.shields.io/travis/jenisys/parse_type/master.svg |
| :target: https://travis-ci.org/jenisys/parse_type |
| :alt: Travis CI Build Status |
| |
| .. image:: https://img.shields.io/pypi/v/parse_type.svg |
| :target: https://pypi.python.org/pypi/parse_type |
| :alt: Latest Version |
| |
| .. image:: https://img.shields.io/pypi/dm/parse_type.svg |
| :target: https://pypi.python.org/pypi/parse_type |
| :alt: Downloads |
| |
| .. image:: https://img.shields.io/pypi/l/parse_type.svg |
| :target: https://pypi.python.org/pypi/parse_type/ |
| :alt: License |
| |
| |
| `parse_type`_ extends the `parse`_ module (opposite of `string.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 converters |
| * an extended parser that supports the CardinalityField naming schema |
| and creates missing type variants (0..1, 0..*, 1..*) from the |
| primary type converter |
| |
| .. _parse_type: http://pypi.python.org/pypi/parse_type |
| .. _parse: http://pypi.python.org/pypi/parse |
| .. _`string.format()`: http://docs.python.org/library/string.html#format-string-syntax |
| |
| |
| Definitions |
| ------------------------------------------------------------------------------- |
| |
| *type converter* |
| A 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 the `parse`_ module to use it in a generic way. |
| A type converter is also called a *parse_type* (a definition used here). |
| |
| *cardinality field* |
| A 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 Example |
| ------------------------------------------------------------------------------- |
| |
| Define an own type converter for numbers (integers): |
| |
| .. code-block:: python |
| |
| # -- USE CASE: |
| def parse_number(text): |
| return int(text) |
| parse_number.pattern = r"\d+" # -- REGULAR EXPRESSION pattern for type. |
| |
| This is equivalent to: |
| |
| .. code-block:: python |
| |
| import parse |
| |
| @parse.with_pattern(r"\d+") |
| def parse_number(text): |
| return int(text) |
| assert hasattr(parse_number, "pattern") |
| assert parse_number.pattern == r"\d+" |
| |
| |
| .. code-block:: python |
| |
| # -- 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") |
| assert result is not None, "REQUIRE: text matches the schema." |
| assert result["number"] == 42 |
| |
| result = parser.parse("Hello XXX") |
| assert result is None, "MISMATCH: text does not match the schema." |
| |
| .. hint:: |
| |
| The described functionality above is standard functionality |
| of the `parse`_ module. It serves as introduction for the remaining cases. |
| |
| |
| Cardinality |
| ------------------------------------------------------------------------------- |
| |
| Create an type converter for "ManyNumbers" (List, separated with commas) |
| with cardinality "1..* = 1+" (many) from the type converter for a "Number". |
| |
| .. code-block:: python |
| |
| # -- USE CASE: Create new type converter with a cardinality constraint. |
| # CARDINALITY: many := one or more (1..*) |
| from parse import Parser |
| from parse_type import TypeBuilder |
| parse_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") |
| assert result["numbers"] == [1, 2, 3] |
| |
| |
| Create an type converter for an "OptionalNumbers" with cardinality "0..1 = ?" |
| (optional) from the type converter for a "Number". |
| |
| .. code-block:: python |
| |
| # -- USE CASE: Create new type converter with cardinality constraint. |
| # CARDINALITY: optional := zero or one (0..1) |
| from parse import Parser |
| from parse_type import TypeBuilder |
| |
| parse_optional_number = TypeBuilder.with_optional(parse_number) |
| schema = "Optional: {number:OptionalNumber}" |
| parser = Parser(schema, dict(OptionalNumber=parse_optional_number)) |
| result = parser.parse("Optional: 42") |
| assert result["number"] == 42 |
| result = parser.parse("Optional: ") |
| assert result["number"] == None |
| |
| |
| Enumeration (Name-to-Value Mapping) |
| ------------------------------------------------------------------------------- |
| |
| Create an type converter for an "Enumeration" from the description of |
| the mapping as dictionary. |
| |
| .. code-block:: python |
| |
| # -- USE CASE: Create a type converter for an enumeration. |
| from parse import Parser |
| from parse_type import TypeBuilder |
| |
| parse_enum_yesno = TypeBuilder.make_enum({"yes": True, "no": False}) |
| parser = Parser("Answer: {answer:YesNo}", dict(YesNo=parse_enum_yesno)) |
| result = parser.parse("Answer: yes") |
| assert result["answer"] == True |
| |
| |
| Create an type converter for an "Enumeration" from the description of |
| the mapping as an enumeration class (`Python 3.4 enum`_ or the `enum34`_ |
| backport; see also: `PEP-0435`_). |
| |
| .. code-block:: python |
| |
| # -- USE CASE: Create a type converter for enum34 enumeration class. |
| # NOTE: Use Python 3.4 or enum34 backport. |
| from parse import Parser |
| from parse_type import TypeBuilder |
| from enum import Enum |
| |
| class Color(Enum): |
| red = 1 |
| green = 2 |
| blue = 3 |
| |
| parse_enum_color = TypeBuilder.make_enum(Color) |
| parser = Parser("Select: {color:Color}", dict(Color=parse_enum_color)) |
| result = parser.parse("Select: red") |
| assert result["color"] is Color.red |
| |
| .. _`Python 3.4 enum`: http://docs.python.org/3.4/library/enum.html#module-enum |
| .. _enum34: http://pypi.python.org/pypi/enum34 |
| .. _PEP-0435: http://www.python.org/dev/peps/pep-0435 |
| |
| |
| Choice (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). |
| |
| .. code-block:: python |
| |
| from parse import Parser |
| from parse_type import TypeBuilder |
| |
| parse_choice_yesno = TypeBuilder.make_choice(["yes", "no"]) |
| schema = "Answer: {answer:ChoiceYesNo}" |
| parser = Parser(schema, dict(ChoiceYesNo=parse_choice_yesno)) |
| result = parser.parse("Answer: yes") |
| assert result["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) |
| |
| .. code-block:: python |
| |
| from parse import Parser, with_pattern |
| from parse_type import TypeBuilder |
| from enum import Enum |
| |
| class Color(Enum): |
| red = 1 |
| green = 2 |
| blue = 3 |
| |
| @with_pattern(r"\d+") |
| def parse_number(text): |
| return int(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") |
| assert result["variant"] == 42 |
| result = parser.parse("Variant: blue") |
| assert result["variant"] is Color.blue |
| result = parser.parse("Variant: __MISMATCH__") |
| assert not result |
| |
| |
| |
| Extended Parser with CardinalityField support |
| ------------------------------------------------------------------------------- |
| |
| The parser extends the ``parse.Parser`` and adds the following functionality: |
| |
| * supports the CardinalityField naming scheme |
| * automatically creates missing type variants for types with |
| a CardinalityField by using the primary type converter for cardinality=1 |
| * extends the provide type converter dictionary with new type variants. |
| |
| Example: |
| |
| .. code-block:: python |
| |
| # -- USE CASE: Parser with CardinalityField support. |
| # NOTE: Automatically adds missing type variants with CardinalityField part. |
| # USE: parse_number() type converter from above. |
| from parse_type.cfparse import Parser |
| |
| # -- 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+" in type_dict, "Created missing type variant based on: Number" |
| |
| # -- USE: parser. |
| result = parser.parse("List: 1, 2, 3") |
| assert result["numbers"] == [1, 2, 3] |