callmonitor - أداة بسيطة لمراقبة وتسجيل مكالمات وظيفة pip install callmonitorأو استنساخ هذا الريبو و:
python setup.py install من السهل الاستخدام ، ما عليك سوى تزيين أي وظيفة باستخدام Decorator @intercept . على سبيل المثال:
from callmonitor import intercept
@ intercept
def test_fn_2 ( x , y = 2 , z = 3 ):
pass سيؤدي ذلك إلى حفظ المدخلات ( args ، kwargs و argspec ) جنبًا إلى جنب مع قاعدة بيانات استدعاء ( callmonitor.DB ) إلى: call-monitor/test_fn_2/<invocation count> .
callmonitor الإخراج في حالة وجود مجلد call-monitor بالفعل (على سبيل المثال ، يتم إنشاء مجموعة سابقة) ، ثم يتم إنشاء مجلد جديد call-monitor-1 ، أو call-monitor-2 ، وما إلى ذلك. راجع الأقسام الموجودة في Data Structure لمزيد من التفاصيل حول كيفية حفظ هذه البيانات.
لتجنب عمليات مختلفة من الكتابة إلى نفس الموقع ، تم إلحاق callmonitor -tid=<N> إلى مجلد الجذر ( call-monitor ). يدعم callmonitor حاليًا mpi4py خارج المربع: إذا كان mpi4py.MPI.COMM_WORLD.Get_rank() > 1 ، يفترض callmonitor تلقائيًا أنه يعمل على وضع IM متعدد الخيوط ويتم إلحاقه -tid=<Get_rank()> إلى الإخراج. إذا callmonitor.Settings برنامجك متعدد الخيوط مع إطار آخر (على callmonitor المثال. concurrent.Futures .
from callmonitor import Settings
settings = Settings ()
settings . enable_multi_threading ( THREAD_ID ) قبل الاحتجاج الأول intercept (يتم إنشاء قاعدة البيانات على القرص عند الحاجة لأول مرة ، يكون ذلك عند هذه النقطة عند قراءة callmonitor.Settings . أي تغييرات تم إجراؤها على callmonitor.Settings بعد ذلك لن تصدر مفعولًا إلا إذا تم إعادة إنشاء قاعدة البيانات - باستخدام callmonitor.CONTEXT.new ).
Handler الحجة الخاص بك في بعض الأحيان ، لن يقطع pickle من حيث توفير مدخلات الوظائف - على سبيل المثال. عندما نحتاج إلى حفظ أنواع البيانات الفاخرة الخاصة بنا. يوفر callmonitor وسيلة لبناء معالجات الحجة لأسفل والتسجيل في Global callmonitor.REGISTRY . يتم الاستعلام عن السجل في كل مرة تتم معالجة مدخلات الوظيفة ، لذلك إذا قمت بإنشاء ArgHandler الخاص بك وإضافتها باستخدام callmonitor.REGISTRY.add ، فستقوم بمعالجة أي وسيطات من نوع البيانات المرتبطة بها من تلك النقطة إلى الأمام. على سبيل المثال ، يوفر numpy وظائف save / load الخاصة بها. لقد قمت بالفعل ببناء (وسجلت) معالج arggument numpy مثل ذلك:
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 ) (تذكر أن callmonitor.REGISTRY.add يجب استدعاؤه قبل الاحتجاج الأول لـ @intercept الذي يحتاج إلى هذا Handler بالذات). يحتاج المعالج المخصص إلى أن يرث فئة 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 وأي معالجات مخصصة - تذكر تسجيلها في callmonitor.REGISTRY قبل تنفيذ db.get )
تذكر: يبدأ 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 ، و Args عبر وظائف __str__ و __repr__ . على سبيل المثال ، يوضح 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 أي وسيطات وليس في args و kwargs من الافتراضيات FullArgSpec . إذا كنت ترغب فقط في إعادة إنشاء الوظيفة الأصلية ، فاستدعاء args و kwargs التي تم إرجاعها بواسطة callmonitor.DB.get يجب أن تكون كافية.
على الرغم من عدم وجود قاعدة بيانات من الناحية الفنية ، دعنا ندعو الدلائل التي تم إنشاؤها بواسطة 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> إما فهرس وسيطة الإدخال ، أو kw لـ Kwargs. على سبيل المثال:
call-monitor
├── db.pkl
└── test_np_1
├── 1
│ ├── arg_1.npy
│ └── input_descriptor.pkl
└── 2
├── arg_n.npy
└── input_descriptor.pkl
تم إيلاء الاعتبار الكامل لتوفير جميع بيانات المكالمات في بنية بيانات واحدة - ربما حتى قاعدة بيانات حقيقية ؛) - ولكن للقيام بذلك بكفاءة على نطاق واسع ليس بالأمر السهل ، وقد يجعل هذه الحزمة مرهقة. ستتضمن الإصدارات المستقبلية القدرة على دمج مكالمات وظائف صغيرة متعددة في كائن تراكم واحد لتجنب أعداد كبيرة من الملفات الصغيرة.
الإصدار 0.3.0 يبرز العديد من التحسينات إلى callmonitor . لذلك لم يعد بإمكاننا تمكين التوافق المتخلف الأصلي. يتم تطوير الأداة التي يمكنها تحويل قاعدة بيانات الإصدار 0.2.0 إلى الإصدار 0.3.0 (أو لاحقًا). لم تعد الإصدارات قبل المواعدة 0.2.0 مدعومة.