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

Uploaded Python 3.8

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

Uploaded Python 3.5

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

Uploaded Python 2Python 3.0

File details

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

File metadata

  • Download URL: compose-1.4.5.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.5.tar.gz
Algorithm Hash digest
SHA256 698d06f225b0ab54e2b7c16b2899d008176edf6f76015fedf49e6977b2100442
MD5 60a086e2cd4d1414c0a1623b0843c0a0
BLAKE2b-256 e320c1a268c8c9b2777b80cb0136de255c706a67292b4391b698b3c476b869af

See more details on using hashes here.

File details

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

File metadata

  • Download URL: compose-1.4.5-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.5-py38-none-any.whl
Algorithm Hash digest
SHA256 6f9c6e5939b5a3d2290d174ac4d711c7abbc7acefa8e9157e80e7427c6298351
MD5 ae217cf560c294884866956af4426e03
BLAKE2b-256 a8f9c234f9c3a851e8ea7009617293e987a70188dc92823fb243e2fff4af8d40

See more details on using hashes here.

File details

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

File metadata

  • Download URL: compose-1.4.5-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.5-py35-none-any.whl
Algorithm Hash digest
SHA256 99ce5a82047b82cd2db9189daddaa15abd97c029edad9b23a0344b25db67846e
MD5 9fa3cd1b20fa4fdbfb9d0221283a2cb5
BLAKE2b-256 3ce3a2b7b44653a53e117bce75f2b9cc9729c88243f7c1876c434c4ed1e8861c

See more details on using hashes here.

File details

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

File metadata

  • Download URL: compose-1.4.5-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.5-py2.py30-none-any.whl
Algorithm Hash digest
SHA256 1b70dd5e75b42ae35b12c164b27f740a3a3d4d7ee601c1978c199a5a9bb9fee5
MD5 b1d056f00b1e2cd27010d27e4e5005b4
BLAKE2b-256 1f743fef45237d1dc6e14d6ba9d7fd2b39405543e7ce8be32809e4173f6a28e4

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