Skip to main content

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

For static type checking, also install the type hint stubs:

pip install compose-stubs

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:

>>> 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 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

acompose instances always return awaitable values, even if none of the composed functions are async:

>>> awaitable_times_16 = acompose(double, double, double, double)
>>> asyncio.run(awaitable_times_16(1))
16

sacompose is like acompose, but sacompose instances return an awaitable value only if any of the composed functions return an awaitable value:

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

If inspect.markcoroutinefunction is available, acompose and sacompose instances will be correctly detected as coroutine functions:

>>> inspect.iscoroutinefunction(async_times_16)
True
>>> inspect.iscoroutinefunction(awaitable_times_16)
True
>>> inspect.iscoroutinefunction(regular_times_4)
False
>>> inspect.iscoroutinefunction(async_times_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(async_times_4, sacompose)
True
>>> isinstance(async_times_4, (compose, acompose))
False

Release files for compose 1.6.2

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

Source distribution (sdist)

Source distribution for compose 1.6.2
File Size Uploaded
compose-1.6.2.tar.gz 5.7 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for compose 1.6.2
File Interpreter ABI Platform
compose-1.6.2-py38-none-any.whl Python 3.8 none any Details
compose-1.6.2-py35-none-any.whl Python 3.5 none any Details
compose-1.6.2-py2.py30-none-any.whl Python 3.0, Python 2 none any Details

Total release size: 20.3 kB

Release files / compose-1.6.2.tar.gz

Download URL compose-1.6.2.tar.gz
Size 5.7 kB
Tags Source
SHA-256 checksum
How to use checksums
c943fa8284c1cb3892925388862e203c9888484e4ce3fd5171506769690d8b4d
BLAKE2b-256 checksum
How to use checksums
d832bac364dddb79b58d5df429bb4f14a217e5d679f272eb16df8f68143ec1e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.12.6

Release files / compose-1.6.2-py38-none-any.whl

Download URL compose-1.6.2-py38-none-any.whl
Size 5.0 kB
Tags Python 3.8
SHA-256 checksum
How to use checksums
5074c7c36bea8d073130398b905410e606a1f75e7f7bd8754f73980028509d83
BLAKE2b-256 checksum
How to use checksums
ccb2166a868ceb985939320afd32f5b8c3911bb0f56fb190dfc97a721bfb5c4a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.12.6

Release files / compose-1.6.2-py35-none-any.whl

Download URL compose-1.6.2-py35-none-any.whl
Size 5.0 kB
Tags Python 3.5
SHA-256 checksum
How to use checksums
c8b58b563594199ca40c864bd44e094392eaa1c4f4492ca8591bd54582b1f093
BLAKE2b-256 checksum
How to use checksums
2308f510f8522265766b988293ef4ddbd0e4396e51c920b08bcd3b268acac644
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.12.6

Release files / compose-1.6.2-py2.py30-none-any.whl

Download URL compose-1.6.2-py2.py30-none-any.whl
Size 4.6 kB
Tags Python 2 Python 3.0
SHA-256 checksum
How to use checksums
e704e66df7cb8888d6c30e46491f94da80d38b2d45511d138dafaa865386fd7d
BLAKE2b-256 checksum
How to use checksums
0b9fe65ede617a93729dd35ceddfe798a6edbf8ed5036df2a552e0e46c7e78c2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.12.6

Release history Release notifications | RSS feed

This release

1.6.2 This release

4 release files

1.6.1

4 release files

1.6.0

4 release files

1.5.0

4 release files

1.4.8

4 release files

1.4.7

4 release files

1.4.6

4 release files

1.4.5

4 release files

1.4.4

4 release files

1.4.3

4 release files

1.4.2

4 release files

1.4.1

4 release files

1.4.0

4 release files

1.3.1

4 release files

1.3.0

4 release files

1.2.8

4 release files

1.2.7

4 release files

1.2.6

4 release files

1.2.5

4 release files

1.2.4

4 release files

1.2.3

4 release files

1.2.2

4 release files

1.2.1

4 release files

1.2.0

4 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

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