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(douple, 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 0x4048e6f0>, <function g at 0x405228e8>)

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 f at 0x4048e6f0>, <function g at 0x405228e8>),)
>>> sacompose(g_of_f).functions
(compose(<function f at 0x4048e6f0>, <function g at 0x405228e8>),)
>>> compose(acompose(g, f)).functions
(acompose(<function f at 0x4048e6f0>, <function g at 0x405228e8>),)
>>> compose(sacompose(g, f)).functions
(sacompose(<function f at 0x4048e6f0>, <function g at 0x405228e8>),)

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

Uploaded Python 3.8

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

Uploaded Python 3.5

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

Uploaded Python 2Python 3.0

File details

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

File metadata

  • Download URL: compose-1.4.4.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.4.tar.gz
Algorithm Hash digest
SHA256 41deac41b48c22671a66bc8feadb7c99da5e89cbcc346491d041e97cf887c6cb
MD5 a942b152560ce8d5ce1f141c43e35a9c
BLAKE2b-256 d9c8278b64d97d6af9bff14b060f9a95df0d311354496f2b7bad0dbb3c4d12fd

See more details on using hashes here.

File details

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

File metadata

  • Download URL: compose-1.4.4-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.4-py38-none-any.whl
Algorithm Hash digest
SHA256 4563db048bff4dfa937fab29cd22db6abe9d6e0de5c05dc858d948f3512e7a74
MD5 25df921f6792ba93e38a5438f6461087
BLAKE2b-256 439fd826e0da16cc42dc44712b1f36fc166c35a79c2a7f856ec64dcc8e8948d9

See more details on using hashes here.

File details

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

File metadata

  • Download URL: compose-1.4.4-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.4-py35-none-any.whl
Algorithm Hash digest
SHA256 b22164e0047c0821fba312891cdf9d9a92dff26aa2604c2a163f90f2a3b88210
MD5 b938c6b0d072de6fcac9f291fbe9e704
BLAKE2b-256 86b1dacc71487520b43a59c667e04aece3927e1d7ee1e5cdd43b6775d4c74fb4

See more details on using hashes here.

File details

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

File metadata

  • Download URL: compose-1.4.4-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.4-py2.py30-none-any.whl
Algorithm Hash digest
SHA256 a695c72d52b2d9a66593fda3e7563e18de8f54969774dfc18176c517ef7e2fea
MD5 4944489db94517fad96d3760bc5ea635
BLAKE2b-256 63dc4f57b6e602e3ea45c54c23872878f85cbf9b06e05f6183bceefb8f5b4ca6

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