如何使用装饰器记录来自特定模块的所有函数调用?

问题描述:

一些背景:如何使用装饰器记录来自特定模块的所有函数调用?

我想写一个装饰器进行日志记录。具体来说,我使用arcpy很多,从工具获取消息的方式是使用arcpy.GetMessages()。然而,这是一种恼人的功能,因为它只保存最近的消息,并且必须在每个工具之后调用。伪代码示例

import arcpy 
import logging 
log = logging.getLogger(__name__) 

def test_function(in_data): 
    out_data = 'C:/new_path/out_data' 
    arcpy.MakeFeatureLayer_management(in_data, out_data) 
    log.info(arcpy.GetMessages()) 

    arcpy.Delete_management(in_data) 
    log.info(arcpy.GetMessages()) 

    # If you did log.info(arcpy.GetMessages()) again here you'd just get 
    # the message from the Delete tool again 

它会好得多写一个装饰,可以识别任何时间arcpy函数被调用,并记录它。像:

def log_the_arcpy(fn): 
    @functools.wraps(fn) 
    def inner(*args, **kwargs): 
     result = fn(*args, **kwargs) 
     # Some magic happens here?! 
     if module_parent == arcpy: #module_parent is obviously fake, is there a real attribute? 
      log.info(arcpy.GetMessages()) 
     return result 
    return inner 

不过,我很困在两个地方:(1)如何识别单个功能的“ArcPy中性”(或其他包),和(2)的总体思路用装饰器挖掘一个函数的内部,并确定潜在的许多函数调用的包成员关系。

星星点点已似乎有用的是:

这些都不是这些想法非常充实 - 这是因为这些主题中的很多对我而言都很新颖。我会很感激任何方向 - 我试着提前询问,以便稍后我不会锁定询问一堆XY Problem questions

如果你要直接在arcpy调用方法,包装,模块将可能是最简单和最性能 - 影响途径:

# arcpy_proxy.py 
import arcpy as _arcpy 
import logging 

class _proxy(object): 

    def __getattr__(self, item): 
     ref = getattr(_arcpy, item) 
     if callable(ref): # wrap only function calls 
      return self._wrap(ref) 
     return ref 

    @classmethod 
    def _wrap(cls, func): 
     def inner(*args, **kwargs): 
      val = func(*args, **kwargs) 
      logging.info(_arcpy.GetMessages()) # log the messages 
      return val 
     return inner 

arcpy = _proxy() 

然后你就可以做from arcpy_proxy import arcpy作为直接替换。您甚至可以在主脚本中添加sys.modules["arcpy"] = arcpy(在课程导入之后),因此您无需在其他任何位置替换它以代理它。

+1

@HFBrowning - 为了方便必须牺牲一些东西;)只要你不用'sys.modules [“arcpy”]'替换它,你就可以完全控制代码的哪些部分会使用包装器,哪些不会(甚至可以在全局名称空间中使用它们,并在需要时使用它们),因此它与手动添加装饰器的形状和形式没有任何区别,只是它更方便。 – zwer

+0

这工作令人难以置信井。非常感谢,我不需要花费两周的时间来了解发电机,反射和检查包。谢谢 :) – HFBrowning

这应该工作:

if hasattr(fn, '__module__') and getattr(fn, '__module__') == 'arcpy': 
    log.info(arcpy.GetMessages()) 

全功能:

def log_the_arcpy(fn): 
    @functools.wraps(fn) 
    def inner(*args, **kwargs): 
     result = fn(*args, **kwargs) 
     if hasattr(fn, '__module__') and getattr(fn, '__module__') == 'arcpy': 
      log.info(arcpy.GetMessages()) 
     return result 
    return inner 
+0

对不起,我可以把它变成类似于我的第二个代码示例的东西吗?我对装饰者来说也很新颖,所以我不确定它到底在哪里 - 因为我的大脑认为'fn'是整个函数,并且很难理解如何测试'__module__'=='arcpy''所有功能*里面*'fn' – HFBrowning

+0

请试试看。 –

+0

它不工作 - 我想是因为'fn'不在模块'arcpy'中(我使用了一个我写的类似于'test_function()'的简单函数 - 如果它不明确,我想装饰我写的包含'arcpy'的函数)。这虽然给了我一些想法,但我可能会在'fn/test_function'中测试函数调用...... – HFBrowning