sidekick.functions

The functions in this module are responsible for creating, transforming, composing and introspecting other functions. Some of those functions might be familiar from the standard lib’s functools module. In spite of those similarities, this module is not a drop-in replacement of the standard lib’s functools module.

This module also exposes the fn type, that extends standard Python functions with new methods and operators. This extended function behavior is applied to most sidekick’s functions and can be easily re-used to extend user code.

Basic types

fn(func) Base class for function-like objects in Sidekick.

Function introspection and conversion

Stub Represent a function declaration Stub.
arity Return arity of a function.
signature Return the signature of a function.
stub Return a Stub object representing the function signature.
to_callable(func) Convert argument to callable.
to_function(func[, name]) Return object as as Python function.
to_fn(func) Convert callable to an fn object.

Partial application

curry(n[, func]) Return the curried version of a function of n arguments.
partial(*args, **kwargs) Return a new function that partially apply the given arguments and keywords.
rpartial(func, *args, **kwargs) Partially apply arguments from the right.
method Return a function that calls a method of its argument with the given values.

Composition

compose Create function that apply argument from right to left.
pipe Pipe a value through a sequence of functions.
pipeline Similar to compose, but order of application is reversed.
thread Similar to pipe, but accept extra arguments to each function in the pipeline.
rthread Like thread, but data is passed as last argument to functions, instead of first.
thread_if Similar to thread, but each form must be a tuple with (test, fn, …args) and only pass the argument to fn if the boolean test is True.
rthread_if Similar to rthread, but each form must be a tuple with (test, fn, …args) and only pass the argument to fn if the boolean test is True.
juxt Juxtapose several functions.

Combinators

identity The identity function.
ridentity Return last positional argument.
always Return a function that always return x when called with any number of arguments.
rec Fix func first argument as itself.
trampoline Decorator that implements tail call elimination via the trampoline technique.
value Evaluate argument, if it is a function or return it otherwise.
call(*args, **kwargs) Return a function caller.
do Runs func on x, returns x.

Runtime control

once Limit function to a single invocation.
thunk A thunk that represents a lazy computation.
call_after Creates a function that invokes func once it’s called more than n times.
call_at_most Creates a function that invokes func while it’s called less than n times.
throttle Limit the rate of execution of func to once at each dt seconds.
background Return a function that executes in the background.
error Raises the given exception.
raising Creates function that raises the given exception.
retry Retry to execute function at least n times before raising an error.
catch Handle exception in function.

Transform arguments

flip Flip the order of arguments in a binary operator.
select_args Creates a function that calls func with the arguments reordered.
keep_args Uses only the first n positional arguments to call func.
reverse_args Creates a function that invokes func with the positional arguments order reversed.
skip_args Skips the first n positional arguments before calling func.
splice_args Return a function that receives a sequence as single argument and splice them into func.
variadic_args Return a function that receives variadic arguments and pass them as a tuple to func.

API reference

class sidekick.functions.fn(func)[source]

Base class for function-like objects in Sidekick.

curry(arity, func=None, **kwargs) → Union[sidekick.functions.fn.Curried, callable][source]

Return a curried function with given arity.

generator

Decorates generator function to return a sidekick iterator instead of a regular Python generator.

Examples

>>> @sk.generator
... def fibonacci():
...     x = y = 1
...     while True:
...         yield x
...         x, y = y, x + y
>>> fibonacci()
sk.iter([1, 1, 2, 3, 5, 8, ...])
partial(*args, **kwargs)[source]

Return a fn-function with all given positional and keyword arguments applied.

result(*args, **kwargs)[source]

Return a result instance after function call.

Exceptions are converted to Err() cases.

rpartial(*args, **kwargs)[source]

Like partial, but fill positional arguments from right to left.

single(*args, **kwargs)[source]

Similar to partial, but with a few constraints:

  • Resulting function must be a function of a single positional argument.
  • Placeholder expressions are evaluated passing this single argument to the resulting function.

Example

>>> add = fn(lambda x, y: x + y)
>>> g = add.single(_, 2 * _)
>>> g(10)  # g(x) = x + 2 * x
30
Returns:fn
thunk(*args, **kwargs)[source]

