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
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)
>>> awaitable_times_4 = sacompose(double, async_double)
>>>
>>> regular_times_4(1)
4
>>> asyncio.run(awaitable_times_4(1))
4
If inspect.markcoroutinefunction (added in Python 3.12) is available, sacompose instances work correctly with inspect.iscoroutinefunction:
>>> inspect.iscoroutinefunction(regular_times_4)
False
>>> inspect.iscoroutinefunction(awaitable_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(awaitable_times_4, sacompose)
True
>>> isinstance(awaitable_times_4, (compose, acompose))
False
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
File details
Details for the file compose-1.6.0.tar.gz
.
File metadata
- Download URL: compose-1.6.0.tar.gz
- Upload date:
- Size: 5.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 12de9aaf42ed4d13e3d17c1b5d45bdd90723e18241ca6f5f8f28ffd305dbb468 |
|
MD5 | dbddf47973e6577b2bf9329fd9b56762 |
|
BLAKE2b-256 | 4d62b793f9c68033c9502c06c35549f1a703aadc37743cdce4056c7f39dbc1de |
File details
Details for the file compose-1.6.0-py38-none-any.whl
.
File metadata
- Download URL: compose-1.6.0-py38-none-any.whl
- Upload date:
- Size: 5.0 kB
- Tags: Python 3.8
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 99798fafb8bd6c7cfa7fbd087b161c9848c843716446def40a54e7112808f8e2 |
|
MD5 | bf91f5e431ab5d9b920e9149fe363864 |
|
BLAKE2b-256 | 700529125ab0b994564751e36b0a327afe65852e37998f80c98ac76b48677012 |
File details
Details for the file compose-1.6.0-py35-none-any.whl
.
File metadata
- Download URL: compose-1.6.0-py35-none-any.whl
- Upload date:
- Size: 5.0 kB
- Tags: Python 3.5
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 36faff55a5d649c0e77deb9bf1f80bc3abe959d306b5bf98bd372ad00e4c78ab |
|
MD5 | 99dbadbeb93b304f39c1d3276c7f411e |
|
BLAKE2b-256 | f8a38fafdbe9159aaf3cc213437cbae824f97ddcc7bea3e36befae0d624f4605 |
File details
Details for the file compose-1.6.0-py2.py30-none-any.whl
.
File metadata
- Download URL: compose-1.6.0-py2.py30-none-any.whl
- Upload date:
- Size: 4.6 kB
- Tags: Python 2, Python 3.0
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | abed7e3981269992e37b1c12aa4cd466e4b5ce8dcb4d50688a97a6efc5e2d28f |
|
MD5 | afa0f8196b3727f46444580a1b3d6665 |
|
BLAKE2b-256 | eb6a29a8e3096c236cf83d16c7ddfabe4ba907592183e2b4332b0c4e09e0dbdb |