@@ -739,21 +739,44 @@ def check_recursion(function: Callable, return_error=False):
739739 return None
740740
741741
742+ class LineTime (object ):
743+ __slots__ = ("line_no" , "start_time" , "end_time" , "code" )
744+
745+ def __init__ (
746+ self , line_no : int , start_time : float , end_time : Optional [float ], code : str
747+ ):
748+ self .line_no = line_no
749+ self .start_time = start_time
750+ self .end_time = end_time
751+ self .code = code
752+
753+
742754class LineProfiler :
743755 """Line-by-line performance profiler."""
744756
745757 stdout = sys .stdout
746758
747- def __init__ (self ):
748- self .line_times : List [Tuple [ int , float , str ] ] = []
759+ def __init__ (self ) -> None :
760+ self .line_times : List [LineTime ] = []
749761 self .start_time = 0.0
750762 self .total_time = 0.0
751763 self .line_cache : Dict [str , list ] = {}
764+ self .filled = 0
752765
753766 def trace_calls (self , frame , event , arg ):
754767 """Trace each line of function calls"""
768+ current_time = time .perf_counter ()
769+ if self .filled < len (self .line_times ):
770+ self .filled += 1
771+ last_item = self .line_times [- 1 ]
772+ if last_item .end_time is None :
773+ self .line_times [- 1 ] = LineTime (
774+ last_item .line_no ,
775+ last_item .start_time ,
776+ current_time ,
777+ last_item .code ,
778+ )
755779 if event == "line" :
756- current_time = time .perf_counter ()
757780 line_no = frame .f_lineno
758781 filename = frame .f_code .co_filename
759782 # Get the code content of current line
@@ -777,7 +800,7 @@ def trace_calls(self, frame, event, arg):
777800 except Exception :
778801 line_content = "-"
779802 self .line_cache .setdefault (filename , [])
780- self .line_times .append ((line_no , current_time , line_content ))
803+ self .line_times .append (LineTime (line_no , current_time , None , line_content ))
781804 return self .trace_calls
782805
783806 def _get_function_base_indent (self , func_name : str ):
@@ -797,23 +820,23 @@ def calculate_and_print_stats(self, func_name: str):
797820 return
798821 # Calculate base indentation level of function
799822 base_indent = self ._get_function_base_indent (func_name )
823+ print (f"{ '=' * 95 } " , file = self .stdout )
800824 print (f"`{ func_name } ` profiling report:" , file = self .stdout )
801825 start_time = time .strftime ("%Y-%m-%d %H:%M:%S" )
802826 print (
803- f"{ start_time } | Total: { self .total_time * 1000 :.3f} ms" ,
827+ f"Start at { start_time } | Total: { self .total_time * 1000 :.3f} ms" ,
804828 file = self .stdout ,
805829 )
806830 # Calculate execution time for each line
807831 line_durations = []
808- for i in range (1 , len (self .line_times )):
809- prev_time = self .line_times [i - 1 ][1 ]
810- curr_time = self .line_times [i ][1 ]
811- duration = curr_time - prev_time
832+ for item in self .line_times :
833+ if item .end_time is None :
834+ continue
812835 line_durations .append (
813836 (
814- self . line_times [ i - 1 ][ 0 ] , # line number
815- duration , # duration
816- self . line_times [ i - 1 ][ 2 ] , # code content
837+ item . line_no , # line number
838+ item . end_time - item . start_time , # duration
839+ item . code , # code content
817840 )
818841 )
819842 # Sort by line number and merge time for same lines
@@ -882,19 +905,20 @@ def line_profiler(func: Callable) -> Callable:
882905 >>> example_function()
883906 45
884907 >>> output = LineProfiler.stdout.getvalue()
885- >>> output.splitlines()[0].startswith("`example_function` profiling report:")
908+ >>> output.splitlines()[0].startswith("=" * 95) and " `example_function` profiling report:" in output
886909 True
887910 >>> LineProfiler.stdout = sys.stdout # Restore original stdout
888911
889- # `example_function` profiling report:
890- # 2025-07-26 17:09:58 | Total: 1.122 ms
891- # ===============================================================================================
892- # Line % Total(ms) Count Avg(ms) Source Code
893- # -----------------------------------------------------------------------------------------------
894- # 3 73 0.825 1 0.825 -
895- # 4 3 0.040 11 0.004 -
896- # 5 4 0.050 10 0.005 -
897- # ===============================================================================================
912+ ===============================================================================================
913+ `example_function` profiling report:
914+ Start at 2025-07-26 17:09:58 | Total: 1.122 ms
915+ ===============================================================================================
916+ Line % Total(ms) Count Avg(ms) Source Code
917+ -----------------------------------------------------------------------------------------------
918+ 3 73 0.825 1 0.825 -
919+ 4 3 0.040 11 0.004 -
920+ 5 4 0.050 10 0.005 -
921+ ===============================================================================================
898922 """
899923
900924 @wraps (func )
0 commit comments