python强大的hook函数

什么是hook?

钩子函数(hook function),可以理解是一个挂钩,作用是有需要的时候挂一个东西上去。具体的解释是:钩子函数是把我们自己实现的hook函数在某一时刻挂接到目标挂载点上。

hook应用场景(一)

相信你对钩子函数并不陌生。我在 requests 和 mitmproxy 都有看到类似的设计。

requests 使用hook

例如 requests 中需要打印状态码:

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

打印状态码,这个动作,我们可以封装到一个函数里,然后作为钩子函数传给requests 使用。

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

代码说明:

把打印状态码封装到一个status_code() 函数中,在requests.get() 方法中通过hooks 参数接收钩子函数status_code()。

运行结果:

> python requests_hooks.py
hook status doce: 200

status_code() 作为一个函数,可以做的事情很多,比如,进一步判断状态码,打印响应的数据,甚至对相应的数据做加解密等处理。

mitmproxy 中的hook

mitmproxy是一个代理工具,我们这之前的文章也有做过介绍。在抓包的过程中,同样需要用到 hooks 去对request请求或response响应做一些额外的处理。

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

运行mitmproxy

> mitmproxy -s anatomy.py

自己实现hook

什么情况下需要实现hook,就是一个功能(类/方法)自身无法满足所有需求,那么可以通过hook 就提供扩展自身能力的可能。

实现hook 并不难,看例子:

import timeclass Programmer(object):"""程序员"""def __init__(self, name, hook=None):self.name = nameself.hooks_func = hookself.now_date = time.strftime("%Y-%m-%d")def get_to_eat(self):print(f"{self.name} - {self.now_date}: eat.")def go_to_code(self):print(f"{self.name} - {self.now_date}: code.")def go_to_sleep(self):print(f"{self.name} - {self.now_date}: sleep.")def everyday(self):# 程序员日常三件事self.get_to_eat()self.go_to_code()self.go_to_sleep()# check the register_hook(hooked or unhooked)# hookedif self.hooks_func is not None:self.hooks_func(self.name)def play_game(name):now_date = time.strftime("%Y-%m-%d")print(f"{name} - {now_date}: play game.")def shopping(name):now_date = time.strftime("%Y-%m-%d")print(f"{name} - {now_date}: shopping.")if __name__ == "__main__":# hook 作为参数传入tom = Programmer("Tom", hook=play_game)jerry = Programmer("Jerry", hook=shopping)spike = Programmer("Spike")# 今日事情tom.everyday()jerry.everyday()spike.everyday()

代码说明:

在上面的例子中Programmer类实现三个功能:eat、code、sleep,但程序员也是普通人,不能每天都只吃饭、编码、睡觉,于是通过register_hook() 提供了做别的事情的能力。

那么,看看Tom、Jerry、Spike三位主角,今天都干了什么吧!

运行结果:

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应用场景(二)

如果把hook理解为:定义一个函数,然后作为参数塞到另一个类/方法里。 显然,这只是一种用法。我重新想了一下。httpRunner 的 debugtalk.py 文件; pytest 的 conftest.py 文件,他们本身也是拥有特殊名字的 hook文件。程序在执行的过程中,调用这些文件中的钩子函数完成一些特殊的任务。

以pytest为例子

└───project├───conftest.py└───test_sample.py
  • conftest.py
import pytest@pytest.fixture()
def baidu_url():"""定义钩子函数"""return "https://www.baidu.com"
  • test_sample.py
import webbrowserdef test_open_url(baidu_url):# 调用 baidu_url 钩子函数# 调用 浏览器 访问 baidu_urlwebbrowser.open_new(baidu_url)

两个文件看似没有直接的调用关系,在执行 test_sample.py 文件时,可以间接的调用 conftest.py 文件中的baidu_url()钩子函数。

执行测试

> pytest -q test_sample.py

实现动态调用hook

接下来,我们来试试做个类似的功能出来。

└───project├───run_conf.py├───loader.py└───run.py
  • run_conf.py

def baidu_url():"""定义钩子函数"""name = "https://www.baidu.com"return name

与 conftest.py 文件类似,在这个文件中实现钩子函数。

  • loader.py
import os
import inspect
import importlibdef loader(name):"""动态执行 hook 函数"""# 被调用文件的目录stack_t = inspect.stack()ins = inspect.getframeinfo(stack_t[1][0])file_dir = os.path.dirname(os.path.abspath(ins.filename))# 被调用文件目录下面 *_conf.py 文件all_hook_files = list(filter(lambda x: x.endswith("_conf.py"), os.listdir(file_dir)))all_hook_module = list(map(lambda x: x.replace(".py", ""), all_hook_files))# 动态加载 *_config.pyhooks = []for module_name in all_hook_module:hooks.append(importlib.import_module(module_name))# 根据传过来的 name 函数名,从 *_conf.py 文件查找并执行。for per_hook in hooks:# 动态执行 process 函数func = getattr(per_hook, name)return func()

