Hide keyboard shortcuts

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 

2 

3""" 

4camcops_server/cc_modules/cc_debug.py 

5 

6=============================================================================== 

7 

8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com). 

9 

10 This file is part of CamCOPS. 

11 

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. 

16 

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. 

21 

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/>. 

24 

25=============================================================================== 

26 

27**Debugging utilities** 

28 

29""" 

30 

31import cProfile 

32# import trace 

33from types import FrameType 

34from typing import Any, Callable, Set, Union 

35 

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. 

39 

40 

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. 

46 

47 Add @profile to the function you want to profile. 

48 Will generate a file called <function name>.profile. 

49 

50 Can be visualised with e.g. SnakeViz (pip install snakeviz) 

51 """ 

52 

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) 

58 

59 return retval 

60 

61 return wrapper 

62 

63 

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 

73 

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}") 

82 

83 

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. 

88 

89 Args: 

90 file_only: 

91 Shows files called only, not functions with line numbers. 

92 """ 

93 called = set() # type: Set[str] 

94 

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) 

112 

113 return _trace_calls