Skip to main content

The classic ``compose``, with all the Pythonic features.

Project description

The classic compose, with all the Pythonic features.

This compose follows the lead of functools.partial and returns callable compose objects which:

  • have a regular and unambiguous repr,

  • retain correct signature introspection,

  • allow introspection of the composed callables,

  • can be type-checked,

  • can be weakly referenced,

  • can have attributes,

  • will merge when nested, and

  • can be pickled (if all composed callables can be pickled).

For async/await support, different variants of compose are included.

Versioning

This library’s version numbers follow the SemVer 2.0.0 specification.

Installation

pip install compose

Usage

Basics

Import compose:

>>> from compose import compose

All the usual function composition you know and love:

>>> def double(x):
...     return x * 2
...
>>> def increment(x):
...     return x + 1
...
>>> double_then_increment = compose(increment, double)
>>> double_then_increment(1)
3

Of course any number of functions can be composed:

>>> def double(x):
...     return x * 2
...
>>> times_eight = compose(double, double, double)
>>> times_16 = compose(double, double, double, double)

We still get the correct signature introspection:

>>> def f(a, b, c=0, **kwargs):
...     pass
...
>>> def g(x):
...     pass
...
>>> g_of_f = compose(g, f)
>>> import inspect
>>> inspect.signature(g_of_f)
<Signature (a, b, c=0, **kwargs)>

And we can inspect all the composed callables:

>>> g_of_f.functions  # in order of execution:
(<function f at 0x...>, <function g at 0x...>)

compose instances flatten when nested:

>>> times_eight_times_two = compose(double, times_eight)
>>> times_eight_times_two.functions == times_16.functions
True

When programmatically inspecting arbitrary callables, we can check if we are looking at a compose instance:

>>> isinstance(g_of_f, compose)
True

compose raises a TypeError when called with no arguments or with any non-callable arguments:

>>> compose()
Traceback (most recent call last):
    ...
TypeError: 'compose' needs at least one argument
>>> compose(increment, 'oops', increment)
Traceback (most recent call last):
    ...
TypeError: 'compose' arguments must be callable

async/await

We can compose async code by using acompose or sacompose (they are mostly the same):

>>> import asyncio
>>> from compose import acompose
>>>
>>> async def get_data():
...     # pretend this data is fetched from some async API
...     await asyncio.sleep(0)
...     return 42
...
>>> get_and_double_data = acompose(double, get_data)
>>> asyncio.run(get_and_double_data())
84

acompose and sacompose can compose any number of async and regular functions, in any order:

>>> async def async_double(x):
...     await asyncio.sleep(0)
...     return x * 2
...
>>> async_times_16 = acompose(async_double, double, async_double, double)
>>> asyncio.run(async_times_16(1))
16

sacompose provides a different way of handling a corner case that arises when composing functions that we get from users or other code: what if every function we receive to compose is regular, not async, but we want to support async?

  • acompose handles that case by returning an awaitable anyway - so we can just write simple code that calls await in all cases. This is the best choice for function composition that we know will be used in async code.

  • sacompose handles that case by returning a callable which will sometimes behave in an async way, by returning an awaitable only if any of the composed functions return an awaitable. This is needed to simplify reusable helper code that can’t know if it is composing for regular or async code:

    >>> from compose import sacompose
    >>>
    >>> regular_times_4 = sacompose(double, double)
    >>> awaitable_times_4 = sacompose(double, async_double)
    >>>
    >>> regular_times_4(1) == 4
    True
    >>> asyncio.run(awaitable_times_4(1)) == 4
    True

acompose and sacompose instances flatten when nested:

>>> acompose(f, acompose(f, f)).functions == (f, f, f)
True
>>> acompose(sacompose(f, f), f).functions == (f, f, f)
True
>>> sacompose(acompose(f, f), f).functions == (f, f, f)
True
>>> sacompose(f, sacompose(f, f)).functions == (f, f, f)
True

But compose instances don’t flatten when nested into acompose and sacompose, and vice versa:

