Skip to content

vidhu/PyTapable

Repository files navigation


Pirate
PyTapable

A Library to Implement Hookable Interfaces

Test Status pypi codecov python versions Downloads a Day License Maintained

🌽 Table of Contents

🍓 About The Project

PyTapable provides a set of utility to help you implement hookable interfaces in your classes. This opens up the posibility for solving a number of usecases such as

  • Providing plugable interfaces for your libraries and frameworks
  • Code seperation by functional and service domains

🌞 Getting Started

This project can be used in python 2.7, 3.5 and greater

$ pip install pytapable

Example

Inline hooks

We first create our hook called my_hook

from pytapable import Hook

my_hook = Hook()

As a consumer, we can tap into this hook by passing a name for our tap and a callback function

def my_callback(context, fn_kwargs):
    print(f"Hook says: {fn_kwargs['greeting']}")
    
my_hook.tap('My Tap Name', my_callback)

Our callback is executed when the hook.call(...) is executed. The callback receives whatever args were passed in the hook.call method in addition to a context dict

my_hook.call(greeting="Hi Callback")

Functional Hooks

Functional hooks are different from inline hooks in that the callback args are predefined.

from pytapable import CreateHook, HookableMixin, create_hook_name


class Car(HookableMixin):
    HOOK_ON_MOVE = create_hook_name('on_move')
    
    @CreateHook(name=HOOK_ON_MOVE)
    def move(self, speed=10):
        return f"Moving at {speed} Mph"
  • Start adding the HookableMixin to the Car Class. This is necessary to install hooks on class methods.
  • Decorate the Car.move method using the @CreateHook decorator. In the decorator, give it a name. As best practice we define the name as a Class Constant so consumers can easily refer to it.
  • The value of the hook can be anything. We use the create_hook_name(str) utility to generate a unique name. Generating a unique name is not required but becomes important when inheriting hooks from other Classes.
def callback(context, fn_kwargs, fn_output, is_before):
    kmph_speed = fn_kwargs['speed'] * 1.61
    print(f"The car is moving {kmph_speed} kmph")

c = Car()
c.hooks[Car.HOOK_ON_MOVE].tap('log_metric_speed', callback, before=False)

c.move(10)
  • Here we tap into the on_move hook which fires our callback after the c.move method has executed
  • The c.move() arguments are passed as fn_kargs to the callback and return value, if any, is passed as fn_output
    • All positional arguments are converted to named arguments
  • The context holds a is_before and is_after flag it signify if the callback was executed before or after c.move()

🍹 Documentation

Full documentation is available here https://pytapable.readthedocs.io/en/latest

😆 Contributing

Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

To tests on your changes locally, run:

$ pip install -r test_requirements.txt
$ tox .

This will run your changes on python-2 and python-3

Documentation for any new changes are a must. We use Sphinx and to build the documentation locally, run:

$ cd docs/
$ make html
    # or on windows
$ make.bat html

✌️ License

Distributed under the Apache License

Logo made by Smashicons

About

Hooks library for python inspired by webpack tapables

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages