Documentation

To install, run:

pip install teleport

Teleport’s primary object is the serializer. A serializer corresponds to a certain data type and lets the user convert values of this type between their two representations: the JSON form and the native form. In order to do this, a serializer must provide a to_json() and a from_json() method.

The JSON form represents a valid JSON string, however, for convenience, our implementation uses an intermediate format, namely the format expected by json.dumps() from the Python standard library. It is limited to dictionaries, lists, numbers, booleans, strings and None.

Internally, your application will use the native form. It can be as rich as you want, however, for the most basic types, it happens to be the same as the JSON form. Even when there is nothing to convert, the serializer is useful as a way of validating input.

Here is a simple serializer:

>>> from teleport import *
>>> Integer.from_json(1)
1

Integer is a basic type, it is represented by the Integer class, which does not need to be instantiated because the type takes no parameters. Array is a parametrized type, it is represented by the Array class, which is instantiated with a parameter:

>>> Array(Integer).from_json([1, 2, 3, 4, 5])
[1, 2, 3, 4, 5]
>>> Array(Integer).from_json([1, 2, 3, 4, 5.1])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "teleport.py", line 349, in from_json
    ret.append(self.param.from_json(item))
  File "teleport.py", line 208, in from_json
    raise ValidationError("Invalid Integer", datum)
teleport.ValidationError: Item at [4] Invalid Integer: 5.1

Array(Integer) is an interesting object. It may be useful to send it over the wire, and with Teleport it is actually very easy to do. Teleport provides a special serializer Schema for serializing other serializers:

>>> Schema.to_json(Array(Integer))
{'type': 'Array', 'param': {'type': 'Integer'}}

The client that receives this JSON object can then deserialize it:

>>> Schema.from_json({'type': 'Array', 'param': {'type': 'Integer'}})
<teleport.Array object at 0xb7189d6c>

Pretty cool, huh? But actually, Schema isn’t all that special. You can use it just like any other serializer. For instance, here is an array of schemas:

>>> Array(Schema).to_json([Integer, Boolean, Float])
[{'type': 'Integer'}, {'type': 'Boolean'}, {'type': 'Float'}]

Another parametrized type is the Struct. It is used for dicts with non-arbitrary keys:

>>> from teleport import Struct, required, optional
>>> s = Struct([
...     required("name", String),
...     optional("scores", Array(Integer))
... ])
>>> s.from_json({"name": "Bob"})
{"name": u"Bob"}
>>> s.from_json({"name": "Bob", "scores": [1, 2, 3.1]})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "teleport.py", line 406, in from_json
    ret[field] = schema.from_json(datum[field])
  File "teleport.py", line 349, in from_json
    ret.append(self.param.from_json(item))
  File "teleport.py", line 208, in from_json
    raise ValidationError("Invalid Integer", datum)
teleport.ValidationError: Item at ['scores'][2] Invalid Integer: 3.1

Creating Custom Types

To create a custom type, define a serializer class:

class YesNoMaybe(object):

    @staticmethod
    def from_json(datum):
        if datum not in [True, False, None]:
            raise ValidationError("Invalid YesNoMaybe", datum)
        return datum

    @staticmethod
    def to_json(datum):
        return datum

YesNoMaybe is a primitive serializer as it defines functions to convert data directly to and from JSON. Another option is a wrapper serializer, which relies on an internal serializer for dealing with JSON and builds on top of it by defining assemble() and disassemble() methods.

The assemble() method may be used to perform additional validation that the internal serializer doesn’t take care of:

class Suit(BasicWrapper):
    schema = String

    @staticmethod
    def assemble(datum):
        if datum not in ["hearts", "spades", "clubs", "diamonds"]:
            raise ValidationError("Invalid Suit", datum)
        return datum

Note that the BasicWrapper mixin defines the to_json() and from_json() functions for you:

>>> Suit.from_json("hearts")
"hearts"
>>> Suit.from_json("heart")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "teleport.py", line 406, in from_json
    ret[field] = schema.from_json(datum[field])
  File "teleport.py", line 349, in from_json
    ret.append(self.param.from_json(item))
teleport.ValidationError: Invalid Suit: "heart"

When the native form of the data type is a class instance, BasicWrapper can be used to teach the class to serialize itself:

class Player(BasicWrapper):
    schema = Struct([
        required("name", String),
        # Note how struct fields can accept an optional doc parameter
        required("level", Integer, "0-100")
    ])

    @staticmethod
    def assemble(datum):
        return Player(**datum)

    @staticmethod
    def disassemble(player):
        return {
            "name": player.name,
            "level": player.level
        }

Custom Types With Parameters

Both primitive and wrapper types can also be parametrized, which means that their serializers will have to be instantiated with parameters and that their JSON form will have an additional attribute param.

Take a look at the source code of Array for an example of a primitive parametrized type, and OrderedMap for an example of a wrapper parametrized type.

Extending Teleport

The types we created above will mostly work, however, if you expect to deserialize their schema from JSON, you will be faced with an error:

>>> Schema.from_json({"type": "Player"})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "teleport.py", line 178, in from_json
    raise UnknownTypeValidationError("Unknown type", t)
teleport.UnknownTypeValidationError: Unknown type: 'Player'

The Schema class that you imported from the teleport module will never be aware of the custom types you’ve created. In order to extend Teleport, you need to recreate all the built-in types (including Schema) in a separate module. This sounds like a complicated task, but standard_types() makes it very easy.

teleport.standard_types(type_getter=None, include=None)
Parameters:
  • type_getter – A function that, given a custom type name, returns the corresponding serializer.
  • include – A list of names of the built-in serializers that you want to be included in your custom Teleport module.
Returns:

A dict, mapping class names to classes.

Create an empty module in your Python application, say myapp.types.