>>> acompose(g_of_f).functions
(compose(<function g at 0x...>, <function f at 0x...>),)
>>> sacompose(g_of_f).functions
(compose(<function g at 0x...>, <function f at 0x...>),)
>>> compose(acompose(g, f)).functions
(acompose(<function g at 0x...>, <function f at 0x...>),)
>>> compose(sacompose(g, f)).functions
(sacompose(<function g at 0x...>, <function f at 0x...>),)

compose, acompose, and sacompose instances are all distinct types:

>>> isinstance(g_of_f, compose)
True
>>> isinstance(g_of_f, (acompose, sacompose))
False
>>> isinstance(async_times_16, acompose)
True
>>> isinstance(async_times_16, (compose, sacompose))
False
>>> isinstance(awaitable_times_4, sacompose)
True
>>> isinstance(awaitable_times_4, (compose, acompose))
False

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

compose-1.4.6.tar.gz (5.7 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

compose-1.4.6-py38-none-any.whl (5.1 kB view details)

Uploaded Python 3.8

compose-1.4.6-py35-none-any.whl (5.1 kB view details)

Uploaded Python 3.5

compose-1.4.6-py2.py30-none-any.whl (4.7 kB view details)

Uploaded Python 2Python 3.0

File details

Details for the file compose-1.4.6.tar.gz.

File metadata

  • Download URL: compose-1.4.6.tar.gz
  • Upload date:
  • Size: 5.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.0 CPython/3.10.4

File hashes

Hashes for compose-1.4.6.tar.gz
Algorithm Hash digest
SHA256 34a1a157786d8d1cbe03f4502219cb87de083b2c51e4a40b6823d3110f0de8d6
MD5 fe48c3670f0f42cf33a1ded10f8cf86e
BLAKE2b-256 0769fb78ff6f9262619296bd3146668e08658700995a4f0ff5fe93425dde9cdb

See more details on using hashes here.

File details

Details for the file compose-1.4.6-py38-none-any.whl.

File metadata

  • Download URL: compose-1.4.6-py38-none-any.whl
  • Upload date:
  • Size: 5.1 kB
  • Tags: Python 3.8
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.0 CPython/3.10.4

File hashes

Hashes for compose-1.4.6-py38-none-any.whl
Algorithm Hash digest
SHA256 1982c36b37376e635038ec4c5e1a3cbcdc8d234918f2bdf89c321d19975550c7
MD5 588bfd0c2f683d407e35746422af8d59
BLAKE2b-256 ee410692b8d3ab2fd0a443b5c4924571790485a77753c3cc3effe0363ff45b3a

See more details on using hashes here.

File details

Details for the file compose-1.4.6-py35-none-any.whl.

File metadata

  • Download URL: compose-1.4.6-py35-none-any.whl
  • Upload date:
  • Size: 5.1 kB
  • Tags: Python 3.5
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.0 CPython/3.10.4

File hashes

Hashes for compose-1.4.6-py35-none-any.whl
Algorithm Hash digest
SHA256 50923f0ebd8db13a3488e37d5bb80effa8df6aee5f2566f79290352e8e634fc8
MD5 a1535c8f639506729b0ca255ec937870
BLAKE2b-256 95c883475fec54c7b53bf41c18b47a4b0b8c403622674cf216576aebd305ce57

See more details on using hashes here.

File details

Details for the file compose-1.4.6-py2.py30-none-any.whl.

File metadata

  • Download URL: compose-1.4.6-py2.py30-none-any.whl
  • Upload date:
  • Size: 4.7 kB
  • Tags: Python 2, Python 3.0
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.0 CPython/3.10.4

File hashes

Hashes for compose-1.4.6-py2.py30-none-any.whl
Algorithm Hash digest
SHA256 326eccbeb2471f0c0dd8c87b8ee3d27d47d6963dba152fe6b7b899e10b1d2f65
MD5 59748e71d30d8b2a9c36fc390a046c20
BLAKE2b-256 ee853d6b7cb3bdf346dea5c829c3ed5243f01e23ab2ab714c667119bb683010e

See more details on using hashes here.

Supported by

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