Coverage for cc_modules/cc_debug.py: 22%
37 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
1#!/usr/bin/env python
3"""
4camcops_server/cc_modules/cc_debug.py
6===============================================================================
8 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
9 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
11 This file is part of CamCOPS.
13 CamCOPS is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
18 CamCOPS is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
26===============================================================================
28**Debugging utilities**
30"""
32import cProfile
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(
66 frame: FrameType, event: str, arg: Any
67) -> Union[TraceFuncType, None]:
68 """
69 A function that can be used as an argument to ``sys.settrace``. It prints
70 details of every function called (filename, line number, function name).
71 """
72 # https://pymotw.com/2/sys/tracing.html
73 # https://docs.python.org/3/library/sys.html#sys.settrace
75 # Function calls only
76 if event != "call":
77 return
78 co = frame.f_code
79 filename = co.co_filename
80 func_name = co.co_name
81 line_no = frame.f_lineno
82 print(f"- Call to {filename}:{line_no}:{func_name}")
85def makefunc_trace_unique_calls(file_only: bool = False) -> TraceFuncType:
86 """
87 Creates a function that you can use as an argument to ``sys.settrace()``.
88 When you execute a trace, it shows only new call to each function.
90 Args:
91 file_only:
92 Shows files called only, not functions with line numbers.
93 """
94 called = set() # type: Set[str]
96 # noinspection PyUnusedLocal
97 def _trace_calls(
98 frame: FrameType, event: str, arg: Any
99 ) -> Union[TraceFuncType, None]:
100 nonlocal called
101 if event != "call":
102 return
103 co = frame.f_code
104 filename = co.co_filename
105 if file_only:
106 signature = filename
107 else:
108 func_name = co.co_name
109 line_no = frame.f_lineno
110 signature = f"{filename}:{line_no}:{func_name}"
111 if signature not in called:
112 print(f"- First call to {signature}")
113 called.add(signature)
115 return _trace_calls