Coverage for cc_modules/cc_debug.py: 19%
36 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-15 14:23 +0100
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-15 14:23 +0100
1"""
2camcops_server/cc_modules/cc_debug.py
4===============================================================================
6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
9 This file is part of CamCOPS.
11 CamCOPS is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 CamCOPS is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
24===============================================================================
26**Debugging utilities**
28"""
30import cProfile
31from types import FrameType
32from typing import Any, Callable, Set, Union
34TraceFuncType = Callable[[FrameType, str, Any], Union[Callable, None]]
35# ... returns a trace function (but we can't make the type definition
36# recursive) or None.
39# https://stackoverflow.com/questions/5375624/a-decorator-that-profiles-a-method-call-and-logs-the-profiling-result # noqa
40def profile(func: Callable) -> Any:
41 """
42 Decorator to generate profiler output for slow code
43 from camcops_server.cc_debug import profile.
45 Add @profile to the function you want to profile.
46 Will generate a file called <function name>.profile.
48 Can be visualised with e.g. SnakeViz (pip install snakeviz)
49 """
51 def wrapper(*args: Any, **kwargs: Any) -> int:
52 datafn = func.__name__ + ".profile"
53 prof = cProfile.Profile()
54 retval = prof.runcall(func, *args, **kwargs)
55 prof.dump_stats(datafn)
57 return retval
59 return wrapper
62# noinspection PyUnusedLocal
63def trace_calls(frame: FrameType, event: str, arg: Any) -> None:
64 """
65 A function that can be used as an argument to ``sys.settrace``. It prints
66 details of every function called (filename, line number, function name).
67 """
68 # https://pymotw.com/2/sys/tracing.html
69 # https://docs.python.org/3/library/sys.html#sys.settrace
71 # Function calls only
72 if event != "call":
73 return
74 co = frame.f_code
75 filename = co.co_filename
76 func_name = co.co_name
77 line_no = frame.f_lineno
78 print(f"- Call to {filename}:{line_no}:{func_name}")
81def makefunc_trace_unique_calls(file_only: bool = False) -> TraceFuncType:
82 """
83 Creates a function that you can use as an argument to ``sys.settrace()``.
84 When you execute a trace, it shows only new call to each function.
86 Args:
87 file_only:
88 Shows files called only, not functions with line numbers.
89 """
90 called = set() # type: Set[str]
92 # noinspection PyUnusedLocal
93 def _trace_calls(frame: FrameType, event: str, arg: Any) -> None:
94 nonlocal called
95 if event != "call":
96 return
97 co = frame.f_code
98 filename = co.co_filename
99 if file_only:
100 signature = filename
101 else:
102 func_name = co.co_name
103 line_no = frame.f_lineno
104 signature = f"{filename}:{line_no}:{func_name}"
105 if signature not in called:
106 print(f"- First call to {signature}")
107 called.add(signature)
109 return _trace_calls