Home

Awesome

<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:

<br/>

flow-image

<br/>

Requirements

Installation

Install using pip...

pip install related

First Example

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:

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.

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.

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.

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.

Exampledescription
Example 00First example above that shows how SetFields work.
Example 01Second example above that demonstrates YAML (de)serialization.
Example 02Compose v3 with long-form ports and singledispatch to_dict
Example 03A single class (Company) with a bunch of value fields.
Example 04A multi-class object model with Enum class value field.
Example 05Handling of renaming of attributes including Python keywords.
Example 06Basic JSON (de)serialization with TimeField, DateTimeField and DecimalField.
Example 07Function decorator that converts inputs to obj and outputs to dict
Example 08Handle self-referencing and out-of-order references using strings.

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:

The related project is an opinionated layer built on top of the attrs library that provides the following:

Class Decorators

decoratordescription
@mutableActivate a related class that instantiates changeable objects.
@immutableActivate 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 typedescription
BooleanFieldbool value field.
ChildFieldChild object of a specified type cls.
DateFielddate field formatted using formatter.
DateTimeFielddatetime field formatted using formatter.
TimeFieldtime field formatted using formatter.
FloatFieldfloat value field.
IntegerFieldint 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.
SetFieldSet of objects all of a specified type cls.
StringFieldstr value field.
URLFieldParseResult object.
UUIDFieldUUID 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

functiondescription
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.

License

The MIT License (MIT) Copyright (c) 2017 Ian Maurer, Genomoncology LLC