Skip to main content

Define signatures to create beautiful APIs

Project description

https://img.shields.io/pypi/v/autosig.svg https://img.shields.io/travis/piccolbo/autosig.svg https://codecov.io/gh/piccolbo/autosig/branch/master/graph/badge.svg Documentation Status Updates Maintainability

Go straight to the documentation. Install with pip install autosig. Python 3 only.

Motivation

When I look at a great API I always observe a great level of consistency: similarly named and ordered arguments at a syntactic level; similar defaults, range of allowable values etc. on the semantic side. When looking at the code, one doesn’t see these regularities represented very explicitly.

Imagine we are starting to develop a library with three entry points, map, reduce and filter:

from collections import Iterable


def map(function, iterable):
    assert callable(function)
    assert isinstance(iterable, Iterable)
    return (function(x) for x in iterable)


def reduce(function, iterable):
    total = next(iterable)
    for x in iterable:
        total = function(total, x)
    return total


def filter(iterable, fun):
    if not isinstance(iterable, Iterable):
        iterable = [iterable]
    if isinstance(fun, set):
        fun = lambda x: x in fun
    return (x for x in iterable if fun(x))

But this is hardly well crafted. The order and naming of arguments isn’t consistent. One function checks its argument right away. The next doesn’t. The third attempts certain conversions to try and work with arguments that are not iterables or functions. There are reasons to build strict or tolerant APIs, but it’s unlikely that mixing the two within the same API is a good idea, unless it’s done deliberately (for instance offering a strict and tolerant version of every function). It wouldn’t be difficult to fix these problems in this small API but we would end up with duplicated logic that we need to keep aligned for the foreseeable future. Let’s do it instead the autosig way:

from autosig import param, Signature, autosig, check
from collections import Iterable


def to_callable(x):
    return (lambda y: y in x) if isinstance(x, set) else x


def to_iterable(x):
    return x if isinstance(x, Iterable) else [x]


API_signature = Signature(
    function=param(converter=to_callable, validator=callable),
    iterable=param(converter=to_iterable, validator=Iterable))


@autosig(API_signature)
def map(function, iterable):
    return (function(x) for x in iterable)


@autosig(API_signature)
def reduce(function, iterable):
    total = next(iterable)
    for x in iterable:
        total = function(total, x)
    return total


@autosig(API_signature)
def filter(function, iterable):
    return (x for x in iterable if function(x))

Let’s go through it step by step. First we defined 2 simple conversion functions. This is a good first step independent of autosig. Next we create a signature object, with two parameters. These are intialized with objects that define the checking and conversion that need to be performed on those parameters, independent of which function is going to use that signature. A type works as a validator, as does any callable that returns True when a value is valid, returns False or raises an exception otherwise. Finally, we repeat the definition of our three API function, attaching the signature just defined with a decorator and then skipping all the checking and conversion logic and going straight to the meat of the function!

At the cost of a little code we have gained a lot:

  • Explicit definition of the desired API signature, in a single place — DRY principle;

  • association of that signature with API functions, checked at load time — no room for error;

  • uniform application of conversion and validation logic without repeating it;

autosig is the pro tool for the API designer! If you want to take a look at a real package that uses autosig, check out altair_recipes.

Features

  • Define reusable parameters with defaults, conversion and validation logic, documentation, preferred position in the signature and whether keyword-only.

  • Define reusable return values with conversion and validation logic and documentation.

  • Define reusable signatures as ordered maps from names to parameters with optional return value definition.

  • Combine signatures to create complex ones on top of simple ones.

  • Decorate functions and methods with their signatures. Enforced at load time. Conversion and validation logic executed at call time.

  • Not hot about signatures? You can just use parameters as in:

    @autosig
    def reduce(function = param(...), iterable=param(...)):

    for more free-form APIs.

  • Open source (BSD license)

  • Extensive property-based testing, excellent coverage

Credits

This package is heavily based on attrs. While that may change in the future, for now it must be said this is a thin layer over that, with a bit of reflection sprinkled over. It is, I suppose, a quite original direction to take attrs into.

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

autosig-0.10.0.tar.gz (9.0 kB view details)

Uploaded Source

Built Distribution

autosig-0.10.0-py3-none-any.whl (8.5 kB view details)

Uploaded Python 3

File details

Details for the file autosig-0.10.0.tar.gz.

File metadata

  • Download URL: autosig-0.10.0.tar.gz
  • Upload date:
  • Size: 9.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.0.5 CPython/3.6.10 Darwin/19.5.0

File hashes

Hashes for autosig-0.10.0.tar.gz
Algorithm Hash digest
SHA256 c399ac3fcfdcca41c9fb32838304b7185c41149c7b995283b9c37c6f53368b38
MD5 8f16ca985a193f947e178304252fffab
BLAKE2b-256 4e2093d80bd74936949daa87f6999dac81535190aebe49443656d7c80bbde86e

See more details on using hashes here.

File details

Details for the file autosig-0.10.0-py3-none-any.whl.

File metadata

  • Download URL: autosig-0.10.0-py3-none-any.whl
  • Upload date:
  • Size: 8.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.0.5 CPython/3.6.10 Darwin/19.5.0

File hashes

Hashes for autosig-0.10.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fa4521d8bb3d7362e986152e6a7a3aa4efd6e9bc781bd5abf4a7d58d9c7dcea3
MD5 3944d45afab3bc322a527af749bb4450
BLAKE2b-256 89e1f59fba7f94d8a89dea854f3ff5bf8372a8e0d9c20c035b1d0d7b981c8b17

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