aboutsummaryrefslogtreecommitdiffstats
path: root/README.rst
diff options
context:
space:
mode:
Diffstat (limited to 'README.rst')
-rw-r--r--README.rst277
1 files changed, 277 insertions, 0 deletions
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..7781245
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,277 @@
+===============================================================================
+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]