from teleport import standard_types

def getter(name):
    if name == "Suit":
        return Suit
    raise KeyError()

class Suit(BasicWrapper):
    schema = String

    @staticmethod
    def assemble(datum):
        if datum not in ["hearts", "spades", "clubs", "diamonds"]:
            raise ValidationError("Invalid Suit", datum)
        return datum

globals().update(standard_types(getter))

standard_types() will inject your custom models into Teleport via the getter parameter. The return value is a dict of freshly recreated Teleport serializers. Now, instead of doing:

from teleport import *

you do:

from myapp.types import *

Note that by default, Teleport will use the class name as the name of the serializer. If your new type’s class name is different from its actual type name, let Teleport know by setting the type_name property on the class.

Built-In Serializers

class teleport.Integer
static from_json(datum)

If datum is an integer, return it; if it is a float with a 0 for its fractional part, return the integer part as an int. Otherwise, raise a ValidationError.

class teleport.Float
static from_json(datum)

If datum is a float, return it; if it is an integer, cast it to a float and return it. Otherwise, raise a ValidationError.

class teleport.Boolean
static from_json(datum)

If datum is a boolean, return it. Otherwise, raise a ValidationError.

class teleport.String
static from_json(datum)

If datum is of unicode type, return it. If it is a string, decode it as UTF-8 and return the result. Otherwise, raise a ValidationError. Unicode errors are dealt with strictly by raising UnicodeDecodeValidationError, a subclass of the above.

class teleport.Binary

This type may be useful when you wish to send a binary file. The byte string will be base64-encoded for safety.

>>> b = open('pixel.png', 'rb').read()
>>> Binary.to_json(b)
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAACXBIWXMAAAsTAAALE
wEAmpwYAAAAB3RJTUUH3QoSBggTmj6VgAAAABl0RVh0Q29tbWVudABDcmVhdGVkIHd
pdGggR0lNUFeBDhcAAAAMSURBVAjXY/j//z8ABf4C/tzMWecAAAAASUVORK5CYII='
static from_json(datum)

If datum is a base64-encoded string, decode and return it. If not a string, or encoding is wrong, raise ValidationError.

static to_json(datum)

Encode datum using base64.

class teleport.DateTime

Bases: teleport.types.BasicWrapper

Wraps the String type.

>>> DateTime.to_json(datetime.now())
u'2013-10-18T01:58:24.904349'
classmethod assemble(datum)

Parse datum as an ISO 8601-encoded time and return a datetime object. If the string is invalid, raise a ValidationError.

classmethod disassemble(datum)

Given a datetime object, return an ISO 8601-encoded string.

class teleport.JSON

This type may be used as a kind of wildcard that will accept any JSON value and return it untouched. Presumably you still want to interpret the meaning of this arbitrary JSON data, you just don’t want to do it through Teleport.

static from_json(datum)

Return the JSON value wrapped in a Box.

class teleport.Box(datum)

Used as a wrapper around JSON data to disambiguate None as a JSON value (null) from None as an absence of value. Its datum attribute will hold the actual JSON value.

For example, an HTTP request body may be empty in which case your function may return None or it may be “null”, in which case the function can return a Box instance with None inside.

class teleport.Array(param)

The argument param is a serializer that defines the type of each item in the array.

from_json(datum)

If datum is a list, construct a new list by putting each element of datum through a serializer provided as param. This serializer may raise a ValidationError. If datum is not a list, ValidationError will also be raised.

to_json(datum)

Serialize each item in the datum iterable using param. Return the resulting values in a list.

class teleport.Map(param)

The argument param is a serializer that defines the type of each item in the map.

from_json(datum)

If datum is a dict, deserialize it, otherwise raise a ValidationError. The keys of the dict must be unicode, and the values will be deserialized using param.

class teleport.OrderedMap(param)

The argument param is a serializer that defines the type of each item in the map.

Internal schema:

Struct([
    required(u"map", Map(param)),
    required(u"order", Array(String))
])

The order of the items in map is not preserved by JSON, hence the existence of order, an array of keys in map.

assemble(datum)

ValidationError is raised if order does not correspond to the keys in map. The native form is Python’s OrderedDict.

class teleport.Struct(param)

param must be an OrderedDict, where the keys are field names, and values are dicts with two items: schema (serializer) and required (Boolean). For each pair, schema is used to serialize and deserialize a dict value matched by the corresponding key.

For convenience, Struct can be instantiated with a list of tuples like the constructor of OrderedDict.

from_json(datum)

If datum is a dict, deserialize it against param and return the resulting dict. Otherwise raise a ValidationError.

A ValidationError will be raised if:

  1. datum is missing a required field
  2. datum has a field not declared in param.
  3. One of the values of datum does not pass validation as defined by the schema of the corresponding field.
class teleport.Schema
static from_json(datum)

Expects a JSON object with a type attribute and an optional param attribute. Uses type to find the serializer. If the type is simple, returns the serializer, if parametrized, deserializes param and uses it to instatiate the serializer class before returning it.

After looking in the built-in types, this method will attempt to find the serializer via type_getter, an argument of standard_types(). See Extending Teleport. If no serializer is found, UnknownTypeValidationError will be raised.

static to_json(datum)

If given a serializer representing a simple type, return a JSON object with a single attribute type, if a parametrized one, also include an attribute param.

By default type is the class name of the serializer, but it can be overridden by the serializer’s type_name property.

Exceptions

class teleport.ValidationError(message, *args)

Raised during deserialization. Stores the location of the error in the JSON document relative to its root.

First argument is the error message, second optional argument is the object that failed validation.

class teleport.UnicodeDecodeValidationError(message, *args)

Bases: teleport.types.ValidationError

class teleport.UnknownTypeValidationError(message, *args)

Bases: teleport.types.ValidationError