parse-type


Nameparse-type JSON
Version 0.6.2 PyPI version JSON
download
home_pagehttps://github.com/jenisys/parse_type
SummarySimplifies to build parse types based on the parse module
upload_time2023-07-05 19:11:36
maintainer
docs_urlNone
authorJens Engel
requires_python>=2.7, !=3.0.*, !=3.1.*
licenseMIT
keywords parse parsing
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            ===============================================================================
parse_type
===============================================================================

.. image:: https://github.com/jenisys/parse_type/actions/workflows/test.yml/badge.svg
    :target: https://github.com/jenisys/parse_type/actions/workflows/test.yml
    :alt: 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]

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/jenisys/parse_type",
    "name": "parse-type",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=2.7, !=3.0.*, !=3.1.*",
    "maintainer_email": "",
    "keywords": "parse,parsing",
    "author": "Jens Engel",
    "author_email": "Jens Engel <jenisys@noreply.github.com>",
    "download_url": "https://files.pythonhosted.org/packages/47/32/416e1d54fae3150def1b2db06dca12c5c5be3d1f1ab75c93d5cb21b2b23c/parse_type-0.6.2.tar.gz",
    "platform": "any",
    "description": "===============================================================================\nparse_type\n===============================================================================\n\n.. image:: https://github.com/jenisys/parse_type/actions/workflows/test.yml/badge.svg\n    :target: https://github.com/jenisys/parse_type/actions/workflows/test.yml\n    :alt: CI Build Status\n\n.. image:: https://img.shields.io/pypi/v/parse_type.svg\n    :target: https://pypi.python.org/pypi/parse_type\n    :alt: Latest Version\n\n.. image:: https://img.shields.io/pypi/dm/parse_type.svg\n    :target: https://pypi.python.org/pypi/parse_type\n    :alt: Downloads\n\n.. image:: https://img.shields.io/pypi/l/parse_type.svg\n    :target: https://pypi.python.org/pypi/parse_type/\n    :alt: License\n\n\n`parse_type`_ extends the `parse`_ module (opposite of `string.format()`_)\nwith the following features:\n\n* build type converters for common use cases (enum/mapping, choice)\n* build a type converter with a cardinality constraint (0..1, 0..*, 1..*)\n    from the type converter with cardinality=1.\n* compose a type converter from other type converters\n* an extended parser that supports the CardinalityField naming schema\n    and creates missing type variants (0..1, 0..*, 1..*) from the\n    primary type converter\n\n.. _parse_type: http://pypi.python.org/pypi/parse_type\n.. _parse:      http://pypi.python.org/pypi/parse\n.. _`string.format()`: http://docs.python.org/library/string.html#format-string-syntax\n\n\nDefinitions\n-------------------------------------------------------------------------------\n\n*type converter*\n    A type converter function that converts a textual representation\n    of a value type into instance of this value type.\n    In addition, a type converter function is often annotated with attributes\n    that allows the `parse`_ module to use it in a generic way.\n    A type converter is also called a *parse_type* (a definition used here).\n\n*cardinality field*\n    A naming convention for related types that differ in cardinality.\n    A cardinality field is a type name suffix in the format of a field.\n    It allows parse format expression, ala::\n\n        \"{person:Person}\"     #< Cardinality: 1    (one; the normal case)\n        \"{person:Person?}\"    #< Cardinality: 0..1 (zero or one  = optional)\n        \"{persons:Person*}\"   #< Cardinality: 0..* (zero or more = many0)\n        \"{persons:Person+}\"   #< Cardinality: 1..* (one  or more = many)\n\n    This naming convention mimics the relationship descriptions in UML diagrams.\n\n\nBasic Example\n-------------------------------------------------------------------------------\n\nDefine an own type converter for numbers (integers):\n\n.. code-block:: python\n\n    # -- USE CASE:\n    def parse_number(text):\n        return int(text)\n    parse_number.pattern = r\"\\d+\"  # -- REGULAR EXPRESSION pattern for type.\n\nThis is equivalent to:\n\n.. code-block:: python\n\n    import parse\n\n    @parse.with_pattern(r\"\\d+\")\n    def parse_number(text):\n         return int(text)\n    assert hasattr(parse_number, \"pattern\")\n    assert parse_number.pattern == r\"\\d+\"\n\n\n.. code-block:: python\n\n    # -- USE CASE: Use the type converter with the parse module.\n    schema = \"Hello {number:Number}\"\n    parser = parse.Parser(schema, dict(Number=parse_number))\n    result = parser.parse(\"Hello 42\")\n    assert result is not None, \"REQUIRE: text matches the schema.\"\n    assert result[\"number\"] == 42\n\n    result = parser.parse(\"Hello XXX\")\n    assert result is None, \"MISMATCH: text does not match the schema.\"\n\n.. hint::\n\n    The described functionality above is standard functionality\n    of the `parse`_ module. It serves as introduction for the remaining cases.\n\n\nCardinality\n-------------------------------------------------------------------------------\n\nCreate an type converter for \"ManyNumbers\" (List, separated with commas)\nwith cardinality \"1..* = 1+\" (many) from the type converter for a \"Number\".\n\n.. code-block:: python\n\n    # -- USE CASE: Create new type converter with a cardinality constraint.\n    # CARDINALITY: many := one or more (1..*)\n    from parse import Parser\n    from parse_type import TypeBuilder\n    parse_numbers = TypeBuilder.with_many(parse_number, listsep=\",\")\n\n    schema = \"List: {numbers:ManyNumbers}\"\n    parser = Parser(schema, dict(ManyNumbers=parse_numbers))\n    result = parser.parse(\"List: 1, 2, 3\")\n    assert result[\"numbers\"] == [1, 2, 3]\n\n\nCreate an type converter for an \"OptionalNumbers\" with cardinality \"0..1 = ?\"\n(optional) from the type converter for a \"Number\".\n\n.. code-block:: python\n\n    # -- USE CASE: Create new type converter with cardinality constraint.\n    # CARDINALITY: optional := zero or one (0..1)\n    from parse import Parser\n    from parse_type import TypeBuilder\n\n    parse_optional_number = TypeBuilder.with_optional(parse_number)\n    schema = \"Optional: {number:OptionalNumber}\"\n    parser = Parser(schema, dict(OptionalNumber=parse_optional_number))\n    result = parser.parse(\"Optional: 42\")\n    assert result[\"number\"] == 42\n    result = parser.parse(\"Optional: \")\n    assert result[\"number\"] == None\n\n\nEnumeration (Name-to-Value Mapping)\n-------------------------------------------------------------------------------\n\nCreate an type converter for an \"Enumeration\" from the description of\nthe mapping as dictionary.\n\n.. code-block:: python\n\n    # -- USE CASE: Create a type converter for an enumeration.\n    from parse import Parser\n    from parse_type import TypeBuilder\n\n    parse_enum_yesno = TypeBuilder.make_enum({\"yes\": True, \"no\": False})\n    parser = Parser(\"Answer: {answer:YesNo}\", dict(YesNo=parse_enum_yesno))\n    result = parser.parse(\"Answer: yes\")\n    assert result[\"answer\"] == True\n\n\nCreate an type converter for an \"Enumeration\" from the description of\nthe mapping as an enumeration class (`Python 3.4 enum`_ or the `enum34`_\nbackport; see also: `PEP-0435`_).\n\n.. code-block:: python\n\n    # -- USE CASE: Create a type converter for enum34 enumeration class.\n    # NOTE: Use Python 3.4 or enum34 backport.\n    from parse import Parser\n    from parse_type import TypeBuilder\n    from enum import Enum\n\n    class Color(Enum):\n        red   = 1\n        green = 2\n        blue  = 3\n\n    parse_enum_color = TypeBuilder.make_enum(Color)\n    parser = Parser(\"Select: {color:Color}\", dict(Color=parse_enum_color))\n    result = parser.parse(\"Select: red\")\n    assert result[\"color\"] is Color.red\n\n.. _`Python 3.4 enum`: http://docs.python.org/3.4/library/enum.html#module-enum\n.. _enum34:   http://pypi.python.org/pypi/enum34\n.. _PEP-0435: http://www.python.org/dev/peps/pep-0435\n\n\nChoice (Name Enumeration)\n-------------------------------------------------------------------------------\n\nA Choice data type allows to select one of several strings.\n\nCreate an type converter for an \"Choice\" list, a list of unique names\n(as string).\n\n.. code-block:: python\n\n    from parse import Parser\n    from parse_type import TypeBuilder\n\n    parse_choice_yesno = TypeBuilder.make_choice([\"yes\", \"no\"])\n    schema = \"Answer: {answer:ChoiceYesNo}\"\n    parser = Parser(schema, dict(ChoiceYesNo=parse_choice_yesno))\n    result = parser.parse(\"Answer: yes\")\n    assert result[\"answer\"] == \"yes\"\n\n\nVariant (Type Alternatives)\n-------------------------------------------------------------------------------\n\nSometimes you need a type converter that can accept text for multiple\ntype converter alternatives. This is normally called a \"variant\" (or: union).\n\nCreate an type converter for an \"Variant\" type that accepts:\n\n* Numbers (positive numbers, as integer)\n* Color enum values (by name)\n\n.. code-block:: python\n\n    from parse import Parser, with_pattern\n    from parse_type import TypeBuilder\n    from enum import Enum\n\n    class Color(Enum):\n        red   = 1\n        green = 2\n        blue  = 3\n\n    @with_pattern(r\"\\d+\")\n    def parse_number(text):\n        return int(text)\n\n    # -- MAKE VARIANT: Alternatives of different type converters.\n    parse_color = TypeBuilder.make_enum(Color)\n    parse_variant = TypeBuilder.make_variant([parse_number, parse_color])\n    schema = \"Variant: {variant:Number_or_Color}\"\n    parser = Parser(schema, dict(Number_or_Color=parse_variant))\n\n    # -- TEST VARIANT: With number, color and mismatch.\n    result = parser.parse(\"Variant: 42\")\n    assert result[\"variant\"] == 42\n    result = parser.parse(\"Variant: blue\")\n    assert result[\"variant\"] is Color.blue\n    result = parser.parse(\"Variant: __MISMATCH__\")\n    assert not result\n\n\n\nExtended Parser with CardinalityField support\n-------------------------------------------------------------------------------\n\nThe parser extends the ``parse.Parser`` and adds the following functionality:\n\n* supports the CardinalityField naming scheme\n* automatically creates missing type variants for types with\n  a CardinalityField by using the primary type converter for cardinality=1\n* extends the provide type converter dictionary with new type variants.\n\nExample:\n\n.. code-block:: python\n\n    # -- USE CASE: Parser with CardinalityField support.\n    # NOTE: Automatically adds missing type variants with CardinalityField part.\n    # USE:  parse_number() type converter from above.\n    from parse_type.cfparse import Parser\n\n    # -- PREPARE: parser, adds missing type variant for cardinality 1..* (many)\n    type_dict = dict(Number=parse_number)\n    schema = \"List: {numbers:Number+}\"\n    parser = Parser(schema, type_dict)\n    assert \"Number+\" in type_dict, \"Created missing type variant based on: Number\"\n\n    # -- USE: parser.\n    result = parser.parse(\"List: 1, 2, 3\")\n    assert result[\"numbers\"] == [1, 2, 3]\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Simplifies to build parse types based on the parse module",
    "version": "0.6.2",
    "project_urls": {
        "Download": "http://pypi.python.org/pypi/parse_type",
        "Homepage": "https://github.com/jenisys/parse_type",
        "Issue Tracker": "https://github.com/jenisys/parse_type/issues/",
        "Source Code": "https://github.com/jenisys/parse_type"
    },
    "split_keywords": [
        "parse",
        "parsing"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ea578a965284ef665bdb538693203ecf064232b89ba43cdab226f9e3d714ed1a",
                "md5": "63444ddf2f5266b0c994a54cab863624",
                "sha256": "06d39a8b70fde873eb2a131141a0e79bb34a432941fb3d66fad247abafc9766c"
            },
            "downloads": -1,
            "filename": "parse_type-0.6.2-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "63444ddf2f5266b0c994a54cab863624",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": ">=2.7, !=3.0.*, !=3.1.*",
            "size": 26429,
            "upload_time": "2023-07-05T19:11:35",
            "upload_time_iso_8601": "2023-07-05T19:11:35.262648Z",
            "url": "https://files.pythonhosted.org/packages/ea/57/8a965284ef665bdb538693203ecf064232b89ba43cdab226f9e3d714ed1a/parse_type-0.6.2-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4732416e1d54fae3150def1b2db06dca12c5c5be3d1f1ab75c93d5cb21b2b23c",
                "md5": "f5c79d0643db5a3936ac22f92d7ee21d",
                "sha256": "79b1f2497060d0928bc46016793f1fca1057c4aacdf15ef876aa48d75a73a355"
            },
            "downloads": -1,
            "filename": "parse_type-0.6.2.tar.gz",
            "has_sig": false,
            "md5_digest": "f5c79d0643db5a3936ac22f92d7ee21d",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=2.7, !=3.0.*, !=3.1.*",
            "size": 69720,
            "upload_time": "2023-07-05T19:11:36",
            "upload_time_iso_8601": "2023-07-05T19:11:36.691527Z",
            "url": "https://files.pythonhosted.org/packages/47/32/416e1d54fae3150def1b2db06dca12c5c5be3d1f1ab75c93d5cb21b2b23c/parse_type-0.6.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-07-05 19:11:36",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "jenisys",
    "github_project": "parse_type",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "tox": true,
    "lcname": "parse-type"
}
        
Elapsed time: 0.10894s