SoFunction
Updated on 2024-11-18

Python powerful hook function use and application scenarios

What's a hook?

Hook function (hook function), can be understood as a hook, the role is to have the need to hang a thing up. Specifically explained: hook function is our own implementation of the hook function at a certain time hooked to the target mount point.

Hook application scenarios (I)

I'm sure you're no stranger to hook functions. I've seen similar designs in requests and mitmproxy.

requests Using hooks

For example, requests requires a status code to be printed:

# requests_hooks.py
import requests
r = ("/get")
print(f"status doce: {r.status_code}")

Printing the status code is an action that we can encapsulate in a function and pass as a hook function for requests to use.

# requests_hooks.py
import requests
def status_code(response, *args, **kwargs):
    print(f"hook status doce: {response.status_code}")
r = ("/get", hooks={"response": status_code})

Code Description:

Encapsulate the print status code into a status_code() function that receives the hook function status_code() in the () method via the hooks argument.

Run results:

> python requests_hooks.py
hook status doce: 200

status_code() As a function, you can do a lot of things, for example, further determine the status code, print the response data, and even do encryption and decryption and other processing of the corresponding data.

Hooks in mitmproxy

mitmproxy is a proxy tool, which we have also introduced in this previous article. In the process of grabbing packets, you also need to use hooks to do some additional processing of request or response.

# 
"""
Basic skeleton of a mitmproxy addon.
Run as follows: mitmproxy -s 
"""
import logging
class Counter:
    def __init__(self):
         = 0
    def request(self, flow):
         =  + 1
        ("We've seen %d flows" % )
addons = [Counter()]

Running mitmproxy

> mitmproxy -s

Implement the hook yourself

What is the case when you need to implement a hook is that a function (class/method) cannot satisfy all the requirements by itself, so you can provide the possibility to extend your own capabilities by using a hook.

Implementing a hook isn't difficult, see the example:

import time
class Programmer(object):
    """Programmer."""
    def __init__(self, name, hook=None):
         = name
        self.hooks_func = hook
        self.now_date = ("%Y-%m-%d")
    def get_to_eat(self):
        print(f"{} - {self.now_date}: eat.")
    def go_to_code(self):
        print(f"{} - {self.now_date}: code.")
    def go_to_sleep(self):
        print(f"{} - {self.now_date}: sleep.")
    def everyday(self):
        # Three things programmers do every day
        self.get_to_eat()
        self.go_to_code()
        self.go_to_sleep()
        # check the register_hook(hooked or unhooked)
        # hooked
        if self.hooks_func is not None:
            self.hooks_func()
def play_game(name):
    now_date = ("%Y-%m-%d")
    print(f"{name} - {now_date}: play game.")
def shopping(name):
    now_date = ("%Y-%m-%d")
    print(f"{name} - {now_date}: shopping.")
if __name__ == "__main__":
    # hook passed as an argument
    tom = Programmer("Tom", hook=play_game)
    jerry = Programmer("Jerry", hook=shopping)
    spike = Programmer("Spike")
    # The thing of the day
    ()
    ()
    ()

Code Description.

In the above example the Programmer class implements three functions: eat, code, and sleep, but programmers are ordinary people and can't just eat, code, and sleep every day, so the ability to do something else is provided through register_hook().

So, let's see what the three main characters, Tom, Jerry, and Spike, have done today!

Run results:

Tom - 2022-12-01: eat.
Tom - 2022-12-01: code.
Tom - 2022-12-01: sleep.
Tom - 2022-12-01: play game.
Jerry - 2022-12-01: eat.
Jerry - 2022-12-01: code.
Jerry - 2022-12-01: sleep.
Jerry - 2022-12-01: shopping.
Spike - 2022-12-01: eat.
Spike - 2022-12-01: code.
Spike - 2022-12-01: sleep.

Hook application scenarios (II)

If you understand hook as: defining a function and stuffing it as an argument to another class/method. Obviously, this is only one usage. I've rethought it a bit. httpRunner's file; pytest's files, which are themselves hook files with special names. During execution, the program calls the hook functions in these files to accomplish some special tasks.

Take pytest as an example

└───project
    ├───
    └───test_sample.py
import pytest
@()
def baidu_url():
    """Defining Hook Functions"""
    return ""
  • test_sample.py
import webbrowser
def test_open_url(baidu_url):
    # call baidu_url hook function
    # call the browser to access baidu_url
    webbrowser.open_new(baidu_url)

The two files do not appear to have a direct calling relationship, and when executing thetest_sample.py file, you can indirectly call the in the filebaidu_url()Hook function.

execute a test

> pytest -q test_sample.py

Implementing a dynamic call to hook

Next, let's try to make a similar function out of it.

└───project
    ├───run_conf.py
    ├───
    └───
  • run_conf.py
def baidu_url():
    """Defining Hook Functions"""
    name = ""
    return name

together with file is similar, and the hook function is implemented in this file.

import os
import inspect
import importlib
def loader(name):
    """
    Dynamic execution of hook functions
    """
    # Directory of the called file
    stack_t = ()
    ins = (stack_t[1][0])
    file_dir = (())
    # The *_conf.py file in the directory of the called file.
    all_hook_files = list(filter(lambda x: ("_conf.py"), (file_dir)))
    all_hook_module = list(map(lambda x: (".py", ""), all_hook_files))
    # Dynamically load *_config.py
    hooks = []
    for module_name in all_hook_module:
        (importlib.import_module(module_name))
    # Look up and execute the name function from the *_conf.py file based on the name function name passed to it.
    for per_hook in hooks:
        # Execute process functions dynamically
        func = getattr(per_hook, name)
        return func()

It's a little more complicated. What he does is he throws in a function name, and he can pass it through the*_conf.pyfile to find the corresponding function name and return the result of the function execution.

loader() A function is a generic thing that you can put to use anywhere.

import webbrowser
from loader import loader
def test_open_url():
    # call baidu_url hook function
    # call the browser to access baidu_url
    url = loader("baidu_url")
    webbrowser.open_new(url)
if __name__ == '__main__':
    test_open_url()

pass (a bill or inspection etc)loader() function executionbaidu_url hook function and get the url.

Note that we don't need the traditional way offrom run_conf import baidu_url Import the module, as long as you know the name of the hook function.

The implementation here is not as elegant as pytest, but it's close.

to this article on the use of python powerful hook function and application scenarios are introduced to this article, more related to the use of python hook function content, please search for my previous articles or continue to browse the following related articles I hope that you will support me more in the future!