API Usage Examples

Note

Under Python 2.7, some of these examples require the following import statement at the top:

from __future__ import print_function

Getting a Wires object

Python Wires ships with a built-in, shared, readily usable Wires object, called w.

from wires import w             # `w` is a built-in, shared `Wires` object

Alternatively, Wires objects are created with:

from wires import Wires

w = Wires()

The Wires class initializer supports optional arguments to override the default behaviour. For example, here’s a Wires object requiring its wires callables to have exactly one wiring:

from wires import Wires

w = Wires(min_wirings=1, max_wirings=1)

Wiring and Unwiring

The wire method wires a callable to a wires callable [1]:

from wires import w

def say_hi():
    print('Hello from wires!')

w.callable.wire(say_hi)             # calling `w.callable` will call `say_hi`

Multiple wirings to the same callable are allowed:

from wires import w

def say_hi():
    print('Hello from wires!')

w.callable.wire(say_hi)             # calling `w.callable` will call `say_hi`
w.callable.wire(say_hi)             # calling `w.callable` will call `say_hi` twice

Wiring a non-callable raises a TypeError exception:

from wires import w

w.callable.wire(42)                 # raises TypeError: 42 isn't callable

The unwire method unwires a given callable:

from wires import w

def say_hi():
    print('Hello from wires!')

w.callable.wire(say_hi)             # calling `w.callable` will call `say_hi`
w.callable.unwire(say_hi)           # calling `w.callable` no longer calls `say_hi`

If multiple wirings exist, unwire unwires the first matching wiring only:

from wires import w

def say_hi():
    print('Hello from wires!')

w.callable.wire(say_hi)             # calling `w.callable` will call `say_hi`
w.callable.wire(say_hi)             # calling `w.callable` will call `say_hi` twice
w.callable.unwire(say_hi)           # calling `w.callable` will call `say_hi` once
w.callable.unwire(say_hi)           # calling `w.callable` no longer calls `say_hi`

Unwiring a non-wired callable raises a ValueError:

from wires import w

def say_hi():
    print('Hello from wires!')

w.callable.unwire(say_hi)           # raises ValueError: non-wired `say_hi`

Wiring Limits

Limiting the number of wirings on wires callables can be done in two different ways.

Using a custom-initialized Wires object, its wires callables default to its wiring limits:

from wires import Wires

def say_hi():
    print('Hello from wires!')

def say_bye():
    print('Bye, see you soon.')

w = Wires(min_wirings=1, max_wirings=1)

w.callable.wire(say_hi)
w.callable.wire(say_bye)            # raises RuntimeError: max_wirings limit reached
w.callable.unwire(say_hi)           # raises RuntimeError: min_wirings limit reached

Overriding wiring limits on a wires callable basis:

from wires import Wires

def say_hi():
    print('Hello from wires!')

def say_bye():
    print('Bye, see you soon.')

w = Wires()                         # defaults to no wiring limits

w.callable1.min_wirings = 1         # set `w.callable1`'s min wirings
w.callable1.max_wirings = 1         # set `w.callable1`'s max wirings

w.callable1.wire(say_hi)
w.callable1.wire(say_bye)           # raises RuntimeError: max_wirings limit reached
w.callable1.unwire(say_hi)          # raises RuntimeError: min_wirings limit reached

w.callable2.wire(say_hi)
w.callable2.wire(say_bye)           # works, no limits on `w.callable2`
w.callable2.unwire(say_bye)         # works, no limits on `w.callable2`
w.callable2.unwire(say_hi)          # works, no limits on `w.callable2`

Clearing wiring limits on a per-wires callable basis:

from wires import Wires

def say_hi():
    print('Hello from wires!')

def say_bye():
    print('Bye, see you soon.')

w = Wires(min_wirings=1, max_wirings=1)

w.callable1.min_wirings = None      # no min wiring limit on `w.callable1`
w.callable1.max_wirings = None      # no max wiring limit on `w.callable1`

w.callable1.wire(say_hi)
w.callable1.wire(say_bye)           # works, no limits on `w.callable1`
w.callable1.unwire(say_bye)         # works, no limits on `w.callable1`
w.callable1.unwire(say_hi)          # works, no limits on `w.callable1`

w.callable2.wire(say_hi)
w.callable2.wire(say_bye)           # raises RuntimeError: max_wirings limit reached
w.callable2.unwire(say_hi)          # raises RuntimeError: min_wirings limit reached

Overriding per-wires callable wiring limits raises a ValueError when:

  • There is at least one wiring.
  • The current wirings don’t meet the limit trying to be set.
from wires import w

def say_hi():
    print('Hello from wires!')

w.callable.wire(say_hi)
w.callable.min_wirings = 2          # raises ValueError: too few wirings

Calling

Calling a just-created, default wires callable works:

from wires import w

w.callable()

Calling a wires callable calls its wired callables, in wiring order:

from wires import w

def say_hi():
    print('Hello from wires!')

def say_bye():
    print('Bye, see you soon.')

w.callable1.wire(say_hi)
w.callable1.wire(say_bye)
w.callable1()                       # calls `say_hi` first, then `say_bye`

