I wrote this prototype after reading the Wikipedia article on hooking. I didn't bother to read any of the code examples listed there because, well, I just didn't. I don't have a good excuse.
The documentation in this file should tell you enough about its purpose and intended usage. I'm looking for a confirmation of whether I even understand what hooks are and how they should be used. I'm also curious about any sort of preferred method of implementation or any way that I could improve these two objects (Hook
and OnHook
).
"""Implement hooks with a simple interface using decorators.
The Hook class creates a function that can be placed in code as an
anchoring point for as yet undetermined functionality, while the
OnHook function creates the interface to that functionality.
To create a hook, use the @Hook decorator on an empty function of
your chosen name. The function's __call__ method will be generated
by the decorator. Then, create a function that accepts a function
as an argument and adds it to the targets callback by using @OnHook.
OnHook expects the target hook as an argument.
Example:
@Hook # decorator, no args
def my_hook(): pass # empty func, no params
@OnHook(my_hook) # decorator, hook as arg
def on_my_hook(func): pass # empty func, func as param
To add a callback to the new hook, use:
on_my_hook(my_callback)
When the hook is executed, your callback will be executed along with
any other callbacks that have been registered.
Written 2014-02-02 by Jack Stout.
"""
class Hook:
"""Decorator to create a hook."""
def __init__(self, func):
self.callbacks = []
def __call__(self):
for callback in self.callbacks:
callback()
def OnHook(target):
"""Decorator to create an interface to a hook.
Requires a target hook as only argument.
"""
def decorator(func):
def add_callback(func):
target.callbacks.append(func)
return add_callback
return decorator
# Here I've created two hooks with interfaces which would be used
# immediately before and after a hypothetical initiative() function.
@Hook
def pre_initiative(): pass
@OnHook(pre_initiative)
def on_pre_initiative(func): pass
@Hook
def post_initiative(): pass
@OnHook(post_initiative)
def on_post_initiative(func): pass
# Two dummy functions are created and are added to the hooks' callback lists.
def dummy_func_1():
print("Inside pre_initiative!")
on_pre_initiative(dummy_func_1)
def dummy_func_2():
print("Inside post_initiative!")
on_post_initiative(dummy_func_2)
# These print functions reveal what has been registered in the hook.
print(pre_initiative.callbacks)
# This function call is what we would see in production code.
pre_initiative()
print(post_initiative.callbacks)
post_initiative()
Less important but related:
This is a prototype but I'm working on a game project that could benefit from this method of quickly attaching function pointers to upcoming state changes. While working on this file, I was imagining the first phase in a tabletop combat encounter where the players and game master roll initiatives and determine the combat order. If a character were to gain a power that allowed them to adjust their initiative roll after everyone has rolled, it would require only a call to on_post_initiative(power_name)
. When initiative()
completes, post_initiative()
would fire and execute power_name()
.
Before getting any feedback, if I were to revisit this code I would add the ability to include arguments to be used with the callback, i.e. on_post_initiative(power_name, character_name)
.