Preface
在某個工作下, 剛好需要統計在某個複雜程式執行中, 到底執行了多少模組底下的函數. 因此就開發了這個工具:
- count_fc.py
Usage:
這邊以模組 xyz.py 與 haha.py 為例:
- haha.py
- xyz.py
接著是主程式:
- main.py
執行結果如下:
接著可以去 /tmp/ 目錄撿收產生的每個模組的呼叫結果:
- count_fc.py
- import os
- import atexit
- import inspect
- import json
- import threading
- import logging
- import coloredlogs
- from collections import defaultdict
- ################################
- # Constants
- ################################
- MODU_PATH = os.path.dirname(__file__) if os.path.dirname(__file__) else './'
- ''' Path of current module '''
- LOGGER_FORMAT = "[%(levelno)s|%(module)s|%(lineno)s] %(message)s"
- ''' Format of Logger '''
- LOGGER_LEVEL = 10 # CRITICAL=50; ERROR=40; WARNING=30; INFO=20; DEBUG=10
- ''' Message level of Logger '''
- ################################
- # Local variables
- ################################
- _glock = threading.Lock()
- ''' Global threading lock '''
- _unloaded_modu_set = set()
- ''' Used to keep already unloaded module to avoid duplicate unload '''
- _log = logging.getLogger(__name__)
- ''' Logger object '''
- _log.setLevel(LOGGER_LEVEL)
- _log.propagate = False
- coloredlogs.install(level=LOGGER_LEVEL, logger=_log, fmt=LOGGER_FORMAT)
- def func_count(f):
- """Function decorator to accumulate the times of execution.
- Args:
- f: callable, function to count execution.
- Returns:
- The wrapped function.
- """
- def wrapped(*args, **kwargs):
- wrapped.calls += 1
- wrapped.name = f.__name__
- wrapped.modu_name = f.__module__
- wrapped.is_wrapped = True
- return f(*args, **kwargs)
- wrapped.calls = 0
- return wrapped
- def unload(modu):
- """The function to be called when a module is unloaded
- Args:
- modu: The module which is about to be unloaded.
- """
- with _glock:
- if modu in _unloaded_modu_set:
- return
- _log.debug(f"Unload {modu}")
- stat_pkfile_path = f'/tmp/{modu.__name__}.json'
- flist = [a for a in dir(modu)]
- fdict = defaultdict(int)
- for a in flist:
- fo = getattr(modu, a)
- if hasattr(fo, 'is_wrapped') and fo.modu_name == modu.__name__:
- _log.debug(f"\tCount {fo.name}")
- fdict[fo.name] += 1
- if fdict:
- with open(stat_pkfile_path, 'w') as fw:
- json.dump(fdict, fw)
- _unloaded_modu_set.add(modu)
- def decorate_mod(modu):
- """Decorate all the function(s) of input module
- Args:
- modu: This function will decorate all the function(s)
- collected from this module with `func_count` and register
- this module with a method which will be called when unload.
- """
- _log.debug(f'Decorate {modu}')
- flist = [a for a in dir(modu)]
- for a in flist:
- fo = getattr(modu, a)
- if inspect.isfunction(fo) and fo.__module__ == modu.__name__:
- _log.debug(f"\tfo={fo}; {fo.__module__}, {modu.__name__}")
- setattr(modu, fo.__name__, func_count(fo))
- setattr(modu, 'unload', unload)
- atexit.register(modu.unload, modu)
這邊以模組 xyz.py 與 haha.py 為例:
- haha.py
- from count_fc import decorate_mod
- from xyz import *
- def say_hi(name):
- print(message(name))
- decorate_mod(sys.modules[__name__])
- import sys
- import random
- from count_fc import decorate_mod
- def message(name):
- return random.choice(greeting_metods)(name)
- def greeting_with_aloha(name):
- return f"Aloha, {name}"
- def greeting_with_hello(name):
- return f"Hello, {name}"
- def greeting_with_hi(name):
- return f"Hi, {name}"
- decorate_mod(sys.modules[__name__])
- greeting_metods = [
- greeting_with_aloha,
- greeting_with_hello,
- greeting_with_hi
- ]
- main.py
- #!/usr/bin/env python
- from haha import say_hi
- if __name__ == '__main__':
- for name in ['John', 'Mary', 'Key', 'Peter', 'Jane', 'Bob']:
- say_hi(name)
- print(say_hi.calls)
接著可以去 /tmp/ 目錄撿收產生的每個模組的呼叫結果:
沒有留言:
張貼留言