PyTapable Documentation

Test Status PyPi Code Coverage Python Versions Maintainability and Code Quality Downloads a day License Maintained

Installation

$ pip install pytapable

Introduction

PyTapable provides a way to attach hooks into your application. Its a way to implement event listeners with side effects. This is particularly useful to maintain service boundaries in your application, allow plugable in your libraries which you users can extend upon etc

Lets first clarify the terminologies used in this library

Hook

A Hook is an object which maintains a list of taps that have been installed/registered with it

Tap

A Tap is an object which holds a reference to the function which is to be run when a hook is executed.

tapping

Tapping into a hook is the act of registering a callable with a hook

callbacks

Consumer defined function/callable which is executed in response to a hook being triggered

Type of Hooks

There are two types of hooks provided in this library

  • FunctionalHook: Functional hooks are hooks which wrap a function. They fire before and after the execution of a function automatically. They are created using decorators on the function. See Functional Hooks

  • InlineHook: Inline hooks are created and triggered manually. They are used in the body of functions and modules See Inline Hooks

The parameters your callbacks are called with differ based on which hooks there were called from. Callbacks from a functional hook contain the function’s arguments and return value (if available) whereas callbacks from an inline hook contain parameters defined by the caller

Context

A context dict is also passed to the callback function which contain various metadata. This is covered in more detail in the API doc for FunctionalHook and Hook

Quick Start

Here’s an example on how to use InlineHooks and FunctionalHooks

InlineHooks

from pytapable import Hook

# 1. Create our hook
my_hook = Hook()

# 2. Define a function to execute when hook triggers
def my_callback(context, fn_kwargs):
   print(f"Hook says: {fn_kwargs['greeting']}")

# 3. Tap into our hook
my_hook.tap('My Tap Name', my_callback)

# 4. Trigger our hook
my_hook.call(greeting="Hi Callback")

>>> "Hook says: Hi Callback"

FunctionalHooks

Lets define out class with a hookable instance method

from pytapable import CreateHook, HookableMixin, create_hook_name

# 1. Class extends `HookableMixin` to initialize hooks on instance
class Car(HookableMixin):
   HOOK_ON_MOVE = create_hook_name('on_move')

   # 2. Mark this method as hookable
   @CreateHook(name=HOOK_ON_MOVE)
   def move(self, speed=10):
      return f"Moving at {speed}Mph"

and then tap into the hook

def log_metric_speed(context, fn_kwargs, fn_output):
   kmph_speed = fn_kwargs['speed'] * 1.61
   print(f"The car is moving at {kmph_speed} kmph")

c = Car()

# 3. Tap into our hook. before=False means callback will
#    execute after hooked function returns
c.hooks[Car.HOOK_ON_MOVE].tap(
   'log_metric_speed',
   log_metric_speed,
   before=False
)

# 4. Hook is automatically triggered
c.move(speed=10)

# Positional args are converted to named arguments
c.move(10)

>>> "The car is moving at 16.1 kmph"

Indices and tables