Pass all arguments to function, without executing.

Returns a thunk, i.e., a zero-argument function that evaluates only during the first execution and re-use the computed value in future evaluations.

See also

thunk()

classmethod wraps(func, fn_obj=None)[source]

Creates a fn function that wraps another function.

sidekick.functions.quick_fn(func: callable) → sidekick.functions.fn.fn[source]

Faster fn constructor.

This is about twice as fast as the regular fn() constructor. It assumes that fn is

class sidekick.functions.Stub[source]

Represent a function declaration Stub.

count()

Return number of occurrences of value.

index()

Return first index of value.

Raises ValueError if the value is not present.

name

Alias for field number 0

render(imports=False) → str[source]

Render complete stub file, optionally including imports.

required_imports() → set[source]

Return set of imported symbols

signatures

Alias for field number 1

sidekick.functions.arity[source]

Return arity of a function.

Examples

>>> from operator import add
>>> sk.arity(add)
2
sidekick.functions.signature[source]

Return the signature of a function.

sidekick.functions.stub[source]

Return a Stub object representing the function signature.

sidekick.functions.to_function(func: Any, name=None) → function[source]

Return object as as Python function.

Non-functions are wrapped into a function definition.

sidekick.functions.to_callable(func: Any) → Callable[source]

Convert argument to callable.

This differs from to_function in which it returns the most efficient version of object that has the same callable interface as the argument.

This removes sidekick’s function wrappers such as fn and try to convert argument to a straightforward function value.

This defines the following semantics:

  • Sidekick’s fn: extract the inner function.
  • None: return the identity function.
  • Mappings: map.__getitem__
  • Functions, methods and other callables: returned as-is.
sidekick.functions.to_fn(func: Any) → sidekick.functions.fn.fn[source]

Convert callable to an fn object.

If func is already an fn instance, it is passed as is.

sidekick.functions.curry(n, func=None)[source]

Return the curried version of a function of n arguments.

Curried functions return partial applications of the function if called with missing arguments:

>>> add = sk.curry(2, lambda x, y: x + y)

We can call a function two ways:

>>> add(1, 2) == add(1)(2)
True

This is useful for building simple functions from partial application

>>> succ = add(1)
>>> succ(2)
3

curry() is itself a curried function, hence it can be called as

>>> add = sk.curry(2)(lambda x, y: x + y)

or equivalently as a decorator

>>> @sk.curry(2)
... def add(x, y):
...     return x + y

Currying usually requires functions of fixed number of arguments (the number of arguments is called the arity of a function). We can control how many arguments participate in the auto-currying by passing the arity number as the first argument to the curry() function.

Variadic functions are accepted, and arity is understood as the minimum number of arguments necessary to invoke the function. The caller can, however, specify additional arguments.

But it accepts more than 2 arguments, if needed. (Notice that only the first two arguments auto-curry.)

>>> add = sk.curry(2, lambda *args: sum(args))
>>> add(1, 2, 3, 4)
10

Sometimes we don’t want to specify the arity of a function or don’t want to think too much about it. curry() accepts 'auto' as an arity specifier that makes it try to infer the arity automatically. Under the hood, it just calls arity() to obtain the correct value.

Sidekick curries most functions where it makes sense. Variadic functions cannot be curried if the extra arguments can be passed by position. This decorator inspect the decorated function to determine if it can be curried or not.

sidekick.functions.partial(*args, **kwargs) → sidekick.functions.fn.fn[source]

Return a new function that partially apply the given arguments and keywords.

Additional positional and keyword arguments after partially applied to function

Parameters:func – Function or func-like object.

Examples

>>> from operator import add
>>> incr =  sk.partial(add, 1)
>>> incr(41)
42

See also

rpartial()

sidekick.functions.rpartial(func: Optional[Callable], *args, **kwargs) → sidekick.functions.fn.fn[source]

Partially apply arguments from the right.

Examples

>>> from operator import truediv as div
>>> half = sk.rpartial(div, 2)
>>> half(42)
21.0

See also

partial()

sidekick.functions.method[source]

Return a function that calls a method of its argument with the given values.

