moprofiler.memory 源代码

# encoding=utf8
"""
提供用于内存性能分析的工具
"""
from __future__ import absolute_import

import logging
import types  # pylint: disable=W0611
from functools import wraps

from memory_profiler import LineProfiler, choose_backend, show_results

from . import base

try:
    import tracemalloc

    has_tracemalloc = True  # pragma: no cover
except ImportError:  # pragma: no cover
    has_tracemalloc = False

LOG = logging.getLogger(__name__)

__memory_profiler_pool = {}  #: 用来存储内存分析器的池子


[文档]class MemoryProfiler(LineProfiler): """ 内存分析器 """
[文档] def print_stats(self, stream=None, precision=1): """ 打印统计结果 :param object stream: 输出方式,默认为 stdout ,可指定为文件 :param int precision: 精度 """ show_results(self, stream=stream, precision=precision)
[文档]def _make_memory_profiler_getter(self_or_cls=None): """ 生成内存分析器获取器 :param MemoryProfilerMixin self_or_cls: 内存分析器 Mixin 实例或类 :rtype: memory_profiler_getter """ def _profiler_getter(name, backend='psutil', raise_except=True, force_new_profiler=False): """ 闭包方法,获取内存分析器 :param str name: 指定的内存分析器名称 :param str backend: 内存分析器的处理后端 :param bool raise_except: 若不存在是否抛出异常,默认为是,若为否,则会生成指定名称的分析器并返回 :param bool force_new_profiler: 是否强制使用新的分析器,默认为 ``否`` :return: 内存分析器对象 :rtype: MemoryProfiler :raises KeyError: 获取的键名不存在 """ if self_or_cls: name = base.get_default_key(self_or_cls, name) if force_new_profiler: __memory_profiler_pool.pop(name, None) if name not in __memory_profiler_pool: if raise_except: raise KeyError(u'获取的键名({name})不存在!'.format(name=name)) LOG.info(u'创建新的内存分析器: {}'.format(name)) __memory_profiler_pool[name] = MemoryProfiler(backend=backend) return __memory_profiler_pool[name] return _profiler_getter
memory_profiler_getter = _make_memory_profiler_getter() #: 用于存储装饰函数、静态方法时创建的内存分析器
[文档]class MemoryProfilerMixin(base.ProfilerMixin): """ 内存分析器 Mixin 类 用以提供复杂的内存分析功能,如: #. 针对需要多次调用的方法进行累加分析的场景 #. 在一次代码执行流程中同时分析多个方法,并灵活控制分析结果的输出 """ # 此处若想修改 backend 可通过继承该 mixin 并修改 defaultdict 中的默认值来实现
[文档] @classmethod def memory_profiler(cls, name, backend='psutil', raise_except=True, force_new_profiler=False): """ 获取指定的内存分析器 :param str name: 指定的内存分析器名称 :param str backend: 内存分析器的处理后端 :param bool raise_except: 若不存在是否抛出异常,默认为是,若为否,则会生成指定名称的分析器并返回 :param bool force_new_profiler: 是否强制使用新的分析器,默认为 ``否`` :return: 内存分析器对象 :rtype: MemoryProfiler :raises KeyError: 获取的键名不存在 """ return _make_memory_profiler_getter(cls)( name, backend=backend, raise_except=raise_except, force_new_profiler=force_new_profiler)
[文档]def _process_backend(backend='psutil'): """ 处理内存分析器的后端 :param str backend: 内存监控的 backend ,默认为 'psutil' :return: 处理后的后端名称 :rtype: str """ backend = choose_backend(backend) if backend == 'tracemalloc' and has_tracemalloc and \ not tracemalloc.is_tracing(): # pragma: no cover tracemalloc.start() return backend
[文档]def _get_profiler(args, backend, name, func, force_new_profiler=False): """ 获取分析器 若当前被装饰的方法未继承自 :py:class:`~moprofiler.memory.MemoryProfilerMixin` , 则将其当做普通函数装饰,使用指定的 ``backend`` ,进行初始化并返回 否则,使用被装饰方法的第一个参数,并调用其 :py:meth:`~moprofiler.memory.MemoryProfilerMixin.memory_profiler` 方法获取实例并返回 :param list args: 被装饰方法的位置参数列表 :param str backend: 内存分析器的处理后端 :param str name: 关键字参数,被装饰方法所使用的内存分析器名称,默认为使用被装饰方法的方法名 :param func: 被装饰的函数/方法 :type func: types.FunctionType or types.MethodType :param bool force_new_profiler: 是否强制使用新的分析器,默认为 ``否`` :return: 内存分析器对象 :rtype: MemoryProfiler """ _name = name or func.__name__ if not (args and base.is_instance_or_subclass(args[0], MemoryProfilerMixin)): # 若当前被装饰的方法未继承 MemoryProfilerMixin ,则将其作为普通函数装饰 lp = memory_profiler_getter( _name, backend=backend, raise_except=False, force_new_profiler=force_new_profiler) else: self_or_cls = args[0] # type: MemoryProfilerMixin lp = self_or_cls.memory_profiler( _name, backend=backend, raise_except=False, force_new_profiler=force_new_profiler) return lp
[文档]def memory_profiler( _function=None, name='', print_res=True, stream=None, precision=1, backend='psutil', force_new_profiler=False): """ 内存分析器装饰器 :param _function: 被封装的对象,由解释器自动传入,不需关心 :type _function: types.FunctionType or types.MethodType :param str name: 关键字参数,被装饰方法所使用的内存分析器名称,默认为使用被装饰方法的方法名 :param bool print_res: 是否在被装饰对象退出后立刻打印分析结果,默认为 True 。 当需要将多次调用结果聚集后输出时,可设为 False ,并通过 Mixin 中的 memory_profiler 进行结果输出 :param object stream: 输出方式,默认为 stdout ,可指定为文件 :param int precision: 精度,默认为 1 :param str backend: 内存监控的 backend ,默认为 'psutil' :param bool force_new_profiler: 是否强制使用新的分析器,默认为 ``否`` :return: 装饰后的函数或方法 :rtype: types.FunctionType or types.MethodType """ invoked = bool(_function and callable(_function)) if invoked: func = _function # type: types.FunctionType or types.MethodType backend = _process_backend(backend) def wrapper(func): """ 装饰器封装函数 """ @wraps(func) def inner(*args, **kwargs): """ 将被封装方法使用 LineProfiler 进行封装 """ lp = _get_profiler( args, backend, name, func, force_new_profiler=force_new_profiler) profiler_wrapper = lp(func) res = profiler_wrapper(*args, **kwargs) if print_res: lp.print_stats(stream=stream, precision=precision) return res return inner return wrapper if not invoked else wrapper(func)