callmonitor一个简单的监视和日志功能调用的工具pip install callmonitor或克隆此回购和:
python setup.py install它易于使用,只需用@intercept Decorator装饰任何功能即可。例如:
from callmonitor import intercept
@ intercept
def test_fn_2 ( x , y = 2 , z = 3 ):
pass这将将输入( args , kwargs和argspec )以及CALL数据库( callmonitor.DB )保存为: call-monitor/test_fn_2/<invocation count> 。
callmonitor不会覆盖输出如果call-monitor文件夹已经存在(例如,以前的运行),则创建了新文件夹call-monitor-1或call-monitor-2 ,依此类推。有关如何保存此数据的更多详细信息,请参见Data Structure的部分。
为了避免从编写到同一位置的不同过程, callmonitor将-tid=<N>附加到root( call-monitor )文件夹。当前callmonitor框外支持mpi4py :如果mpi4py.MPI.COMM_WORLD.Get_rank() > 1 , callmonitor自动假设其正在运行IM多线程模式,并附加-tid=<Get_rank()>向输出。如果您的程序为多线程(例如concurrent.Futures )多线程,则您需要使用callmonitor.Settings告诉callmonitor您的线程ID:
from callmonitor import Settings
settings = Settings ()
settings . enable_multi_threading ( THREAD_ID )在第一次intercept拦截之前(第一次需要在磁盘上创建数据库,是在callmonitor.CONTEXT.new callmonitor.Settings读取callmonitor.Settings时候。
Handler程序有时, pickle不会用保存功能输入来削减它 - 例如。当我们需要保存自己的精美数据类型时。 callmonitor提供了一种构建您的向下参数处理程序并注册到Global callmonitor.REGISTRY方法。每次处理函数输入时都会查询注册表,因此,如果您构建自己的ArgHandler并使用G callmonitor.REGISTRY.add添加它们,它将从该点开始处理关联数据类型的任何参数。例如, numpy提供了自己的save / load功能。我们已经构建(并注册)一个Numpy Arggument处理程序这样:
import numpy as np
from os . path import join
from callmonitor import Handler , REGISTRY
class NPHandler ( Handler ):
def load ( self , path ):
self . data = np . load ( join ( path , f"arg_ { self . target } .npy" ))
def save ( self , path ):
np . save ( join ( path , f"arg_ { self . target } .npy" ), self . data )
@ classmethod
def accumulator_done ( cls ):
pass
REGISTRY . add ( np . ndarray , NPHandler ) (请记住,在需要此特定Handler的@intercept的第一次调用之前,需要调用callmonitor.REGISTRY.add )。自定义处理程序需要继承callmonitor.Handler类并定义save , load和accumulator_done (最后一个是@classmethod )。
callmonitor.load(<path>)将在<path>上加载数据库(请参阅Data Structure的部分)。例如:
from callmonitor import load
db = load ( "call-monitor" )现在,我们可以使用DB.get从数据库中获取单个函数调用数据:
args , kwargs = db . get ( "function_name" , invocation_count ) (这也将自动加载.npy文件和任何自定义处理程序 - 请记住在执行db.get之前在callmonitor.REGISTRY中注册这些文件)
请记住: invocation_count从1开始。因此,要访问第一个调用test_np_1 ,请运行:
In [ 4 ]: db . get ( "test_np_1" , 1 )
Out [ 4 ]: ([ 10 , array ([ 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ])], {})callmonitor互动我们尝试启用以下以下用户类别的顶级摘要:
REGISTRYDBDB.get_args ,并通过__str__和__repr__函数进行Args 。例如, callmonitor.REGISTRY显示了配置了哪些数据类型/处理程序对: In [ 2 ]: callmonitor . REGISTRY
Out [ 2 ]:
{
< class 'numpy.ndarray' > : < class 'callmonitor.handler.NPHandler' >
}同样, DB对象显示了调用函数摘要以及一次。
In [ 3 ]: db = callmonitor . load ( "call-monitor" )
In [ 4 ]: db
Out [ 4 ]:
{
Locked : True
test_np_1 : {
calls : 2
args : [ 'x' , 'n' ]
defaults : None
}
}Args容器挑选args , kwargs和argspec.defaults可能非常乏味 - 尤其是如果您想找出特定参数的价值。因此, callmonitor.DB提供了一个添加getter get_args返回Args对象。 callmonitor.Args是容器类,可将每个输入参数按名称作为属性存储。例如:
In [ 3 ]: args = db . get_args ( "test_fn_1" , 1 )
In [ 4 ]: args
Out [ 4 ]: dict_keys ([ 'x' , 'y' , 'z' ])
In [ 5 ]: args . x
Out [ 5 ]: 1注意: callmonitor.Args构造函数将填充FullArgSpec默认值中不在args和kwargs中的任何参数。如果您只想重新创建原始功能,请调用args和callmonitor.DB.get返回的夸kwargs 。
虽然从技术上讲不是数据库,但让我们将callmonitor生成的目录称为数据库,因为缺乏更好的术语。每个数据库都由一个db.pkl文件(包含元数据)以及每个函数的文件夹(枚举每个函数调用)。例如:
call-monitor
├── db.pkl
├── test_fn_1
│ ├── 1
│ │ └── input_descriptor.pkl
│ └── 2
│ └── input_descriptor.pkl
└── test_fn_2
└── 1
└── input_descriptor.pkl
特别注意numpy输入 - 这些称为arg_<label>.npy ,其中<label>是输入参数的索引,或KWARGS的kw 。例如:
call-monitor
├── db.pkl
└── test_np_1
├── 1
│ ├── arg_1.npy
│ └── input_descriptor.pkl
└── 2
├── arg_n.npy
└── input_descriptor.pkl
全面考虑将所有呼叫数据保存在单个数据结构中 - 甚至可能是一个真实的数据库;) - 但是在大规模上有效地进行此操作并不容易,并且可能会使此软件包变得笨拙。未来版本将包括将多个小功能调用融合到单个累加器对象中以避免大量小文件的能力。
版本0.3.0 Brign为callmonitor提供了许多增强功能。因此,我们再也无法启用本地向后兼容性。当前正在开发一个可以将0.2.0数据库版本的数据库转换为0.3.0(或更高版本)的工具。预期0.2.0的版本不再支持。