Metadata-Version: 1.1
Name: related
Version: 0.5.3
Summary: Related: Straightforward nested object models in Python
Home-page: UNKNOWN
Author: UNKNOWN
Author-email: UNKNOWN
License: MIT license
Description-Content-Type: UNKNOWN
Description: <img src='./.images/logo.png' width='140' align="left" />
        <a href='https://codecov.io/github/genomoncology/related/'><img src='https://codecov.io/github/genomoncology/related/branch/master/graph/badge.svg' align="right" /></a>
        <a href='https://travis-ci.org/genomoncology/related'><img src='https://img.shields.io/travis/genomoncology/related.svg' align="right" /></a>
        <a href='https://pypi.python.org/pypi/related'><img src='https://img.shields.io/pypi/v/related.svg' align="right" /></a>
        
        <br/><br/>
        
        `Related` is a Python library for creating nested object models
        that can be serialized to and de-serialized from
        nested python dictionaries. 
        When paired with other libraries (e.g. [PyYAML]),
        `Related` object models can be used to convert to and from
        nested data formats (e.g. JSON, YAML).
        
        Example use cases for `related` object models include:
        
        * Configuration file reading and writing
        * REST API message response generation and request processing
        * Object-Document Mapping for a document store (e.g. MongoDB, elasticsearch)
        * Data import parsing or export generation
        
        <br/>
        
        ![flow-image]
        
        <br/>
        
        # Requirements
        
        * Python (2.7, 3.5, 3.6)
        * attrs (17.1.0)
        
        
        # Installation
        
        Install using `pip`...
        
            pip install related
        
        
        # First Example
        
        ```python
        import related
        
        @related.immutable
        class Person(object):
            first_name = related.StringField()
            last_name = related.StringField()
            
        @related.immutable
        class RoleModels(object):
            scientists = related.SetField(Person)
        
        people = [Person(first_name="Grace", last_name="Hopper"),
                  Person(first_name="Katherine", last_name="Johnson"),
                  Person(first_name="Katherine", last_name="Johnson")]
                  
        print(related.to_yaml(RoleModels(scientists=people)))
        ```
        
        Yields:
        
        ```yaml
        scientists:
        - first_name: Grace
          last_name: Hopper
        - first_name: Katherine
          last_name: Johnson
        ```
        
        
        # Second Example
        
        The below example is based off of this [Docker Compose example].
        It shows how a YAML file can be loaded into an object model, tested, and
        then generated back into a string that matches the original YAML.
        
        ```yaml
        version: '2'
        services:
          web:
            build: .
            ports:
            - 5000:5000
            volumes:
            - .:/code
          redis:
            image: redis
        ```
        
        Below is the `related` object model that represents the above configuration.
        Notice how the name-based mapping of services (i.e. web, redis) are
        represented by the model.
        
        
        ```python
        import related
        
        
        @related.immutable
        class Service(object):
            name = related.StringField()
            image = related.StringField(required=False)
            build = related.StringField(required=False)
            ports = related.SequenceField(str, required=False)
            volumes = related.SequenceField(str, required=False)
            command = related.StringField(required=False)
        
        
        @related.immutable
        class Compose(object):
            version = related.StringField(required=False, default=None)
            services = related.MappingField(Service, "name", required=False)
        ```
        
        The above yaml can then be loaded by using one of the convenience
        method and then round-tripped back to yaml to check that the format
        has been maintained. The `related` module uses `OrderedDict` objects
        in order to maintain sort order by default.
        
        ```python
        from os.path import join, dirname
        
        from model import Compose
        from related import to_yaml, from_yaml, to_model
        
        YML_FILE = join(dirname(__file__), "docker-compose.yml")
        
        
        def test_compose_from_yml():
            original_yaml = open(YML_FILE).read().strip()
            yml_dict = from_yaml(original_yaml)
            compose = to_model(Compose, yml_dict)
        
            assert compose.version == '2'
            assert compose.services['web'].ports == ["5000:5000"]
            assert compose.services['redis'].image == "redis"
        
            generated_yaml = to_yaml(compose,
                                     suppress_empty_values=True,
                                     suppress_map_key_values=True).strip()
        
            assert original_yaml == generated_yaml
        ```
        
        
        # More Examples
        
        More examples can be found by reviewing the [tests/] folder of this project.
        Below are links and descriptions of the tests provided so far.
        
        | Example        | description                                                    | 
        | -------------- | -------------------------------------------------------------- |
        | [Example 00]   | First example above that shows how SetFields work.             |
        | [Example 01]   | Second example above that demonstrates YAML (de)serialization. |
        | [Example 02]   | Compose v3 with long-form ports and singledispatch to_dict     |
        | [Example 03]   | A single class (Company) with a bunch of value fields.         | 
        | [Example 04]   | A multi-class object model with Enum class value field.        |
        
        
        # Documentation
        
        Below is a quick version of documentation until more time can be dedicated.
        
        
        ## Overview
        
        The [attrs] library is the underlying engine for `related`.
        As explained in [this article by Glyph],
        `attrs` cleanly and cleverly
        eliminates a lot of the boilerplate
        required when creating Python classes
        without using inheritance.
        Some core functionality provided by attrs:
        
        * Generated initializer method
            (``__init__``)
        * Generated comparison methods
            (``__eq__``, ``__ne__``, ``__lt__``, ``__le__``, ``__gt__``, ``__ge__`` )
        * Human-readable representation method
            (``__repr__``)
        * Attribute converter and validator framework
        
        
        The `related` project is an opinionated layer
        built on top of the `attrs` library
        that provides the following:
        
        * Value fields that handle both validation and conversion
          to and from basic data types like
          ``str``, ``float``, and ``bool``.
        * Nested fields that support relationships such as
          Child, Sequences, Mappings, and Sets of objects.
        * ``to_dict`` function that converts nested object graphs
          to python dictionaries.
          Made customizable (without resorting to [monkey-patching])
          by the [singledispatch library].
        * ``to_model`` function that instantiated classes
          used by the de-serialization process going from
          python dictionaries to the related model.
        * Conversion helper functions 
          (``to_yaml``, ``from_yaml``, ``to_json``, ``from_json``)
          for easily going between
          related models and data formats.
        * ``@mutable`` and ``@immutable`` for decorating classes
          as related models without the need for inheritance increasing
          maintainability and flexibility.
        
        
        ## Class Decorators
        
        | decorator             | description                                                      | 
        | --------------        | ---------------------------------------------------------------- |
        | @mutable              | Activate a related class that instantiates changeable objects.   |
        | @immutable            | Activate a related class that instantiates unchangeable objects. |
        
        See the [decorators.py] file to view the source code until proper
        documentation is generated.
        
        
        ## Field Types
        
        | field type            | description                                                      | 
        | --------------        | ---------------------------------------------------------------- |
        | BooleanField          | `bool` value field.                                              | 
        | ChildField            | Child object of a specified type `cls`.                          |
        | DateField             | `date` field formatted using `formatter`.                        |
        | FloatField            | `float` value field.                                             |
        | IntegerField          | `int` value field.                                               | 
        | MappingField(cls,key) | Dictionary of objects of type `cls` index by `key` field values. |
        | RegexField(regex)     | `str` value field that is validated by re.match(`regex`).        | 
        | SequenceField(cls)    | List of objects all of specified type `cls`.                     |
        | SetField              | Set of objects all of a specified type `cls`.                    |
        | StringField           | `str` value field.                                               | 
        | URLField              | [ParseResult] object.                                            |
        | UUIDField             | [UUID] object, will create [uuid4] by default if not specified.  |
        
        
        Adding your own field types is fairly straightforward
        due to the power of the underlying `attrs` project.
        See the [fields.py] file to see how the above are constructed.
        
        
        ## Functions
        
        | function            | description                                           | 
        | ------------------- | ----------------------------------------------------- |
        | from_json(s,cls)    | Convert a JSON string or stream into specified class. | 
        | from_yaml(s,cls)    | Convert a YAML string or stream into specified class. | 
        | is_related(obj)     | Returns True if object is @mutable or @immutable.     |
        | to_dict(obj)        | Singledispatch function for converting to a dict.     | 
        | to_json(obj)        | Convert object to a (pretty) JSON string via to_dict. | 
        | to_model(cls,value) | Convert a value to a `cls` instance.                  |
        | to_yaml(obj)        | Convert object to a YAML string via to_dict.          | 
         
        
        See the [functions.py] file to view the source code until proper
        documentation is generated.
        
        
        # Credits/Prior Art
        
        The `related` project has been heavily influenced by the following
        projects that might be worth looking at if `related` doesn't meet your needs.
        
        * [attrs] - The engine that powers `related` functionality.
        * [Django ORM] - Object-relational mapping for Django that inspired `related's` design.
        * [cattrs] - Alternative take for handling nested-objects using `attrs`.
        * [addict] and [box] - Python dictionary wrappers that do not require a model.
        * [Jackson] - Java-based technology for serializing and de-serializing objects.
        
        
        # License
        
        The MIT License (MIT)
        Copyright (c) 2017 [Ian Maurer], [Genomoncology LLC]
        
        
        
        
        [flow-image]: ./.images/flow.png
        [decorators.py]: ./src/related/decorators.py
        [fields.py]: ./src/related/fields.py
        [functions.py]: ./src/related/functions.py
        [attrs]: http://attrs.readthedocs.io/en/stable/
        [this article by Glyph]: https://glyph.twistedmatrix.com/2016/08/attrs.html
        [Genomoncology LLC]: http://genomoncology.com 
        [Ian Maurer]: https://github.com/imaurer
        [singledispatch library]: https://pypi.python.org/pypi/singledispatch
        [monkey-patching]: http://stackoverflow.com/questions/5626193/what-is-a-monkey-patch
        [Django ORM]: https://docs.djangoproject.com/en/1.11/topics/db/models/
        [UUID]: https://docs.python.org/3/library/uuid.html#uuid.UUID
        [uuid4]: https://docs.python.org/3/library/uuid.html#uuid.uuid4
        [ParseResult]: https://docs.python.org/2/library/urlparse.html#urlparse.ParseResult
        [cattrs]: http://cattrs.readthedocs.io/en/latest/readme.html
        [addict]: https://github.com/mewwts/addict
        [box]: https://pypi.python.org/pypi/python-box
        [Jackson]: https://github.com/FasterXML/jackson
        [Docker Compose example]: https://docs.docker.com/compose/gettingstarted/#step-3-define-services-in-a-compose-file
        [PyYAML]: https://pypi.python.org/pypi/PyYAML
        
        [tests/]: ./tests/
        [Example 00]: ./tests/ex00_sets_hashes
        [Example 01]: ./tests/ex01_compose_v2
        [Example 02]: ./tests/ex02_compose_v3.2
        [Example 03]: ./tests/ex03_company
        [Example 04]: ./tests/ex04_contact
        
        
        0.3 (2017-06-23)
        ----------------
        - New type: ImmutableDict
        - Add function on TypedMapping
        - Bug fixes in from_yaml and from_json functions.
        
        
        0.2 (2017-06-05)
        ----------------
        - Allow None by default in Typed Collections.
        
        
        0.1 (2017-05-24)
        ----------------
        - Initial release.
        
Keywords: related object models yaml json dict nested
Platform: UNKNOWN
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
