3. API documentation

3.1. Core API

A system for helping you separate your IO and state-manipulation code (hereafter referred to as “effects”) from everything else, thus allowing the majority of your code to be easier to test and compose (that is, have the general benefits of purely functional code).

See https://effect.readthedocs.org/ for documentation.

class effect.Effect(intent, callbacks=NOTHING)

Bases: object

Take an object that describes a desired effect (called an “Intent”), and allow binding callbacks to be called with the result of the effect.

Effects can be performed with perform().

Parameters:intent – The intent to be performed.
on(success=None, error=None)

Return a new Effect with the given success and/or error callbacks bound.

The result of the Effect will be passed to the first callback. Any callbacks added afterwards will receive the result of the previous callback. Normal return values are passed on to the next success callback, and exceptions are passed to the next error callback as a sys.exc_info() tuple.

If a callback returns an Effect, the result of that Effect will be passed to the next callback.

effect.sync_perform(dispatcher, effect)

Perform an effect, and return its ultimate result. If the final result is an error, the exception will be raised.

This requires that the effect (and all effects returned from any of its callbacks) be synchronous. If the result is not available immediately, NotSynchronousError will be raised.

effect.sync_performer(f)

A decorator for performers that return a value synchronously.

This decorator should be used if performing the intent will be synchronous, i.e., it will block until the result is available and the result will be simply returned. This is the common case unless you’re using an asynchronous framework like Twisted or asyncio.

Note that in addition to returning (or raising) values as normal, you can also return another Effect, in which case that Effect will be immediately performed with the same dispatcher. This is useful if you’re implementing one intent which is built on top of other effects, without having to explicitly perform them.

The function being decorated is expected to take a dispatcher and an intent, and should return or raise normally. The wrapper function that this decorator returns will accept a dispatcher, an intent, and a box (conforming to the performer interface). The wrapper deals with putting the return value or exception into the box.

Example:

@sync_performer
def perform_foo(dispatcher, foo):
    return do_side_effect(foo)
class effect.TypeDispatcher(mapping)

Bases: object

An Effect dispatcher which looks up the performer to use by type.

Parameters:mapping – mapping of intent type to performer
class effect.ComposedDispatcher(dispatchers)

Bases: object

A dispatcher which composes other dispatchers.

The dispatchers given will be searched in order until a performer is found.

Parameters:dispatchers – Dispatchers to search.
class effect.Delay(delay)

Bases: object

An intent which represents a delay in time.

When performed, the specified delay will pass and then the effect will result in None.

Parameters:delay (float) – The number of seconds to delay.
effect.perform_delay_with_sleep(*args, **kwargs)

Perform a Delay by calling time.sleep.

class effect.ParallelEffects(effects)

Bases: object

An effect intent that asks for a number of effects to be run in parallel, and for their results to be gathered up into a sequence.

effect.async.perform_parallel_async() can perform this Intent assuming all child effects have asynchronous performers. effect.threads.perform_parallel_with_pool() can perform blocking performers in a thread pool.

Note that any performer for this intent will need to be compatible with performers for all of its child effects’ intents. Notably, if child effects have blocking performers, the threaded performer should be used, and if they’re asynchronous, the asynchronous performer should be used.

Performers of this intent must fail with a FirstError exception when any child effect fails, representing the first error.

Parameters:effects – Effects to be performed in parallel.
effect.parallel(effects)

Given multiple Effects, return one Effect that represents the aggregate of all of their effects. The result of the aggregate Effect will be a list of their results, in the same order as the input to this function. If any child effect fails, the first such failure will be propagated as a FirstError exception. If additional error information is desired, use parallel_all_errors().

This is just a convenience wrapper for returning of Effect of ParallelEffects.

Parameters:effects – Effects which should be performed in parallel.
Returns:An Effect that results in a list of results, or which fails with a FirstError.
effect.parallel_all_errors(effects)

Given multiple Effects, return one Effect that represents the aggregate of all of their effects. The result of the aggregate Effect will be a list of their results, in the same order as the input to this function.

