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.7.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.7-py38-none-any.whl (5.0 kB view details)

Uploaded Python 3.8

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

Uploaded Python 3.5

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

Uploaded Python 2Python 3.0

File details

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

File metadata

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

File hashes

Hashes for compose-1.4.7.tar.gz
Algorithm Hash digest
SHA256 e492b2ff6cff199f8571ddaa9e4e3ae9a7ee5554ae66434e8beaf8260f818f82
MD5 a7e27a88e23394e7938ba34e2a2a8ebd
BLAKE2b-256 19c3f71970d45a5ecae06d5bf6d61c27e84447d1c8c809a3ac7d64da2acfe4e9

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for compose-1.4.7-py38-none-any.whl
Algorithm Hash digest
SHA256 6f5ad5508855b7c04d79d67652fa5b2ca116121825727074c9e3f181904b95ba
MD5 bd3f0a08a6d948bf49016a3e107d9272
BLAKE2b-256 fd7d86510e4c532e790d1a6a5adafe791b982532c988fc84c05751991ab2eebc

See more details on using hashes here.

File details

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

File metadata

  • Download URL: compose-1.4.7-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.5

File hashes

Hashes for compose-1.4.7-py35-none-any.whl
Algorithm Hash digest
SHA256 92b10dbbce78ef3c25e8fa5eb60114698d9981492b8ab560186a6b9113ebaf2f
MD5 9ea4831e1bcdb54eeaf76edcaa2de9b3
BLAKE2b-256 41703a6741075359cfc88d82ae5d039802fd7ecc1654b770013dc10e32c8711c

See more details on using hashes here.

File details

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

File metadata

  • Download URL: compose-1.4.7-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.5

File hashes

Hashes for compose-1.4.7-py2.py30-none-any.whl
Algorithm Hash digest
SHA256 173bc3dc266ae901308ac3e6874ae3241d566b6dc0849b6bcc16922ef51a9d67
MD5 001525c3dbf3ea11bf384ea2e302e0fe
BLAKE2b-256 362603fe6908baeeb64577a9f1ab96977dfb99351628e0bb935a8daf12bfd0e1

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