A method caller object. It can be used as a function

>>> pop_first = sk.method("pop", 0)
>>> pop_first([1, 2, 3])
1

or as a function factory.

>>> pop_first = sk.method.pop(0)
>>> pop_first([1, 2, 3])
1

The second usage is syntactically cleaner and prevents the usage of invalid Python names. All method calls performed in the sk.method object returns the corresponding methodcaller function.

sidekick.functions.compose[source]

Create function that apply argument from right to left.

compose(f, g, h, …) ==> f << g << h << …

Example

>>> f = sk.compose((X + 1), (X * 2))
>>> f(2)  # double than increment
5

See also

pipe() pipeline()

sidekick.functions.pipe[source]

Pipe a value through a sequence of functions.

I.e. pipe(data, f, g, h) is equivalent to h(g(f(data))) or to data | f | g | h, if f, g, h are fn objects.

Examples

>>> from math import sqrt
>>> sk.pipe(-4, abs, sqrt)
2.0
sidekick.functions.pipeline[source]

Similar to compose, but order of application is reversed.

pipeline(f, g, h, …) ==> f >> g >> h >> …

Example

>>> f = sk.pipeline((X + 1), (X * 2))
>>> f(2)  # increment and double
6

See also

pipe() compose()

sidekick.functions.thread[source]

Similar to pipe, but accept extra arguments to each function in the pipeline.

Arguments are passed as tuples and the value is passed as the first argument.

Examples

>>> sk.thread(20, (op.div, 2), (op.mul, 4), (op.add, 2))
42.0

See also

pipe() rthread()

sidekick.functions.rthread[source]

Like thread, but data is passed as last argument to functions, instead of first.

Examples

>>> sk.rthread(2, (op.div, 20), (op.mul, 4), (op.add, 2))
42.0

See also

pipe() thread()

sidekick.functions.thread_if[source]

Similar to thread, but each form must be a tuple with (test, fn, …args) and only pass the argument to fn if the boolean test is True.

If test is callable, the current value to the callable to decide if fn must be executed or not.

Like thread, Arguments are passed as tuples and the value is passed as the first argument.

Examples

>>> sk.thread_if(20, (True, op.div, 2), (False, op.mul, 4), (sk.is_even, op.add, 2))
12.0
sidekick.functions.rthread_if[source]

Similar to rthread, but each form must be a tuple with (test, fn, …args) and only pass the argument to fn if the boolean test is True.

If test is callable, the current value to the callable to decide if fn must be executed or not.

Like rthread, Arguments are passed as tuples and the value is passed as the last argument.

Examples

>>> sk.rthread_if(20, (True, op.div, 2), (False, op.mul, 4), (sk.is_even, op.add, 2))
0.1
sidekick.functions.juxt[source]

Juxtapose several functions.

Creates a function that calls several functions with the same arguments and return a tuple with all results.

It return a tuple with the results of calling each function. If last=True or first=True, return the result of the last/first call instead of a tuple with all the elements.

Examples

We can create an argument logger using either first/last=True

>>> sqr_log = sk.juxt(print, (X * X), last=True)
>>> sqr_log(4)
4
16

Consume a sequence

>>> pairs = sk.juxt(next, next)
>>> nums = iter(range(10))
>>> pairs(nums), pairs(nums)
((0, 1), (2, 3))
sidekick.functions.identity[source]

The identity function.

Return its first argument unchanged. Identity accepts one or more positional arguments and any number of keyword arguments.

Examples

>>> sk.identity(1, 2, 3, foo=4)
1

See also

ridentity()

sidekick.functions.ridentity[source]

Return last positional argument.

Similar to identity, but return the last positional argument and not the first. In the case the function receives a single argument, both identity functions coincide.

Examples
>>> sk.ridentity(1, 2, 3)
3

See also

identity()

sidekick.functions.always[source]

Return a function that always return x when called with any number of arguments.

Examples

>>> f = sk.always(42)
>>> f('answer', for_what='question of life, the universe ...')
42
sidekick.functions.rec[source]

Fix func first argument as itself.

This is a version of the Y-combinator and is useful to implement recursion from scratch.

