Coverage for src/backend/InvenTree/InvenTree/profiling.py: 0%

60 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 17:47 +0000

1"""Helper functions for profiling InvenTree code. 

2 

3A set of decorators to assist with profiling functions and logging, 

4which implement oft-repeated patterns used during development and debugging. 

5 

6Note: These functions are not to be used in production code. 

7""" 

8 

9from functools import wraps 

10 

11 

12def ensure_debug(): 

13 """Ensure that InvenTree is running in DEBUG mode.""" 

14 from django.conf import settings 

15 

16 if not settings.DEBUG: 

17 raise RuntimeError('Profiling functions can only be used in DEBUG mode!') 

18 

19 

20def time_function(func): # pragma: no cover 

21 """Decorator to time a function's execution duration. 

22 

23 Args: 

24 func: Function to be timed 

25 """ 

26 

27 @wraps(func) 

28 def wrapper(*args, **kwargs): 

29 import time 

30 

31 ensure_debug() 

32 

33 start_time = time.time() 

34 result = func(*args, **kwargs) 

35 end_time = time.time() 

36 

37 duration = end_time - start_time 

38 print(f"Function '{func.__name__}' executed in {duration:.6f} seconds.") 

39 

40 return result 

41 

42 return wrapper 

43 

44 

45def profile_function(filename='profile.prof'): # pragma: no cover 

46 """Decorator to profile a function using cProfile. 

47 

48 Args: 

49 func: Function to be profiled 

50 filename: Output filename for the profiling data 

51 """ 

52 

53 def decorator(func): 

54 

55 @wraps(func) 

56 def wrapper(*args, **kwargs): 

57 import cProfile 

58 import io 

59 import pstats 

60 

61 ensure_debug() 

62 

63 pr = cProfile.Profile() 

64 pr.enable() 

65 

66 result = func(*args, **kwargs) 

67 

68 pr.disable() 

69 s = io.StringIO() 

70 sortby = pstats.SortKey.CUMULATIVE 

71 ps = pstats.Stats(pr, stream=s).sort_stats(sortby) 

72 ps.dump_stats(filename) 

73 print(s.getvalue()) 

74 

75 return result 

76 

77 return wrapper 

78 

79 return decorator 

80 

81 

82def log_slow_queries( 

83 threshold: float = 0.01, n: int = 5, log_to_file: bool = True 

84): # pragma: no cover 

85 """Decorator to log slow database queries in a Django view function. 

86 

87 Args: 

88 func: Function to be decorated 

89 threshold: Time threshold (in seconds) for logging slow queries 

90 n: Number of slowest queries to log 

91 log_to_file: Whether to log to a file or print to console 

92 """ 

93 

94 def decorator(func): 

95 

96 @wraps(func) 

97 def wrapper(*args, **kwargs): 

98 from django.db import connection 

99 

100 ensure_debug() 

101 

102 result = func(*args, **kwargs) 

103 

104 slow_queries = [ 

105 q for q in connection.queries if float(q.get('time', 0)) >= threshold 

106 ] 

107 slow_queries.sort(key=lambda x: float(x.get('time', 0)), reverse=True) 

108 

109 log_entries = [] 

110 for query in slow_queries[:n]: 

111 log_entry = f'Time: {query["time"]}s | SQL: {query["sql"]}' 

112 log_entries.append(log_entry) 

113 

114 if log_entries: 

115 log_message = '\n'.join(log_entries) 

116 if log_to_file: 

117 with open('slow_queries.log', 'w', encoding='utf-8') as f: 

118 f.write(f'Slow queries detected:\n{log_message}\n') 

119 else: 

120 print(f'Slow queries detected:\n{log_message}') 

121 

122 return result 

123 

124 return wrapper 

125 

126 return decorator 

127 

128 

129# Raise an exception if this file is imported outside of DEBUG mode 

130ensure_debug()