This is like parallel(), but it differs in that exceptions from all child effects will be accumulated and provided in the return value, instead of just the first one.

Parameters:effects – Effects which should be performed in parallel.
Returns:An Effect that results in a list of (is_error, result) tuples, where is_error is True if the child effect raised an exception, in which case result will be an exc_info tuple. If is_error is False, then result will just be the result as provided by the child effect.
class effect.Constant(result)

Bases: object

An intent that returns a pre-specified result when performed.

Parameters:result – The object which the Effect will result in.
class effect.Error(exception)

Bases: object

An intent that raises a pre-specified exception when performed.

Parameters:exception (BaseException) – Exception instance to raise.
class effect.Func(func, *args, **kwargs)

Bases: object

An intent that returns the result of the specified function.

Note that Func is something of a cop-out. It doesn’t follow the convention of an intent being transparent data that is easy to introspect, since it just wraps an opaque callable. This has two drawbacks:

  • it’s harder to test, since the only thing you can do is call the function, instead of inspect its data.
  • it doesn’t offer any ability for changing the way the effect is performed.

If you use Func in your application code, know that you are giving up some ease of testing and flexibility. It’s preferable to represent your intents as inert objects with public attributes of simple data. However, this is useful for integrating wih “legacy” side-effecting code in a quick way.

Parameters:
  • func – The function to call when this intent is performed.
  • args – Positional arguments to pass to the function.
  • kwargs – Keyword arguments to pass to the function.
effect.catch(exc_type, callable)

A helper for handling errors of a specific type:

eff.on(error=catch(SpecificException,
                   lambda exc_info: "got an error!"))

If any exception other than a SpecificException is thrown, it will be ignored by this handler and propogate further down the chain of callbacks.

effect.raise_(exception, tb=None)

Simple convenience function to allow raising exceptions from lambdas.

This is slightly more convenient than six.reraise because it takes an exception instance instead of needing the type separate from the instance.

Parameters:exception – An exception instance (not an exception type).
  • raise_(exc) is the same as raise exc.
  • raise_(exc, tb) is the same as raise type(exc), exc, tb.
exception effect.NoPerformerFoundError

Bases: exceptions.Exception

Raised when a performer for an intent couldn’t be found.

exception effect.NotSynchronousError

Bases: exceptions.Exception

Performing an effect did not immediately return a value.

effect.perform(dispatcher, effect)

Perform an effect and invoke callbacks bound to it. You probably don’t want to use this. Instead, use sync_perform() (or, if you’re using Twisted, see the txeffect library).

The dispatcher will be called with the intent, and is expected to return a performer (another callable). See TypeDispatcher and ComposedDispatcher for some implementations of dispatchers, and effect.base_dispatcher for a dispatcher supporting basic intents like Constant et al.

The performer will often be decorated with sync_performer() or the deferred_performer from txeffect and will be invoked with the dispatcher [1] and the intent, and should perform the desired effect. [2] The performer should return the result of the effect, or raise an exception, and the result will be passed on to the first callback, then the result of the first callback will be passed to the next callback, and so on.

Both performers and callbacks may return regular values, raise exceptions, or return another Effect, which will be recursively performed, such that the result of the returned Effect becomes the result passed to the next callback. In the case of exceptions, the next error-callback will be called with a sys.exc_info()-style tuple.

Returns:None
[1]The dispatcher is passed because some performers need to make recursive calls to perform(), because they need to perform other effects (see parallel() and perform_parallel_async() for an example of this).
[2]Without using one of those decorators, the performer is actually passed three arguments, not two: the dispatcher, the intent, and a “box”. The box is an object that lets the performer provide the result, optionally asynchronously. To provide the result, use box.succeed(result) or box.fail(exc_info), where exc_info is a sys.exc_info()-style tuple. Decorators like sync_performer() simply abstract this away.
exception effect.FirstError(exc_info, index)

Bases: exceptions.Exception

One of the effects in a ParallelEffects resulted in an error. This represents the first such error that occurred.