Python’s powerful hook function

Whatishook?

Ahookfunctioncanbeunderstoodasahook,anditsfunctionistohangsomethingupwhennecessary.Thespecificexplanationis:thehookfunctionistohookourownimplementedhookfunctiontothetargetmountpointatacertainmoment.

hookapplicationscenario(1)

Ibelieveyouarefamiliarwithhookfunctions.I’veseensimilardesignsinrequestsandmitmproxy.

requestsusehook

Forexample,statuscodeneedstobeprintedinrequests:

#requests_hooks.py
importrequests

r=requests.get("https://httpbin.org/get")
print(f"statusdoce:{r.status_code}")

Printingthestatuscode,thisactioncanbeencapsulatedintoafunctionandthenpassedtorequestsasahookfunction.

#requests_hooks.py
importrequests


defstatus_code(response,*args,**kwargs):
print(f"hookstatusdoce:{response.status_code}")


r=requests.get("https://httpbin.org/get",hooks={"response":status_code})

CodeDescription:

Encapsulatetheprintingstatuscodeintoastatus_code()function,andreceivethehookfunctionstatus_code()throughthehooksparameterintherequests.get()method.

Runresults:

>pythonrequests_hooks.py
hookstatusdoce:200

status_code()Asafunction,itcandomanythings,suchasfurtherjudgingthestatuscode,printingtheresponsedata,andevenencryptinganddecryptingthecorrespondingdata.

mitmproxy-hook

mitmproxyisaproxytool,whichwehavealsointroducedinourpreviousarticle.Duringthepacketcaptureprocess,youalsoneedtousehookstodosomeadditionalprocessingontherequestorresponse.

#anatomy.py
"""
Basicskeletonofamitmproxyaddon.

Runasfollows:mitmproxy-sanatomy.py
"""
importlogging


classCounter:
def__init__(self):
self.num=0

defrequest(self,flow):
self.num=self.num+1
logging.info("We'veseen%dflows"%self.num)


addons=[Counter()]

Runmitmproxy

>mitmproxy-sanatomy.py
Implementhookbyyourself

Whenitisnecessarytoimplementhooks,itiswhenafunction(class/method)cannotmeetallneedsbyitself,sohookscanbeusedtoprovidethepossibilityofexpandingitsowncapabilities.

Itisnotdifficulttoimplementhooks,lookattheexample:

importtime


classProgrammer(object):
"""programmer"""

def__init__(self,name,hook=None):
self.name=name
self.hooks_func=hook
self.now_date=time.strftime("%Y-%m-%d")

defget_to_eat(self):
print(f"{self.name}-{self.now_date}:eat.")

defgo_to_code(self):
print(f"{self.name}-{self.now_date}:code.")

defgo_to_sleep(self):
print(f"{self.name}-{self.now_date}:sleep.")

defeveryday(self):
#Threethingsthatprogrammersdoeveryday
self.get_to_eat()
self.go_to_code()
self.go_to_sleep()
#checktheregister_hook(hookedorunhooked)
#hooked
ifself.hooks_funcisnotNone:
self.hooks_func(self.name)


defplay_game(name):
now_date=time.strftime("%Y-%m-%d")
print(f"{name}-{now_date}:playgame.")


defshopping(name):
now_date=time.strftime("%Y-%m-%d")
print(f"{name}-{now_date}:shopping.")


if__name__=="__main__":
#hookispassedinasaparameter
tom=Programmer("Tom",hook=play_game)
jerry=Programmer("Jerry",hook=shopping)
spike=Programmer("Spike")
#today’sevents
tom.everyday()
jerry.everyday()
spike.everyday()

CodeDescription:

Intheaboveexample,theProgrammerclassimplementsthreefunctions:eat,code,andsleep.However,programmersarealsoordinarypeopleandcannotjusteat,code,andsleepeveryday,soregister_hook()providestheabilitytodootherthings.

So,takealookatwhatthethreeprotagonistsTom,Jerry,andSpikedidtoday!

Runresults:

Tom-2022-12-01:eat.
Tom-2022-12-01:code.
Tom-2022-12-01:sleep.
Tom-2022-12-01:playgame.
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.
hookapplicationscenario(two)

Ifyouunderstandhookas:definingafunctionandtheninsertingitasaparameterintoanotherclass/method.Obviously,thisisjustoneusage.Ithoughtaboutitagain.Thedebugtalk.pyfileofhttpRunner;theconftest.pyfileofpytestarealsohookfileswithspecialnames.Duringtheexecutionoftheprogram,thehookfunctionsinthesefilesarecalledtocompletesomespecialtasks.

Takepytestasanexample

└───project
├───conftest.py
└───test_sample.py
  • conftest.py
importpytest


@pytest.fixture()
defbaidu_url():
"""Definehookfunction"""
return"https://www.baidu.com"

  • test_sample.py
importwebbrowser


deftest_open_url(baidu_url):
#Callbaidu_urlhookfunction
#Callthebrowsertoaccessbaidu_url
webbrowser.open_new(baidu_url)

Thetwofilesseemtohavenodirectcallingrelationship.Whenexecutingthetest_sample.pyfile,youcanindirectlycallbaidu_url()conftest.pyfile./code>Hookfunction.

Executetest

>pytest-qtest_sample.py
Realizedynamiccallinghook

Next,let’strytocreateasimilarfunction.

└───project
├───run_conf.py
├───loader.py
└───run.py
  • run_conf.py
defbaidu_url():
"""Definehookfunction"""
name="https://www.baidu.com"
returnname

Similartotheconftest.pyfile,thehookfunctionisimplementedinthisfile.

  • loader.py
importos
importinspect
importimportlib


defloader(name):
"""
Dynamicallyexecutehookfunctions
"""
#Directoryofthecalledfile
stack_t=inspect.stack()
ins=inspect.getframeinfo(stack_t[1][0])
file_dir=os.path.dirname(os.path.abspath(ins.filename))
#*_conf.pyfileunderthecalledfiledirectory
all_hook_files=list(filter(lambdax:x.endswith("_conf.py"),os.listdir(file_dir)))
all_hook_module=list(map(lambdax:x.replace(".py",""),all_hook_files))

#Dynamicallyload*_config.py
hooks=[]
formodule_nameinall_hook_module:
hooks.append(importlib.import_module(module_name))

#Basedonthepassednamefunctionname,searchandexecuteitfromthe*_conf.pyfile.
forper_hookinhooks:
#Dynamicallyexecuteprocessfunction
func=getattr(per_hook,name)
returnfun()

Thisthingismorecomplicated.Itsfunctionistothrowitafunctionname.Itcanfindthecorrespondingfunctionnameinthe*_conf.pyfileandreturnthefunctionexecutionresult.

Theloader()functionisauniversalthing,youcanputitanywhereanduseit.

  • run.py
importwebbrowser
fromloaderimportloader


deftest_open_url():
#Callbaidu_urlhookfunction
#Callthebrowsertovisitbaidu_url
url=loader("baidu_url")
webbrowser.open_new(url)


if__name__=='__main__':
test_open_url()

Executethebaidu_urlhookfunctionthroughtheloader()functionandgettheurl.

Notethatwedonotneedtoimportthemodulelikethetraditionalwayfromrun_confimportbaidu_url,weonlyneedtoknowthenameofthehookfunction.

Theimplementationhereisnotaselegantaspytest,butitisclose.

Finally,Iwouldliketothankeveryonewhoreadmyarticlecarefully.Reciprocityisalwaysnecessary.Althoughitisnotaveryvaluablething,ifyoucanuseit,youcantakeitdirectly:

Thisinformationshouldbethemostcomprehensiveandcompletepreparationwarehousefor[softwaretesting]friends.Thiswarehousehasalsoaccompaniedtensofthousandsoftestengineersthroughthemostdifficultjourney.Ihopeitcanalsohelpyou!

Theknowledgepointsofthearticlematchtheofficialknowledgefiles,andyoucanfurtherlearnrelevantknowledge.PythonentryskilltreeBasicgrammarFunction386776peoplearelearningthesystem