这个东西就比较复杂了,他的作用就是丢给他一个的函数名, 他能通过*_conf.py文件中查找对应的函数名,并将函数执行结果返回。

loader() 函数是一个通用的东西,你可以把他放到任何位置使用。

  • run.py
import webbrowser
from loader import loaderdef test_open_url():# 调用 baidu_url 钩子函数# 调用 浏览器 访问 baidu_urlurl = loader("baidu_url")webbrowser.open_new(url)if __name__ == '__main__':test_open_url()

通过loader() 函数执行baidu_url 钩子函数,并拿到 url。

注意,我们不需要像传统的方式一样from run_conf import baidu_url 导入模块,只要知道钩子函数的名字即可。

这里的实现并没有 pytest 那么优雅,但也比较接近了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/422198.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SCT9431FSAR:3.8V-36V输入,3A同步降压DCDC转换器,具有EMI降低功能;替代AP63356/7

• 3.8V-36V宽输入电压 • 高达3A的连续输出负载电流 • EMI降低 o 专有的门设计,用于交换节点无振铃 o 扩频(FSS) • 轻负载条件下具有22uA静态电流的脉冲跳过模式(PSM) o 在1mA轻负载下效率高达79% o 在10mA轻负载…

负载均衡-Ribbon-自定义负载均衡算法

1.Ribbon 是什么 SpringCloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间服务处连接在一起Ribbon的客户端组件提供一系列完整的配置项,如:连接超时、重试等。简…

AlmaLinux 8.9 安装图解

风险告知 本人及本篇博文不为任何人及任何行为的任何风险承担责任,图解仅供参考,请悉知!本次安装图解是在一个全新的演示环境下进行的,演示环境中没有任何有价值的数据,但这并不代表摆在你面前的环境也是如此。生产环境…

「优选算法刷题」:在排序数组中查找元素的第一个和最后个位置

一、题目 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。 示例 1&a…

Go 的 Http 请求系统指南

文章目录 快速体验请求方法URL参数响应信息BodyStatusCodeHeaderEncoding 图片下载定制请求头复杂的POST请求表单提交提交文件 CookieClient 上设置 Cookie请求上设置 Cookie 重定向和请求历史超时设置总超时连接超时读取超时 请求代理错误处理总结 前几天在 “知乎想法” 谈到…

【英文干货】【Word_Search】找单词游戏(第1天)

本期主题:Mindfulness(意识力) 本期单词: Awareness 意识 Breathing 呼吸 Calm 平静的 De-Stress 减压 Feelings 感受,情感 Inspection 调查 Meditation 冥想 Peace 和平 Quiet 安静的 Recollection 回忆 R…

2.【SpringBoot3】用户模块接口开发

文章目录 开发模式和环境搭建开发模式环境搭建 1. 用户注册1.1 注册接口基本代码编写1.2 注册接口参数校验 2. 用户登录2.1 登录接口基本代码编写2.2 登录认证2.2.1 登录认证引入2.2.2 JWT 简介2.2.3 登录功能集成 JWT2.2.4 拦截器 3. 获取用户详细信息3.1 获取用户详细信息基本…

SDCMS靶场通过

考察核心:MIME类型检测文件内容敏感语句检测 这个挺搞的,一开始一直以为检查文件后缀名的,每次上传都失败,上传的多了才发现某些后缀名改成php也可通过,png图片文件只把后缀名改成php也可以通过,之前不成功…

代码随想录算法训练营29期|day27 任务以及具体安排

39. 组合总和// 剪枝优化 class Solution {public List<List<Integer>> combinationSum(int[] candidates, int target) {List<List<Integer>> res new ArrayList<>();Arrays.sort(candidates); // 先进行排序backtracking(res, new ArrayList&…

数字湖南劲风起:纷享销客当选2023年湖南省数字化十大优秀服务商

1月19日&#xff0c;湖南省数字经济促进会主办的长江数字经济发展大会暨第四届湖南省数字经济论坛圆满举行&#xff0c;纷享销客凭借过硬的产品实力、优质的服务水平及行业口碑&#xff0c;当选【2023年湖南省数字化十大优秀服务商】&#xff01; “一江一体同风起&#xff0c;…

flutter 实现定时滚动的公告栏的两种不错方式

相同的部分 自定义一个类继承StatefulWidget 所有公告信息存放在list里 第一种 scrollControllerAnimatedContainer 逻辑如下 我们可以发现启动了一个timer计时器计时5秒&#xff0c;hasClients检查其目标对象&#xff08;我们用的是listview&#xff09;是否被渲染&#x…

捆扎机怎么调整松紧度

捆扎机松紧力度可以通过以下方法调节&#xff1a; 1. 调节束紧力&#xff1a;捆扎机上通常有专门的调节束紧力的旋钮。顺时针方向转动螺帽&#xff0c;对弹簧的压力就会减小&#xff0c;打包机就会越松&#xff1b;逆时针调整&#xff0c;打包机就会越紧。正常情况下&#xff0…