Skip to main content

Library to define data contracts for JSON and build RESTful services with Webapp2 or Flask

Project description

pytracts

Downloads

A library for defining data contracts in native Python code, based on the Google ProtoRPC library

Define JSON Contracts with Python Objects

from pytracts import messages, to_json, to_dict

class TeamMessage(messages.Message):
    name = messages.StringField()
    colors = messages.StringField(repeated=True)
    mascot = messages.StringField()

gophers = TeamMessage(name='Minnesota', colors=['maroon', 'gold'], mascot='Goldy Gopher')

# Export data to python dictionary
print to_dict.encode_message(gophers)
#=> {'colors': ['maroon', 'gold'], 'name': 'Minnesota', 'mascot': 'Goldy Gopher'}

# Export data to json string
print to_json.encode_message(gophers)
#=> {"colors": ["maroon", "gold"], "name": "Minnesota", "mascot": "Goldy Gopher"}

# Load data from dict
badgers = to_dict.decode_message(TeamMessage, {
    "name": "Wisconsin", 
    "mascot": "Bucky Badger", 
    "colors": ["cardinal", "white"]})
print badgers.name
#=> Wisconsin

# Load data from JSON
badgers = to_json.decode_message(TeamMessage, '{
    "name": "Wisconsin", 
    "mascot": "Bucky Badger", 
    "colors": ["cardinal", "white"]}')
print badgers.mascot
#=> Bucky Badger

Support for nested messages

from pytracts import messages

class AddressMessage(messages.MessageField)
    street = messages.StringField()
    city = messages.StringField()
    state = messages.StringField()
    zip = messages.IntegerField()

    
class PersonMessage(messages.Message):
    home_address = messages.MessageField(AddressMessage)
    work_address = messages.MessageField(AddressMessage)

leslie = PersonMessage(
    home_address=AddressMessage(
        street='123 Sesame St', 
        city='Pawnee', state='IN', zip=22113),
    work_address=AddressMessage(
        street='987 Brookstone Ln', 
        city='Pawnee', state='IN', zip=22113)
)

Support for Arbitrary Data Types and Unstructured JSON

Arbitrary types:

from pytracts import messages, to_json

class BoxMessage(messages.Message):
    height = messages.UntypedField()
    width = messages.UntypedField()

b = BoxMessage(height=123, width="65%")

print to_json.encode_message(b)
#=> {"width": "65%", "height": 123}

Unstructured dictionaries:

from pytracts import messages, to_json

class UserMessage(messages.Message):
    name = messages.StringField()
    email = messages.StringField()
    metadata = messages.DictField()

bob = UserMessage(name='Bob', email='bob@example.com', metadata={'height': 72, 'weight': 180})

print to_json.encode_message(bob)
#=> {"metadata": {"weight": 180, "height": 72}, "email": "bob@example.com", "name": "Bob"}

Annotate Flask Handlers for JSON serialization

from flask import Flask, url_for
import werkzeug

from pytracts import messages, flask as pt

class TeamMessage(messages.Message):
    id = messages.StringField()
    name = messages.StringField()
    colors = messages.StringField(repeated=True)
    mascot = messages.StringField()


class TeamsResponseMessage(messages.Message):
    page = messages.IntegerField()
    teams = messages.MessageField(TeamMessage, repeated=True)


gophers = TeamMessage(id='gophers', name='Minnesota', colors=['maroon', 'gold'], mascot='Goldy Gopher')
badgers = TeamMessage(id='badgers', name='Wisconsin', colors=['cardinal', 'gold'], mascot='Bucky Badger')
teams = dict([(t.id, t) for t in [gophers, badgers]])

# Annotate endpoints to automatically serialize to JSON
@pt.endpoint('/v1/teams')
def get_teams():

    response = TeamsResponseMessage()
    response.page = 1
    response.teams = list(teams.values())

    return response

# Use Webapp2 exceptions for other status codes
@pt.endpoint('/v1/teams/<team_id>')
def get_team(team_id):
    if team_id in teams:
        return teams[team_id]
    else:
        raise werkzeug.exceptions.NotFound(f'Team {team_id} not found')

# Take a message from the JSON body of the request
@pt.endpoint('/v1/teams', methods=['POST'], body={'team_details': TeamMessage})
def create_team(team_details):
    # Create the team based on details
    if team_details.id in teams:
        raise werkzeug.exceptions.Forbidden(f'Team {team_details.id} already exists')

    teams[team_details.id] = team_details
    # Return 201 status with a location header
    return 201, {'Location': url_for('get_team', team_id=team_details.id)}

app = Flask(__name__)
pt.register_endpoints(app)

See full sample app for more details.

PATCH support

Check if properties have any value set, as opposed to the default value

t = TeamMessage()

print TeamMessage.name.is_set(t)
#=> False

print t.name
#=> None

t.name = None

print TeamMessage.name.is_set(t)
#=> True

print t.name
#=> None

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

pytracts-2.0.0-py2.py3-none-any.whl (90.7 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file pytracts-2.0.0-py2.py3-none-any.whl.

File metadata

  • Download URL: pytracts-2.0.0-py2.py3-none-any.whl
  • Upload date:
  • Size: 90.7 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for pytracts-2.0.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 89873a103ddf85755880c1e750305d4c0445a966da05f6d7b063448edc5d65d9
MD5 6e1d70b6b0a799b25c34a101d5915762
BLAKE2b-256 7f2cb88559e608f4c3f466d4cd80f5d5bf1cc58140751f2ebeca99d7266269b2

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page