{
"info": {
"author": "",
"author_email": "",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7"
],
"description": "
\n
\n
\n
\n\n
\n\n`Related` is a Python library for creating nested object models\nthat can be serialized to and de-serialized from\nnested python dictionaries.\nWhen paired with other libraries (e.g. [PyYAML]),\n`Related` object models can be used to convert to and from\nnested data formats (e.g. JSON, YAML).\n\nExample use cases for `related` object models include:\n\n* Configuration file reading and writing\n* REST API message response generation and request processing\n* Object-Document Mapping for a document store (e.g. MongoDB, elasticsearch)\n* Data import parsing or export generation\n\n
\n\n![flow-image]\n\n
\n\n# Requirements\n\n* Python (2.7, 3.5, 3.6)\n\n\n# Installation\n\nInstall using `pip`...\n\n pip install related\n\n\n# First Example\n\n```python\nimport related\n\n@related.immutable\nclass Person(object):\n first_name = related.StringField()\n last_name = related.StringField()\n\n@related.immutable\nclass RoleModels(object):\n scientists = related.SetField(Person)\n\npeople = [Person(first_name=\"Grace\", last_name=\"Hopper\"),\n Person(first_name=\"Katherine\", last_name=\"Johnson\"),\n Person(first_name=\"Katherine\", last_name=\"Johnson\")]\n\nprint(related.to_yaml(RoleModels(scientists=people)))\n```\n\nYields:\n\n```yaml\nscientists:\n- first_name: Grace\n last_name: Hopper\n- first_name: Katherine\n last_name: Johnson\n```\n\n\n# Second Example\n\nThe below example is based off of this [Docker Compose example].\nIt shows how a YAML file can be loaded into an object model, tested, and\nthen generated back into a string that matches the original YAML.\n\n```yaml\nversion: '2'\nservices:\n web:\n build: .\n ports:\n - 5000:5000\n volumes:\n - .:/code\n redis:\n image: redis\n```\n\nBelow is the `related` object model that represents the above configuration.\nNotice how the name-based mapping of services (i.e. web, redis) are\nrepresented by the model.\n\n\n```python\nimport related\n\n\n@related.immutable\nclass Service(object):\n name = related.StringField()\n image = related.StringField(required=False)\n build = related.StringField(required=False)\n ports = related.SequenceField(str, required=False)\n volumes = related.SequenceField(str, required=False)\n command = related.StringField(required=False)\n\n\n@related.immutable\nclass Compose(object):\n version = related.StringField(required=False, default=None)\n services = related.MappingField(Service, \"name\", required=False)\n```\n\nThe above yaml can then be loaded by using one of the convenience\nmethod and then round-tripped back to yaml to check that the format\nhas been maintained. The `related` module uses `OrderedDict` objects\nin order to maintain sort order by default.\n\n```python\nfrom os.path import join, dirname\n\nfrom model import Compose\nfrom related import to_yaml, from_yaml, to_model\n\nYML_FILE = join(dirname(__file__), \"docker-compose.yml\")\n\n\ndef test_compose_from_yml():\n original_yaml = open(YML_FILE).read().strip()\n yml_dict = from_yaml(original_yaml)\n compose = to_model(Compose, yml_dict)\n\n assert compose.version == '2'\n assert compose.services['web'].ports == [\"5000:5000\"]\n assert compose.services['redis'].image == \"redis\"\n\n generated_yaml = to_yaml(compose,\n suppress_empty_values=True,\n suppress_map_key_values=True).strip()\n\n assert original_yaml == generated_yaml\n```\n\n\n# More Examples\n\nMore examples can be found by reviewing the [tests/] folder of this project.\nBelow are links and descriptions of the tests provided so far.\n\n| Example | description |\n| -------------- | ------------------------------------------------------------------ |\n| [Example 00] | First example above that shows how SetFields work. |\n| [Example 01] | Second example above that demonstrates YAML (de)serialization. |\n| [Example 02] | Compose v3 with long-form ports and singledispatch to_dict |\n| [Example 03] | A single class (Company) with a bunch of value fields. |\n| [Example 04] | A multi-class object model with Enum class value field. |\n| [Example 05] | Handling of renaming of attributes including Python keywords. |\n| [Example 06] | Basic JSON (de)serialization with TimeField, DateTimeField and DecimalField. |\n| [Example 07] | Function decorator that converts inputs to obj and outputs to dict |\n| [Example 08] | Handle self-referencing and out-of-order references using strings. |\n\n\n# Documentation\n\nBelow is a quick version of documentation until more time can be dedicated.\n\n\n## Overview\n\nThe [attrs] library is the underlying engine for `related`.\nAs explained in [this article by Glyph],\n`attrs` cleanly and cleverly\neliminates a lot of the boilerplate\nrequired when creating Python classes\nwithout using inheritance.\nSome core functionality provided by attrs:\n\n* Generated initializer method\n (``__init__``)\n* Generated comparison methods\n (``__eq__``, ``__ne__``, ``__lt__``, ``__le__``, ``__gt__``, ``__ge__`` )\n* Human-readable representation method\n (``__repr__``)\n* Attribute converter and validator framework\n\n\nThe `related` project is an opinionated layer\nbuilt on top of the `attrs` library\nthat provides the following:\n\n* Value fields that handle both validation and conversion\n to and from basic data types like\n ``str``, ``float``, and ``bool``.\n* Nested fields that support relationships such as\n Child, Sequences, Mappings, and Sets of objects.\n* ``to_dict`` function that converts nested object graphs\n to python dictionaries.\n Made customizable (without resorting to [monkey-patching])\n by the [singledispatch library].\n* ``to_model`` function that instantiated classes\n used by the de-serialization process going from\n python dictionaries to the related model.\n* Conversion helper functions\n (``to_yaml``, ``from_yaml``, ``to_json``, ``from_json``)\n for easily going between\n related models and data formats.\n* ``@mutable`` and ``@immutable`` for decorating classes\n as related models without the need for inheritance increasing\n maintainability and flexibility.\n\n\n## Class Decorators\n\n| decorator | description |\n| -------------- | ---------------------------------------------------------------- |\n| @mutable | Activate a related class that instantiates changeable objects. |\n| @immutable | Activate a related class that instantiates unchangeable objects. |\n\nSee the [decorators.py] file to view the source code until proper\ndocumentation is generated.\n\n\n## Field Types\n\n| field type | description |\n| -------------- | ---------------------------------------------------------------- |\n| BooleanField | `bool` value field. |\n| ChildField | Child object of a specified type `cls`. |\n| DateField | `date` field formatted using `formatter`. |\n| DateTimeField | `datetime` field formatted using `formatter`. |\n| TimeField | `time` field formatted using `formatter`. |\n| FloatField | `float` value field. |\n| IntegerField | `int` value field. |\n| MappingField(cls,key) | Dictionary of objects of type `cls` index by `key` field values. |\n| RegexField(regex) | `str` value field that is validated by re.match(`regex`). |\n| SequenceField(cls) | List of objects all of specified type `cls`. |\n| SetField | Set of objects all of a specified type `cls`. |\n| StringField | `str` value field. |\n| URLField | [ParseResult] object. |\n| UUIDField | [UUID] object, will create [uuid4] by default if not specified. |\n\n\nAdding your own field types is fairly straightforward\ndue to the power of the underlying `attrs` project.\nSee the [fields.py] file to see how the above are constructed.\n\n\n## Functions\n\n| function | description |\n| ------------------- | ----------------------------------------------------- |\n| from_json(s,cls) | Convert a JSON string or stream into specified class. |\n| from_yaml(s,cls) | Convert a YAML string or stream into specified class. |\n| is_related(obj) | Returns True if object is @mutable or @immutable. |\n| to_dict(obj) | Singledispatch function for converting to a dict. |\n| to_json(obj) | Convert object to a (pretty) JSON string via to_dict. |\n| to_model(cls,value) | Convert a value to a `cls` instance. |\n| to_yaml(obj) | Convert object to a YAML string via to_dict. |\n\n\nSee the [functions.py] file to view the source code until proper\ndocumentation is generated.\n\n\n# Credits/Prior Art\n\nThe `related` project has been heavily influenced by the following\nprojects that might be worth looking at if `related` doesn't meet your needs.\n\n* [attrs] - The engine that powers `related` functionality.\n* [Django ORM] - Object-relational mapping for Django that inspired `related's` design.\n* [cattrs] - Alternative take for handling nested-objects using `attrs`.\n* [addict] and [box] - Python dictionary wrappers that do not require a model.\n* [Jackson] - Java-based technology for serializing and de-serializing objects.\n\n\n# License\n\nThe MIT License (MIT)\nCopyright (c) 2017 [Ian Maurer], [Genomoncology LLC]\n\n\n\n\n[flow-image]: ./.images/flow.png\n[decorators.py]: ./src/related/decorators.py\n[fields.py]: ./src/related/fields.py\n[functions.py]: ./src/related/functions.py\n[attrs]: http://attrs.readthedocs.io/en/stable/\n[this article by Glyph]: https://glyph.twistedmatrix.com/2016/08/attrs.html\n[Genomoncology LLC]: http://genomoncology.com\n[Ian Maurer]: https://github.com/imaurer\n[singledispatch library]: https://pypi.python.org/pypi/singledispatch\n[monkey-patching]: http://stackoverflow.com/questions/5626193/what-is-a-monkey-patch\n[Django ORM]: https://docs.djangoproject.com/en/1.11/topics/db/models/\n[UUID]: https://docs.python.org/3/library/uuid.html#uuid.UUID\n[uuid4]: https://docs.python.org/3/library/uuid.html#uuid.uuid4\n[ParseResult]: https://docs.python.org/2/library/urlparse.html#urlparse.ParseResult\n[cattrs]: http://cattrs.readthedocs.io/en/latest/readme.html\n[addict]: https://github.com/mewwts/addict\n[box]: https://pypi.python.org/pypi/python-box\n[Jackson]: https://github.com/FasterXML/jackson\n[Docker Compose example]: https://docs.docker.com/compose/gettingstarted/#step-3-define-services-in-a-compose-file\n[PyYAML]: https://pypi.python.org/pypi/PyYAML\n\n[tests/]: ./tests/\n[Example 00]: ./tests/ex00_sets_hashes\n[Example 01]: ./tests/ex01_compose_v2\n[Example 02]: ./tests/ex02_compose_v3.2\n[Example 03]: ./tests/ex03_company\n[Example 04]: ./tests/ex04_contact\n[Example 05]: ./tests/ex05_field_names\n[Example 06]: ./tests/ex06_json\n[Example 07]: ./tests/ex07_serializer\n[Example 08]: ./tests/ex08_self_reference\n\n\n0.6.2 (2018-02-12)\n----------------\n- Contribution [GabrielDav]: TimeField and DateTimeField fields.\n\n\n0.6.1 (2018-01-31)\n----------------\n- Strict Mode [Issue #8] throws an exception when receiving an undefined key.\n\n\n0.3 (2017-06-23)\n----------------\n- New type: ImmutableDict\n- Add function on TypedMapping\n- Bug fixes in from_yaml and from_json functions.\n\n\n0.2 (2017-06-05)\n----------------\n- Allow None by default in Typed Collections.\n\n\n0.1 (2017-05-24)\n----------------\n- Initial release.",
"description_content_type": "",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "",
"keywords": "related object models yaml json dict nested",
"license": "MIT license",
"maintainer": "",
"maintainer_email": "",
"name": "related-li",
"package_url": "https://pypi.org/project/related-li/",
"platform": "",
"project_url": "https://pypi.org/project/related-li/",
"project_urls": null,
"release_url": "https://pypi.org/project/related-li/0.7.0/",
"requires_dist": null,
"requires_python": "",
"summary": "Related: Straightforward nested object models in Python",
"version": "0.7.0"
},
"last_serial": 4313528,
"releases": {
"0.7.0": [
{
"comment_text": "",
"digests": {
"md5": "616d826014ced8c5e354d09b46eca88f",
"sha256": "602432af2f9a149140745b4640626445d14769aa02a7b108f56010ce394ab60b"
},
"downloads": -1,
"filename": "related-li-0.7.0.tar.gz",
"has_sig": false,
"md5_digest": "616d826014ced8c5e354d09b46eca88f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 26429,
"upload_time": "2018-09-26T18:10:52",
"url": "https://files.pythonhosted.org/packages/5b/5c/2a12057f26c659b941c07ffc19ce2fcb863be12cfc7e3f2fc8e762709f6f/related-li-0.7.0.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "616d826014ced8c5e354d09b46eca88f",
"sha256": "602432af2f9a149140745b4640626445d14769aa02a7b108f56010ce394ab60b"
},
"downloads": -1,
"filename": "related-li-0.7.0.tar.gz",
"has_sig": false,
"md5_digest": "616d826014ced8c5e354d09b46eca88f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 26429,
"upload_time": "2018-09-26T18:10:52",
"url": "https://files.pythonhosted.org/packages/5b/5c/2a12057f26c659b941c07ffc19ce2fcb863be12cfc7e3f2fc8e762709f6f/related-li-0.7.0.tar.gz"
}
]
}