Coverage for cc_modules/cc_debug.py : 22%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
3"""
4camcops_server/cc_modules/cc_debug.py
6===============================================================================
8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com).
10 This file is part of CamCOPS.
12 CamCOPS is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
17 CamCOPS is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
25===============================================================================
27**Debugging utilities**
29"""
31import cProfile
32# import trace
33from types import FrameType
34from typing import Any, Callable, Set, Union
36TraceFuncType = Callable[[FrameType, str, Any], Union[Callable, None]]
37# ... returns a trace function (but we can't make the type definition
38# recursive) or None.
41# https://stackoverflow.com/questions/5375624/a-decorator-that-profiles-a-method-call-and-logs-the-profiling-result # noqa
42def profile(func):
43 """
44 Decorator to generate profiler output for slow code
45 from camcops_server.cc_debug import profile.
47 Add @profile to the function you want to profile.
48 Will generate a file called <function name>.profile.
50 Can be visualised with e.g. SnakeViz (pip install snakeviz)
51 """
53 def wrapper(*args, **kwargs):
54 datafn = func.__name__ + ".profile"
55 prof = cProfile.Profile()
56 retval = prof.runcall(func, *args, **kwargs)
57 prof.dump_stats(datafn)
59 return retval
61 return wrapper
64# noinspection PyUnusedLocal
65def trace_calls(frame: FrameType, event: str, arg: Any) \
66 -> Union[TraceFuncType, None]:
67 """
68 A function that can be used as an argument to ``sys.settrace``. It prints
69 details of every function called (filename, line number, function name).
70 """
71 # https://pymotw.com/2/sys/tracing.html
72 # https://docs.python.org/3/library/sys.html#sys.settrace
74 # Function calls only
75 if event != "call":
76 return
77 co = frame.f_code
78 filename = co.co_filename
79 func_name = co.co_name
80 line_no = frame.f_lineno
81 print(f"- Call to {filename}:{line_no}:{func_name}")
84def makefunc_trace_unique_calls(file_only: bool = False) -> TraceFuncType:
85 """
86 Creates a function that you can use as an argument to ``sys.settrace()``.
87 When you execute a trace, it shows only new call to each function.
89 Args:
90 file_only:
91 Shows files called only, not functions with line numbers.
92 """
93 called = set() # type: Set[str]
95 # noinspection PyUnusedLocal
96 def _trace_calls(frame: FrameType, event: str, arg: Any) \
97 -> Union[TraceFuncType, None]:
98 nonlocal called
99 if event != "call":
100 return
101 co = frame.f_code
102 filename = co.co_filename
103 if file_only:
104 signature = filename
105 else:
106 func_name = co.co_name
107 line_no = frame.f_lineno
108 signature = f"{filename}:{line_no}:{func_name}"
109 if signature not in called:
110 print(f"- First call to {signature}")
111 called.add(signature)
113 return _trace_calls