w.callable2.wire(say_bye)
w.callable2.wire(say_hi)
w.callable2()                       # calls `say_bye` first, then `say_hi`

Calling a wires callable where the current number of wirings is below the minimum wiring limit raises a ValueError (set by the Wires object or overriden at the wires callable level):

from wires import w

w.callable.min_wirings = 1
w.callable()                        # raises ValueError: less than min_wirings wired

Argument Passing

Call-time arguments are passed to each wired callable:

from wires import w

def a_print(*args, **kw):
    print('args=%r kw=%r' % (args, kw))

w.callable.wire(a_print)
w.callable()                        # prints: args=() kw={}
w.callable(42, 24)                  # prints: args=(42, 24) kw={}
w.callable(a=42, b=24)              # prints: args=() kw={'a': 42, 'b': 24}
w.callable(42, a=24)                # prints: args=(42,) kw={'a': 24}

Wiring actions can include wire-time arguments, later combined with call-time arguments:

from wires import w

def a_print(*args, **kw):
    print('args=%r kw=%r' % (args, kw))

w.callable1.wire(a_print, 'one')
w.callable2.wire(a_print, a='nother')

w.callable1()                       # prints: args=('one',) kw={}
w.callable1(42, 24)                 # prints: args=('one', 42, 24) kw={}
w.callable1(a=42, b=24)             # prints: args=('one',) kw={'a': 42, 'b': 24}
w.callable1(42, a=24)               # prints: args=('one', 42) kw={'a': 24}

w.callable2()                       # prints: args=() kw={'a': 'nother'}
w.callable2(42, 24)                 # prints: args=(42, 24) kw={'a': 'nother'}
w.callable2(a=42, b=24)             # prints: args=() kw={'a': 42, 'b': 24}
w.callable2(42, a=24)               # prints: args=(42,) kw={'a': 24}

Unwiring actions can include wire-time arguments in the unwire call:

  • If no positional/keyword arguments are passed (other than the mandatory callable argument) the first wiring to that callable is removed.
  • If positional/keyword arguments are passed, the specific wiring to that callable with the provided wire-time arguments is removed.

In either case, a ValueError is raised when no matching wiring exists.

from wires import w

def p_arg(arg):
    print(arg)

w.callable.wire(p_arg, 'a')
w.callable()                        # prints 'a'

w.callable.wire(p_arg, 'b')
w.callable()                        # prints 'a', then prints 'b'

w.callable.unwire(p_arg, 'b')
w.callable()                        # prints 'a'

w.callable.unwire(p_arg)
w.callable()                        # does nothing

w.callable.unwire(p_arg, 'c')       # raises ValueError: no such wiring

Call-time coupling

Note

For a description of possible behaviours, refer to Call-time Coupling Concepts.

By default, calling a wires callable calls all its wirings and returns None:

from wires import Wires

def raise_exception():
    print('about to raise')
    raise ZeroDivisionError()

def return_42():
    print('about to return')
    return 42

w = Wires()                     # Default call coupling.

w.callable.wire(raise_exception)
w.callable.wire(return_42)

w.callable()                    # prints 'about to raise', then 'about to return'
                                # returns None

Call-time coupling can be:

  • Set at the Wires object level, applicable to all its wired callables.
  • Overridden on a wires callable basis.
  • Overridden at call-time.

Setting returns at the Wires object level:

from wires import Wires

def raise_exception():
    print('about to raise')
    raise ZeroDivisionError()

def return_42():
    print('about to return')
    return 42

w = Wires(returns=True)         # Non-default call coupling.

w.callable.wire(raise_exception)
w.callable.wire(return_42)

w.callable()                    # prints 'about to raise', then 'about to return'
                                # returns [(ZeroDivisionError(), None), (None, 42)]

Overriding returns at the wires callable level:

from wires import Wires

def raise_exception():
    print('about to raise')
    raise ZeroDivisionError()

def return_42():
    print('about to return')
    return 42

w = Wires()                     # Default call coupling.
w.callable.returns = True       # Override call coupling for `callable`.

w.callable.wire(raise_exception)
w.callable.wire(return_42)

w.callable()                    # prints 'about to raise', then 'about to return'
                                # returns [(ZeroDivisionError(), None), (None, 42)]

Overriding returns at call-time:

from wires import Wires

def raise_exception():
    print('about to raise')
    raise ZeroDivisionError()

def return_42():
    print('about to return')
    return 42

w = Wires()                     # Default call coupling.

w.callable.wire(raise_exception)
w.callable.wire(return_42)

w(returns=True).callable()      # Override call coupling at call time.
                                # prints 'about to raise', then 'about to return'
                                # returns [(ZeroDivisionError(), None), (None, 42)]

Setting ignore exceptions at the Wires object level:

from wires import Wires

def raise_exception():
    print('about to raise')
    raise ZeroDivisionError()

def return_42():
    print('about to return')
    return 42

w = Wires(ignore_exceptions=False)  # Non-default call coupling.

w.callable.wire(raise_exception)
w.callable.wire(return_42)

w.callable()                        # prints 'about to raise' only
                                    # returns None

