Functional Hooks

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.

Usage on Class Instance methods

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"

c = Car()
c.hooks[Car.HOOK_ON_MOVE].tap(
   'log_metric_speed',
   lambda fn_args, fn_output, context: ...,
   before=False
)

How it works

When a method is decorated using the CreateHook() decorator, the wrapped function is marked. The class must extend the HookableMixin class. This is necessary because when the Car class is initialized, the hookable mixin goes through all the marked methods and constructs a FunctionalHook` for each of them.

These newly created hooks are stored on the instance.hooks attribute which is defined by the HookableMixin class

Arguments passed to callables from a FunctionalHook are predefined unlike Hook (inline hook). Refer to the documentation below to understand the arguments

Inheritance

HookableMixin allows you to inherit hooks from other classes that implement the HookableMixin

class MyClass(HookableMixin):

   def __init__(self):
      super(MyClass, self).__init__()
      self.car = Car()

      self.inherit_hooks(self.car)

my_class = MyClass()
my_class.hooks[Car.HOOK_ON_MOVE].tap(...)

Functional Hooks Documentation

FunctionalHook

class pytapable.FunctionalHook(interceptor=None)[source]

Functional hooks are created when CreateHook is used to decorate a class function. When a functional hook is tapped, a FunctionalTap is created. Look at FunctionalHook.call() to see how taps are called

call(fn_args, is_before, fn_output=None)[source]

Triggers all taps installed on this hook.

Taps receive predefined arguments (fn_args, fn_output, context)

fn_args = {
  "args": *args,
  "kwargs": **kwargs
}

fn_output = Optional[Any]

context = {
    'hook_type': FunctionalHook.HOOK_TYPE,
    'hook_type_label': self.label,
    'tap_name': tap.name,
    'is_before': is_before
}
Parameters
  • fn_args (dict) – The arguments the hooked function was called with. fn_args: { args: Tuple, kwargs: Dict }

  • is_before (bool) – True if the hook is being called after the hooked function has executed

  • fn_output (Optional[Any]) – The return value of the hooked function if any. None otherwise

tap(name, fn, before=True, after=True)[source]

Creates a FunctionalTap for this hook

Parameters
  • name (str) – Name of the tap

  • fn (Callable) – This will be called when the hook triggers

  • before (bool) – If true, this tap will be called before the hooked function executes

  • after (bool) – If true, this tap will be called after the hooked function executes

CreateHook

@pytapable.CreateHook(name, interceptor=None)[source]

Decorator used for creating Hooks on instance methods. It takes in a name and optionally an instance of a HookInterceptor.

Note

This decorator doesn’t actually create the hook. It just annotates the method. The hooks are created by the HookableMixin when the class is instantiated

HookableMixin

class pytapable.HookableMixin(*args, **kwargs)[source]

Mixin which instantiates all the decorated class methods. This is needed for decorated class methods

inherit_hooks(hookable_instance)[source]

Given an instance which extends the HookableMixin class, inherits all hooks from it to expose it on top level

References to the inherited hooks are added in the self.hooks dict

Parameters

hookable_instance (HookableMixin) – Instance from which to inherit hooks

create_hook_name

pytapable.create_hook_name(name='')[source]

Utility to create a unique hook name. Optionally takes in a name. The output string is the name prefixed with a UUID. This is useful to prevent collision in hook names when one class with hooks inherits hooks from another class

Example

>>> create_hook_name()
>>> '7087eefc-8e94-4f0a-b7d3-453062bb7a34'
>>> create_hook_name('my_hook')
>>> '7087eefc-8e94-4f0a-b7d3-453062bb7a34:my_hook'
Parameters

name (Optional[str]) – Name of the hook

create_hook_names

pytapable.create_hook_names(*names)[source]

Useful shortcut to create multiple unique hook names in one statement

Example

>>> HOOK_MY, HOOK_UNIQUE, HOOK_HOOK = create_hook_names('my', 'unique', 'hook')
>>> HOOK_ONE, HOOK_TWO, HOOK_THREE = create_hook_names(*range(3))
Parameters

*names – Argument of hook names

Returns

iterable which can be deconstructed across constants.

Return type

Iterable