Examples

In this example, the factorial receive a second argument which is the function it must recurse to. rec pass the function to itself so now the factorial only needs the usual numeric argument.

>>> sk.map(
...     sk.rec(lambda f, n: 1 if n == 0 else n * f(f, n - 1)),
...     range(10),
... )
sk.iter([1, 1, 2, 6, 24, 120, ...])
sidekick.functions.power(func: Optional[Callable], n: int) → sidekick.functions.fn.fn[source]

Return a function that applies f to is argument n times.

power(f, n)(x) ==> f(f(…f(x))) # apply f n times.

Examples

>>> g = sk.power((2 * X), 3)
>>> g(10)
80
sidekick.functions.trampoline[source]

Decorator that implements tail call elimination via the trampoline technique.

Parameters:func – A function that returns an args tuple to call it recursively or raise StopIteration when done.

Examples

>>> @sk.trampoline
... def fat(n, acc=1):
...     if n > 0:
...         return n - 1, acc * n
...     else:
...         raise StopIteration(acc)
>>> fat(5)
120
sidekick.functions.call(*args, **kwargs) → sidekick.functions.fn.fn[source]

Return a function caller.

Creates a function that receives another function and apply the given arguments.

Examples

>>> caller = sk.call(1, 2)
>>> caller(op.add), caller(op.mul)
(3, 2)

This function can be used as a decorator to declare self calling functions:

>>> @sk.call()
... def patch_module():
...     import builtins
...
...     builtins.evil = lambda: print('Evil patch')
...     return True

The variable patch_module will be assigned to the return value of the function and the function object itself will be garbage collected.

sidekick.functions.value[source]

Evaluate argument, if it is a function or return it otherwise.

Parameters:fn_or_value – Callable or some other value. If input is a callable, call it with the provided arguments and return. Otherwise, simply return.

Examples

>>> sk.value(42)
42
>>> sk.value(lambda: 42)
42
sidekick.functions.do[source]

Runs func on x, returns x.

Because the results of func are not returned, only the side effects of func are relevant.

Logging functions can be made by composing do with a storage function like list.append or file.write

Examples

>>> log = []
>>> inc = sk.do(log.append) >> (X + 1)
>>> [inc(1), inc(11)]
[2, 12]
>>> log
[1, 11]
sidekick.functions.once[source]

Limit function to a single invocation.

Repeated calls to the function return the value of the first invocation.

Examples

This is useful to wrap initialization routines or singleton factories. >>> @sk.once … def configure(): … print(‘setting up…’) … return {‘status’: ‘ok’} >>> configure() setting up… {‘status’: ‘ok’}

sidekick.functions.thunk[source]

A thunk that represents a lazy computation.

Python thunks are represented by zero-argument functions that compute the value of computation on demand and store it for subsequent invocations.

This function is designed to be used as a decorator.

Example

>>> @sk.thunk(host='localhost', port=5432)
... def db(host, port):
...     print(f'connecting to SQL server at {host}:{port}...')
...     return {'host': host, 'port': port}
>>> db()
connecting to SQL server at localhost:5432...
{'host': 'localhost', 'port': 5432}
>>> db()
{'host': 'localhost', 'port': 5432}

See also

once()

sidekick.functions.call_after[source]

Creates a function that invokes func once it’s called more than n times.

Parameters:
  • n – Number of times before starting invoking n.
  • func – Function to be invoked.
  • default – Value returned before func() starts being called.

Example

>>> f = sk.call_after(2, (X * 2), default=0)
>>> [f(1), f(2), f(3), f(4), ...]
[0, 0, 6, 8, ...]
sidekick.functions.call_at_most[source]

Creates a function that invokes func while it’s called less than n times. Subsequent calls to the created function return the result of the last func invocation.

Parameters:
  • n – The number of calls at which func is no longer invoked.
  • func – Function to restrict.

Examples

>>> log = sk.call_at_most(2, print)
>>> log("error1"); log("error2"); log("error3"); log("error4")
error1
error2

See also

once() call_after()

sidekick.functions.throttle[source]

Limit the rate of execution of func to once at each dt seconds.

