1313
1414import click
1515from rich .table import Table
16- from sqlalchemy .orm import Mapped
17- from sqlalchemy .orm import mapped_column
1816
1917from _pytask .click import ColoredCommand
2018from _pytask .click import EnumChoice
2119from _pytask .console import console
2220from _pytask .console import format_task_name
2321from _pytask .dag import create_dag
24- from _pytask .database_utils import BaseTable
25- from _pytask .database_utils import DatabaseSession
2622from _pytask .exceptions import CollectionError
2723from _pytask .exceptions import ConfigurationError
2824from _pytask .node_protocols import PPathNode
3127from _pytask .outcomes import TaskOutcome
3228from _pytask .pluginmanager import hookimpl
3329from _pytask .pluginmanager import storage
30+ from _pytask .runtime_store import RuntimeState
3431from _pytask .session import Session
3532from _pytask .traceback import Traceback
3633
@@ -48,16 +45,6 @@ class _ExportFormats(enum.Enum):
4845 CSV = "csv"
4946
5047
51- class Runtime (BaseTable ):
52- """Record of runtimes of tasks."""
53-
54- __tablename__ = "runtime"
55-
56- task : Mapped [str ] = mapped_column (primary_key = True )
57- date : Mapped [float ]
58- duration : Mapped [float ]
59-
60-
6148@hookimpl (tryfirst = True )
6249def pytask_extend_command_line_interface (cli : click .Group ) -> None :
6350 """Extend the command line interface."""
@@ -67,6 +54,7 @@ def pytask_extend_command_line_interface(cli: click.Group) -> None:
6754@hookimpl
6855def pytask_post_parse (config : dict [str , Any ]) -> None :
6956 """Register the export option."""
57+ config ["runtime_state" ] = RuntimeState .from_root (config ["root" ])
7058 config ["pm" ].register (ExportNameSpace )
7159 config ["pm" ].register (DurationNameSpace )
7260 config ["pm" ].register (FileSizeNameSpace )
@@ -83,26 +71,16 @@ def pytask_execute_task(task: PTask) -> Generator[None, None, None]:
8371
8472
8573@hookimpl
86- def pytask_execute_task_process_report (report : ExecutionReport ) -> None :
87- """Store runtime of successfully finishing tasks in database."""
74+ def pytask_execute_task_process_report (
75+ session : Session , report : ExecutionReport
76+ ) -> None :
77+ """Store runtime of successfully finishing tasks."""
8878 task = report .task
8979 duration = task .attributes .get ("duration" )
9080 if report .outcome == TaskOutcome .SUCCESS and duration is not None :
91- _create_or_update_runtime (task .signature , * duration )
92-
93-
94- def _create_or_update_runtime (task_signature : str , start : float , end : float ) -> None :
95- """Create or update a runtime entry."""
96- with DatabaseSession () as session :
97- runtime = session .get (Runtime , task_signature )
98-
99- if not runtime :
100- session .add (Runtime (task = task_signature , date = start , duration = end - start ))
101- else :
102- for attr , val in (("date" , start ), ("duration" , end - start )):
103- setattr (runtime , attr , val )
104-
105- session .commit ()
81+ runtime_state = session .config .get ("runtime_state" )
82+ if runtime_state is not None :
83+ runtime_state .update_task (task , * duration )
10684
10785
10886@click .command (cls = ColoredCommand )
@@ -189,20 +167,23 @@ class DurationNameSpace:
189167 @staticmethod
190168 @hookimpl
191169 def pytask_profile_add_info_on_task (
192- tasks : list [PTask ], profile : dict [str , dict [str , Any ]]
170+ session : Session , tasks : list [PTask ], profile : dict [str , dict [str , Any ]]
193171 ) -> None :
194172 """Add the runtime for tasks to the profile."""
195- runtimes = _collect_runtimes (tasks )
173+ runtimes = _collect_runtimes (session , tasks )
196174 for name , duration in runtimes .items ():
197175 profile [name ]["Duration (in s)" ] = round (duration , 2 )
198176
199177
200- def _collect_runtimes (tasks : list [PTask ]) -> dict [str , float ]:
178+ def _collect_runtimes (session : Session , tasks : list [PTask ]) -> dict [str , float ]:
201179 """Collect runtimes."""
202- with DatabaseSession () as session :
203- runtimes = [session .get (Runtime , task .signature ) for task in tasks ]
180+ runtime_state = session .config .get ("runtime_state" )
181+ if runtime_state is None :
182+ return {}
204183 return {
205- task .name : r .duration for task , r in zip (tasks , runtimes , strict = False ) if r
184+ task .name : duration
185+ for task in tasks
186+ if (duration := runtime_state .get_duration (task )) is not None
206187 }
207188
208189
@@ -313,3 +294,16 @@ def _get_info_names(profile: dict[str, dict[str, Any]]) -> list[str]:
313294 base : set [str ] = set ()
314295 info_names : list [str ] = sorted (base .union (* (set (val ) for val in profile .values ())))
315296 return info_names
297+
298+
299+ @hookimpl
300+ def pytask_unconfigure (session : Session ) -> None :
301+ """Flush runtime information on normal build exits."""
302+ if session .config .get ("command" ) != "build" :
303+ return
304+ if session .config .get ("dry_run" ) or session .config .get ("explain" ):
305+ return
306+ runtime_state = session .config .get ("runtime_state" )
307+ if runtime_state is None :
308+ return
309+ runtime_state .flush ()
0 commit comments