Skip to main content

py2api

Expose python objects to external clients with minimal effort.

For the eager

py2api allows you to role out webservices (with flask) with minimal boilerplate.

To get started quickly, have a look at the examples, and play around with edits of them.

The main boilerplate here is writing code for input and output conversion. To help with this, we provide some tools. Have a look at the output_trans.py and py2rest/input_trans.py modules, especially the doctest, which should give you an idea of how to work it.

A webservice module assembled with py2api looks something like this:

from mystuff import SystemService
from py2api import OutputTrans
from py2api.py2rest import InputTransWithAttrInURL

attr_list = ["ping", "status", "stop", "start", "restart"]

name = "/api/process/"
process = WebObjWrapper(
    obj_constructor=SystemService,
    obj_constructor_arg_names=["name"],
    permissible_attr=attr_list,
    input_trans=InputTransWithAttrInURL(trans_spec=None, attr_from_url=name + "(\w+)"),
    output_trans=OutputTrans(trans_spec=None),
    name=name,
    debug=0,
)

app = add_routes_to_app(
    app, routes={process.__name__ + attr: process for attr in attr_list}
)

Note the two trans_spec=None used when making the input_trans and output_trans arguments of WebObjWrapper. There was no need to specify any special conversion there because the methods we were wrapping here only used simple types (string, int, float, list).

But often, when faced with more complex types, specifying how to carry out conversion in each and every situation constitutes a big part of the boilerplate. We provide tools to do this through a mapping (dict) separate from the code. This enables tools to be created to operate on this specification. You can specify conversion verbosely, by specifying how every argument of every function should be converted. If you're the type, you can also do so concisely, by specifying contextual rules involving the object's names and types, etc. grouping conversion rules together.

Such a (more concise) conversion specification looks like this:

from py2api.constants import _ATTR, _ARGNAME, _ELSE, _VALTYPE

def ensure_array(x):
    if not isinstance(x, ndarray):
        return array(x)
    return x

trans_spec = {
        _ATTR: {
            'this_attr': list,
            'other_attr': str,
            'yet_another_attr': {
                _VALTYPE: {
                    dict: lambda x: x
                },
                _ELSE: lambda x: {'result': x}
            }
        },
        _ARGNAME: {
            'sr': int,
            'ratio': float,
            'snips': list,
            'wf': ensure_array
        },
    }
}

See that the conversion function could be a builtin function like list, str, float, or int, or could be a custom function, declared on the fly with a lambda, or refering to a function declared elsewhere.

Motivation

Say you have some functions, classes and/or whole modules that contain some functionality that you'd like to expose as a webservice. So you grab some python libary to do that. If you make your choice right, you won't have to deal with the nitty gritty details of receiving and sending web requests. Say, you chose flask. Good choice. Known to have minimal boiler plate. So now all you have to do make a module and do something like this...

from blah import a_func_you_want_to_expose
@app.route("/a_func_you_want_to_expose/", methods=['GET'])
def _a_func_you_want_to_expose():
    # A whole bunch of boiler plate to get your arguments out
    # of the web request object, which could be in the url, the json, the data...
    # oh, and then you have to convert these all to python objects, 
    # because they're all simple types like strings, numbers and lists, 
    # you'll probably want to do some validation, perhaps add a few 
    # things that weren't really a concern of the original function, 
    # like logging, caching, providing some documentation/help, 
    # and then, oh, okay, you can call that function of yours now:

    results = a_func_you_want_to_expose(...the arguments...)

    # But wait, it's not finished! You'll need to convert these results 
    # to something that a web request can handle. Say a string or a json...

That's all. Enjoyed it?

Now do that again, and again, and again, for every fooing object you want to expose.

Still enjoy it?

We didn't. So we made py2api.

With py2api, you write the same boilerplate, but you only write it once. In py2api you specify all the concerns of the routes you want elsewhere, a bit like what OpenAPI does. But further, you have tools to reduce this specification to a set of conventions and rules that define them. For example, if you use a variable called "num" a lot, and most of the time it has to be an int, except in a few cases, your specification has to be just that. It looks like this:

This whole py2api thing wasn't only about minimizing the time spent on a boilerplate.

It's also because separation of concerns is good, and in the above example, concerns aren't as separated as they could: You have conversion, validation, logging, caching.

Also, because code reuse is good, and we don't mean copy/pasting: You probably have many arguments and types that show up in different places, and will end up writing, or copy pasting, code to handle these many times. What if you made a mistake? You'll have to find all those copies and correct them all.

Minimizing what you have to write to get something done is good: There's less places to look for bugs.

Consistency is good.

Et cetera.

Metadata

Release files for py2api 0.0.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for py2api 0.0.5
File Size Uploaded
py2api-0.0.5.tar.gz 39.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for py2api 0.0.5
File Interpreter ABI Platform
py2api-0.0.5-py3-none-any.whl Python 3 none any Details

Total release size: 83.1 kB

Release files / py2api-0.0.5.tar.gz

Download URL py2api-0.0.5.tar.gz
Size 39.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e4ced9c23491ce0016cff6370a71de9073c209dce1e61833894bb0d53ceea69b
BLAKE2b-256 checksum
How to use checksums
d57d67c58f9ce00eebf6840629caf6fa86d19f2f634ca8bc65b0f73ab65ae9a0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / py2api-0.0.5-py3-none-any.whl

Download URL py2api-0.0.5-py3-none-any.whl
Size 43.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
01dc58924e0d4fbda377100aff4c621591cc1e4932f01b325ff84bc9b8d40473
BLAKE2b-256 checksum
How to use checksums
b6af43f3305dde7520bd077b2dff2d39e397e4aa5da77c6da6deb0158d0fdf77
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.0.5 This release

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page