When rate-limited, returns the last result returned by func.

Example

>>> f = sk.throttle(1, (X * 2))
>>> [f(21), f(14), f(7), f(0)]
[42, 42, 42, 42]
sidekick.functions.background[source]

Return a function that executes in the background.

The transformed function return a thunk that forces the evaluation of the function in a blocking manner. Function can also be used as a decorator.

Parameters:
  • func – Function or callable wrapped to support being called in the background.
  • timeout – Timeout in seconds.
  • default – Default value to return if if function timeout when evaluation is requested, otherwise, raises a TimeoutError.

Examples

>>> fib = lambda n: 1 if n <= 2 else fib(n - 1) + fib(n - 2)
>>> fib_bg = sk.background(fib, timeout=1.0)
>>> result = fib_bg(10)  # Do not block execution, return a thunk
>>> result()             # Call the result to get value (blocking operation)
55
sidekick.functions.error[source]

Raises the given exception.

If argument is not an exception, raises ValueError(exc).

Examples

>>> sk.error('some error')
Traceback (most recent call last):
...
ValueError: some error

See also

  • raising(): create a function that raises an error instead of raising it immediately
sidekick.functions.raising[source]

Creates function that raises the given exception.

If argument is not an exception, raises ValueError(exc). The returning function accepts any number of arguments.

Examples

>>> func = sk.raising('some error')
>>> func()
Traceback (most recent call last):
...
ValueError: some error

See also

  • raising(): create a function that raises an error instead of raising it immediately
sidekick.functions.retry[source]

Retry to execute function at least n times before raising an error.

This is useful for functions that may fail due to interaction with external resources (e.g., fetch data from the network).

Parameters:
  • n – Maximum number of times to execute function
  • func – Function that may raise errors.
  • error – Exception or tuple with suppressed exceptions.
  • sleep – Interval in which it sleeps between attempts.

Example

>>> queue = [111, 7, None, None]
>>> process = sk.retry(5, lambda n: queue.pop() * n)
>>> process(6)
42
sidekick.functions.catch[source]

Handle exception in function. If the exception occurs, it executes the given handler.

Examples

>>> nan = sk.always(float('nan'))
>>> div = sk.catch(ZeroDivisionError, (X / Y), handler=nan)
>>> div(1, 0)
nan

The function can be used to re-write exceptions by passing the optional raises parameter.

>>> @sk.catch(KeyError, raises=ValueError("invalid name"))
... def get_value(name):
...     return data[name]
sidekick.functions.flip[source]

Flip the order of arguments in a binary operator.

The resulting function is always curried.

Examples

>>> from operator import sub
>>> rsub = sk.flip(sub)
>>> rsub(2, 10)
8
sidekick.functions.select_args[source]

Creates a function that calls func with the arguments reordered.

Examples

>>> double = sk.select_args([0, 0], (X + Y))
>>> double(21)
42
sidekick.functions.keep_args[source]

Uses only the first n positional arguments to call func.

Examples

>>> incr = sk.keep_args(1, (X + 1))
>>> incr(41, 'whatever')
42
sidekick.functions.reverse_args[source]

Creates a function that invokes func with the positional arguments order reversed.

Examples

>>> concat = sk.reverse_args(lambda x, y, z: x + y + z)
>>> concat("a", "b", "c")
'cba'
sidekick.functions.skip_args[source]

Skips the first n positional arguments before calling func.

Examples

>>> incr = sk.skip_args(1, (X + 1))
>>> incr('whatever', 41)
42
sidekick.functions.splice_args[source]

Return a function that receives a sequence as single argument and splice them into func.

Parameters:
  • func – Function that receives several positional arguments.
  • slice – If given and is a slice, correspond to the slice in the input arguments that will be passed to func.

Example

>>> vsum = sk.splice_args(max)
>>> vsum([1, 2, 3, 4])
4
sidekick.functions.variadic_args[source]

Return a function that receives variadic arguments and pass them as a tuple to func.

Parameters:func – Function that receives a single tuple positional argument.

Example

>>> vsum = sk.variadic_args(sum)
>>> vsum(1, 2, 3, 4)
10