88import sys
99import time
1010from contextlib import suppress
11+ from dataclasses import dataclass
1112from typing import TYPE_CHECKING
1213from typing import Any
1314
3233from _pytask .traceback import Traceback
3334
3435if TYPE_CHECKING :
36+ from collections .abc import Callable
3537 from collections .abc import Generator
3638 from pathlib import Path
3739 from typing import NoReturn
@@ -45,57 +47,61 @@ class _ExportFormats(enum.Enum):
4547 CSV = "csv"
4648
4749
48- @hookimpl (tryfirst = True )
49- def pytask_extend_command_line_interface (cli : click .Group ) -> None :
50- """Extend the command line interface."""
51- cli .add_command (profile )
52-
53-
54- @hookimpl
55- def pytask_post_parse (config : dict [str , Any ]) -> None :
56- """Register the export option."""
57- runtime_state = RuntimeState .from_root (config ["root" ])
58- config ["pm" ].register (ProfilePlugin (runtime_state ))
59- config ["pm" ].register (DurationNameSpace (runtime_state ))
60- config ["pm" ].register (ExportNameSpace )
61- config ["pm" ].register (FileSizeNameSpace )
62-
63-
64- @hookimpl (wrapper = True )
65- def pytask_execute_task (task : PTask ) -> Generator [None , None , None ]:
66- """Attach the duration of the execution to the task."""
67- start = time .time ()
68- result = yield
69- end = time .time ()
70- task .attributes ["duration" ] = (start , end )
71- return result
72-
73-
50+ @dataclass
7451class ProfilePlugin :
7552 """Collect and persist runtime profiling data."""
7653
77- def __init__ (self , runtime_state : RuntimeState ) -> None :
54+ runtime_state : RuntimeState | None = None
55+ runtime_state_factory : Callable [[Path ], RuntimeState ] = RuntimeState .from_root
56+
57+ @hookimpl (tryfirst = True )
58+ def pytask_extend_command_line_interface (self , cli : click .Group ) -> None :
59+ """Extend the command line interface."""
60+ cli .add_command (profile )
61+
62+ @hookimpl
63+ def pytask_post_parse (self , config : dict [str , Any ]) -> None :
64+ """Register the export option."""
65+ runtime_state = self .runtime_state_factory (config ["root" ])
7866 self .runtime_state = runtime_state
67+ config ["pm" ].register (DurationNameSpace (runtime_state ))
68+ config ["pm" ].register (ExportNameSpace )
69+ config ["pm" ].register (FileSizeNameSpace )
70+
71+ @hookimpl (wrapper = True )
72+ def pytask_execute_task (self , task : PTask ) -> Generator [None , None , None ]:
73+ """Attach the duration of the execution to the task."""
74+ start = time .time ()
75+ result = yield
76+ end = time .time ()
77+ task .attributes ["duration" ] = (start , end )
78+ return result
7979
8080 @hookimpl
8181 def pytask_execute_task_process_report (
8282 self , session : Session , report : ExecutionReport
8383 ) -> None :
8484 """Store runtime of successfully finishing tasks."""
8585 _ = session
86+ runtime_state = self .runtime_state
87+ if runtime_state is None :
88+ return
8689 task = report .task
8790 duration = task .attributes .get ("duration" )
8891 if report .outcome == TaskOutcome .SUCCESS and duration is not None :
89- self . runtime_state .update_task (task , * duration )
92+ runtime_state .update_task (task , * duration )
9093
9194 @hookimpl
9295 def pytask_unconfigure (self , session : Session ) -> None :
9396 """Flush runtime information on normal build exits."""
97+ runtime_state = self .runtime_state
98+ if runtime_state is None :
99+ return
94100 if session .config .get ("command" ) != "build" :
95101 return
96102 if session .config .get ("dry_run" ) or session .config .get ("explain" ):
97103 return
98- self . runtime_state .flush ()
104+ runtime_state .flush ()
99105
100106
101107class DurationNameSpace :
0 commit comments