How to watch for a variable change in python without dunder setattr or pdb

前端 未结 5 724
南方客
南方客 2020-12-05 05:44

There is large python project where one attribute of one class just have wrong value in some place.

It should be sqlalchemy.orm.attributes.InstrumentedAttribute, but

5条回答
  •  不知归路
    2020-12-05 06:29

    A simpler way to watch for an object's attribute change (which can also be a module-level variable or anything accessible with getattr) would be to leverage hunter library, a flexible code tracing toolkit. To detect state changes we need a predicate which can look like the following:

    import traceback
    
    
    class MutationWatcher:
    
        def __init__(self, target, attrs):
            self.target = target
            self.state = {k: getattr(target, k) for k in attrs}
    
        def __call__(self, event):
            result = False
            for k, v in self.state.items():
                current_value = getattr(self.target, k)
                if v != current_value:
                    result = True
                    self.state[k] = current_value
                    print('Value of attribute {} has chaned from {!r} to {!r}'.format(
                        k, v, current_value))
    
            if result:
                traceback.print_stack(event.frame)
    
            return result
    

    Then given a sample code:

    class TargetThatChangesWeirdly:
        attr_name = 1
    
    
    def some_nested_function_that_does_the_nasty_mutation(obj):
        obj.attr_name = 2
    
    
    def some_public_api(obj):
        some_nested_function_that_does_the_nasty_mutation(obj)
    

    We can instrument it with hunter like:

    # or any other entry point that calls the public API of interest
    if __name__ == '__main__':
        obj = TargetThatChangesWeirdly()
    
        import hunter
        watcher = MutationWatcher(obj, ['attr_name'])
        hunter.trace(watcher, stdlib=False, action=hunter.CodePrinter)
    
        some_public_api(obj)
    

    Running the module produces:

    Value of attribute attr_name has chaned from 1 to 2
      File "test.py", line 44, in 
        some_public_api(obj)
      File "test.py", line 10, in some_public_api
        some_nested_function_that_does_the_nasty_mutation(obj)
      File "test.py", line 6, in some_nested_function_that_does_the_nasty_mutation
        obj.attr_name = 2
                                     test.py:6     return        obj.attr_name = 2
                                                   ...       return value: None
    

    You can also use other actions that hunter supports. For instance, Debugger which breaks into pdb (debugger on an attribute change).

提交回复
热议问题