Skip to main content

Simple WSGI JSON API Server

Project description

swjas

Simple WSGI JSON API Server

Installation

pip install swjas

Core

Usage

Call core.makeApplication to create your WSGI application.

  • makeApplication
    • Parameters
      • route : Iterable
        Iterable of (path, handler) tuples or lists
        • path : str
          Mapped URL path.
          Do not include scheme, authority, query or fragment.
        • handler : Callable
          Object (or function) to call to handle incoming POST requests to path.
          • Parameters
            • data : dict, list, int, float, string, bool or None
              Deserialied JSON request body if any, otherwhise None
          • Returns
            JSON serializable object or None.
            (Note: datetime.datetime objects have built-in serialization support)
            (Note: Custom objects can be serialized if they provide a _json property that returns a JSON serializable object)
      • allowEmptyRequestBody : bool (True by default)
        In the case of empty or space request body, if True handler argument will be None, otherwise a BadRequestException will be raised.
        (Note: if True empty or space request body will not be distinguishable from null body)
    • Returns
      WSGI application.

Examples

Serve using wsgi_ref

from wsgiref.simple_server import make_server
from swjas.core import makeApplication

# Services

def authentication(data):
    authenticated = data["username"] == "Francesco" and data["password"] == "12345"
    return {
        "authenticated": authenticated
    }

def intDivision(data):
    if data["b"] == 0:
        return {
            "error": "Division by zero"
        }
    return {
        "quotient": data["a"] // data["b"],
        "remainder": data["a"] % data["b"]
    }

# Server

if __name__ == "__main__":
    routes = [
        ('auth', authentication),
        ('idiv', intDivision)
    ]
    server = make_server("localhost", 8000, makeApplication(routes))
    server.serve_forever()

Serve using waitress

import waitress
from swjas.core import makeApplication
import random, string

# Services

def generateRandomID(data):
    id = ""
    for _ in range(data["length"]):
        id += random.choice(string.ascii_letters)
    return {
        "id": id
    }

# Server

if __name__ == "__main__":
    routes = [
        ('genid', generateRandomID)
    ]
    waitress.serve(makeApplication(routes), listen='*:8000')

Define custom JSON serializable objects

class Point:
    
    def __init__(x, y):
        self.x = x
        self.y = y

    @property
    def _json(self):
        return [self.x, self.y]

Clean

Validate and clean the request body.

Usage

Decorate your handlers with the clean.clean decorator.

  • clean
    • Parameters
      • field : Field
        Expected request body scheme.
    • Returns
      Validated and cleaned request body.
    • Raises
      • exceptions.BadRequestException
        If the request body does not match the provided scheme.
  • Field
    • Constructor
      • Parameters
        • missing : Do or Default
          What to do when this field is missing.
        • error : Do or Default
          What to do when this field is invalid.
    • Methods
      • clean (abstract)
        Validate and clean the field.
        Derived fields must implement it.
        • Parameters
          • value
            Deserialized request body value to clean.
        • Returns
          Cleaned value.
        • Raises
          • FieldException
            If the value is invalid.
      • cleanAndAdd
        Validate and clean the field.
        • Parameters
          • present : bool
            Whether the field is present or not.
          • value
            Deserialized request body value to clean.
          • add : Callable
            Object (or function) to call if the field is cleaned.
            • Parameters
              • value
                Cleaned value.

Examples

Simple cleaning

from swjas.clean import clean, Default, IntField, StringField, FloatField, DictField, ListField

@clean(DictField({
    "a": IntField(min=1),
    "b": IntField(min=1)
}))
def sumPositiveIntegers(data):
    return {
        "sum": data["a"] + data["b"]
    }

@clean(ListField(minLength=1, fields=FloatField()))
def floatAverage(data):
    return sum(data) / len(data)

@clean(DictField({
    "username": StringField(minLength=5, maxLength=20, regex=r"[A-Za-z0-9]+"),
    "password": StringField(minLength=8, maxLength=16, regex=r"[A-Za-z0-9]+"),
    "age": IntField(min=18, max=150, missing=Default(None)),
    "fullName": StringField(minLength=1, maxLength=128, missing=Default(None))
}))
def printMe(data):
    print(data)
    return {
        "success": True
    }

Custom field

from swjas.clean import TypeField, FieldException, Do

class EvenNumberField(TypeField):

    def __init__(self, missing=Do.RAISE, error=Do.RAISE):
        super().__init__(int, missing=missing, error=error)

    def clean(self, value):
        value = super().clean(value)
        if value % 2 != 0:
            raise FieldException("Odd number")
        return value

Exceptions

Usage

Any uncatched exceptions.HttpException will be serialized and added to the response body and will set the corresponding Http response status.
Any exception.PrintableException cause set with the raise ... from syntax will be added to the response body too.

Examples

Raise an HttpException

from swjas.exceptions import AuthorizationException, NotFoundException
if not authenticated:
    raise AuthorizationException(message="Requested object requires authentication")
elif not exists:
    raise NotFoundException(message="Requested object does not exist")

Raise an HttpException with cause

try:
    validateRequest(data)
except Exception as e:
    from swjas.exceptions import BadRequestException
    raise BadRequestException(message="Error while validating the request") from e

Create an HttpException on the fly

from swjas.exceptions import HttpException
raise HttpException.build(410)

Define an HttpException

from swjas.exceptions import HttpException

class GoneException(HttpException):
    statusCode = 410

Client

Send requests.

Usage

Run python -m swjas.client -h from command line or call client.request or client.service functions.

Examples

Send a request providing JSON body via command line and save result to JSON file

>>> python -m swjas.client //localhost:8080/signup --outputstatus --indent 4 > response.json
{
    "username": "Francesco",
    "password": "12345"  
}
^Z

Send a request providing JSON body via file and print result to console

>>> python -m swjas.client //localhost:8080/signup --if request.json
{
    "result": "success"
}

Send a request via script

from swjas.client import HttpException, RequestException, request

data = {
    "username": "Francesco",
    "password": "12345"
}

try:
    res = request("//localhost:8080/signup", data)
except HttpException as e:
    print(f"{e.statusCode}: {e.statusMessage}\n{e.responseBody}")
except RequestException as e:
    print(f"Request failed: {e}")

Project details


Download files

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

Source Distribution

swjas-0.1.0.tar.gz (15.8 kB view details)

Uploaded Source

File details

Details for the file swjas-0.1.0.tar.gz.

File metadata

  • Download URL: swjas-0.1.0.tar.gz
  • Upload date:
  • Size: 15.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.32.1 CPython/3.7.3

File hashes

Hashes for swjas-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f3a9ae56fe4d25c2cb0cabe0069e9345916f2a8d21a5e74c175b25340593bb32
MD5 5d9dc5bd4e28d356bba556705ae5466e
BLAKE2b-256 7c3db8a19dc55c956e720f953f7fd6877dd6e1b59174959024caef1d61ea03b3

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