如何使用装饰器记录来自特定模块的所有函数调用?
我想写一个装饰器进行日志记录。具体来说,我使用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)的总体思路用装饰器挖掘一个函数的内部,并确定潜在的许多函数调用的包成员关系。
星星点点已似乎有用的是:
- 使用
hasattr
- 使用反射(这样great blog post did to log a function's local variables upon exit)或 (但似乎并没有完全正确/也许很慢?)
- 使用
inspect
和/或发电机暂停执行的一些魔法
这些都不是这些想法非常充实 - 这是因为这些主题中的很多对我而言都很新颖。我会很感激任何方向 - 我试着提前询问,以便稍后我不会锁定询问一堆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
(在课程导入之后),因此您无需在其他任何位置替换它以代理它。
这应该工作:
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
对不起,我可以把它变成类似于我的第二个代码示例的东西吗?我对装饰者来说也很新颖,所以我不确定它到底在哪里 - 因为我的大脑认为'fn'是整个函数,并且很难理解如何测试'__module__'=='arcpy''所有功能*里面*'fn' – HFBrowning
请试试看。 –
它不工作 - 我想是因为'fn'不在模块'arcpy'中(我使用了一个我写的类似于'test_function()'的简单函数 - 如果它不明确,我想装饰我写的包含'arcpy'的函数)。这虽然给了我一些想法,但我可能会在'fn/test_function'中测试函数调用...... – HFBrowning
@HFBrowning - 为了方便必须牺牲一些东西;)只要你不用'sys.modules [“arcpy”]'替换它,你就可以完全控制代码的哪些部分会使用包装器,哪些不会(甚至可以在全局名称空间中使用它们,并在需要时使用它们),因此它与手动添加装饰器的形状和形式没有任何区别,只是它更方便。 – zwer
这工作令人难以置信井。非常感谢,我不需要花费两周的时间来了解发电机,反射和检查包。谢谢 :) – HFBrowning