Package: assignhooks
Descriptionassignhooksallows you to instrument your python code so that assignments to a variable can execute an action before and/or after the fact. For instance:classSampleClass():name=''def__assignpre__(self,lhs_name,rhs_name,rhs):print('PRE: assigning%s=%s'%(lhs_name,rhs_name))# modify rhs if needed before assignmentrhs.name=lhs_namereturnrhsdef__assignpost__(self,lhs_name,rhs_name):print('POST: lhs',self)print('POST: lhs_name',lhs_name)print('POST: rhs_name',rhs_name)print('POST: assigning%s=%s'%(lhs_name,rhs_name))self.name=lhs_nameb=SampleClass()c=bThe first assignment would generate a call to__assignpost__asbstill has no contents so there is no information on its class and hence no way to find out if it has an__assignpre__method.__assignpost__allowsbto find out its name in this case. In the example above b contains aSampleClassinstance whose name is initialized tob(the variable name)In the second assignment, b is being assigned on the right-hand-side. As b is already existing we can check if its class provides an__assignpre__implementation.bknows is being assigned before the fact so we can used__assignpre__to check on the assignment or to provide the suitable value to be assigned (the return from__assignpre__method)Applications:Can be used to add automatically names to created objects, for debug for instance:classMyClass:def__init__(self,val,name=None):self.val=valself.name=namedef__assignpost__(self,lhs_name,rhs_name):ifself.nameisNone:self.name=lhs_name# will assign x.name as 'x' automaticallyx=MyClass(val=4)assertx.name=='x'Can be used to control what gets assigned when a specific instance of a class is assignedHow to use it:Checkouttest.pyandtestmod.pyunderexamplesdirectory.test.pycontains an example of how to instrument and usetestmod.pymodule (the module being object of instrumentation).Automatic patchSuppose that there is atestmod.pythat contains a module to instrument. Importing ‘assignhooks’ gives access to ‘assignhooks.instrument.start()’. It should be called before the imports you want to instrument. To stop this behavior invokeassignhooks.instrument.stop()Assumingtestmod.pycontains:<testmod.py>classT():def__assignpre__(self,lhs_name,rhs_name,rhs):print('%shas been copied to%s'%(rhs_name,lhs_name))returnrhsb=T()c=bAnd<test.py>importassignhooksassignhooks.instrument.start()# instrument from now onimporttestmodassignhooks.instrument.stop()# stop instrumentingIf executed:$ python3 ./test.pywill produceb has been copied to cmanually patchfromassignhooks.patchimportpatch_moduleimporttestmodpatch_module(test)InstallJust do:pip3 install assignhooksHow does it workInternally assignhooks.magic replaces __import__ by a new version that after the import patches the module AST tree. It performs the following transformations:x=T()gets replaced by:ifTrue:x=T()ifhasattr(x,'__assignpost__'):x.__assignpost__('x','T')andx=ygets replaced by:ifTrue:ifhasattr(y,'__assignpre__'):x=y.__assignpre__('x','y',y)else:x=yifhasattr(x,'__assignpost__'):x.__assignpost__('x','y')NOTE: thatif True:is only used to group the statements while doing node replacements in the AST.NotesTested withPython 3.8.7on MacOSCreditsBased on original code fromassignmodule fromryankung. Mainly adapted for my needs to handle object creation. Changes include:Original had an__assign__overload only valid on existing objects (like__assignpre__). Added__assignpost__to handle object creation casesRenamed__assign__to__assignpre__and added extra parametersKnown IssuesWon’t work underREPLMay slow down import operation. The effect in run-time is the replacement of raw assignments with conditional + assignment + hookMay fail when patching standard modules