Overriding ignore exceptions at the wires callable level:

from wires import Wires

def raise_exception():
    print('about to raise')
    raise ZeroDivisionError()

def return_42():
    print('about to return')
    return 42

w = Wires()                             # Default call coupling.
w.callable.ignore_exceptions = False    # Override call coupling for `callable`.

w.callable.wire(raise_exception)
w.callable.wire(return_42)

w.callable()                            # prints 'about to raise' only
                                        # returns None

Overriding ignore exceptions at call-time:

from wires import Wires

def raise_exception():
    print('about to raise')
    raise ZeroDivisionError()

def return_42():
    print('about to return')
    return 42

w = Wires()                             # Default call coupling.

w.callable.wire(raise_exception)
w.callable.wire(return_42)

w(ignore_exceptions=False).callable()   # Override call coupling at call time.
                                        # prints 'about to raise' only
                                        # returns None

Setting both returns and ignore exceptions at the Wires level:

from wires import Wires

def raise_exception():
    print('about to raise')
    raise ZeroDivisionError()

def return_42():
    print('about to return')
    return 42

w = Wires(returns=True, ignore_exceptions=False)    # Non-default call coupling.

w.callable.wire(raise_exception)
w.callable.wire(return_42)

w.callable()                        # prints 'about to raise' only
                                    # raises RuntimeError((ZeroDivisionError(), None),)

Overriding both returns and ignore exceptions at the wires callable level:

from wires import Wires

def raise_exception():
    print('about to raise')
    raise ZeroDivisionError()

def return_42():
    print('about to return')
    return 42

w = Wires()                             # Default call coupling.
w.callable.returns = True               # Override call coupling for `callable`.
w.callable.ignore_exceptions = False    # Override call coupling for `callable`.

w.callable.wire(raise_exception)
w.callable.wire(return_42)

w.callable()                        # prints 'about to raise' only
                                    # raises RuntimeError((ZeroDivisionError(), None),)

Note

Overriding multiple per wires callable settings can also be done with a single call to set.

Overriding both returns and ignore exceptions at call-time:

from wires import Wires

def raise_exception():
    print('about to raise')
    raise ZeroDivisionError()

def return_42():
    print('about to return')
    return 42

w = Wires()                         # Default call coupling.

w.callable.wire(raise_exception)
w.callable.wire(return_42)

w(returns=True, ignore_exceptions=False).callable()
                                    # prints 'about to raise' only
                                    # raises RuntimeError((ZeroDivisionError(), None),)

Introspection

Wires objects

Using dir() to obtain all attributes:

from wires import w

def say_hi():
    print('Hello from wires!')

w.callable1.wire(say_hi)
w.callable2.wire(say_hi)

dir(w)                              # 'callable1' and 'callable2' in resulting list

Using len() to get the wires callables count:

from wires import w

def say_hi():
    print('Hello from wires!')

w.callable1.wire(say_hi)
w.callable2.wire(say_hi)

len(w)                                  # returns 2

Iterating over a Wires object produces each wires callable:

from wires import w

def say_hi():
    print('Hello from wires!')

w.callable1.wire(say_hi)
w.callable2.wire(say_hi)

for wcallable in w:
    wcallable()                         # calls `w.callable1` and `w.callable2`
                                        # order not guaranteed

Wires callable objects

Each holds a __name__ attribute, like regular Python functions:

from wires import w

w.callable.__name__                     # returns 'callable'

Getting min_wirings and max_wirings, falling back to the Wires object settings:

from wires import Wires

w = Wires(min_wirings=1, max_wirings=1)

w.callable1.min_wirings                 # returns 1
w.callable1.max_wirings                 # returns 1

w.callable2.min_wirings = None
w.callable2.max_wirings = None
w.callable2.min_wirings                 # returns None
w.callable2.max_wirings                 # returns None

Getting returns and ignore_exceptions, falling back to the Wires object settings:

from wires import Wires

w = Wires(returns=True, ignore_exceptions=False)

w.callable1.returns                     # returns True
w.callable1.ignore_exceptions           # returns False

w.callable2.returns = False
w.callable2.ignore_exceptions = True
w.callable2.returns                     # returns False
w.callable2.ignore_exceptions           # returns True

Using len() to get number wirings:

from wires import w

def say_hi():
    print('Hello from wires!')

def say_bye():
    print('Bye, see you soon.')

w.callable.wire(say_hi)
w.callable.wire(say_bye)

len(w.callable)                         # returns 2

Using wirings to get the current list of wired callables and wire-time arguments:

from wires import w

def a_print(*args, **kw):
    print('args=%r kw=%r' % (args, kw))

w.callable.wire(a_print)
w.callable.wire(a_print, 42, 24)
w.callable.wire(a_print, a=42, b=24)

w.callable.wirings          # returns [
                            #   (<function a_print at ...>, (), {}),
                            #   (<function a_print at ...>, (42, 24), {}),
                            #   (<function a_print at ...>, (), {'a': 42, 'b': 24}),
                            # ]
[1]Per the Concepts section, wires callables are Wires object auto-created attributes.