1616from .constants import DEFAULT_READ_TIMEOUT_SECONDS
1717from .cursor import Cursor
1818from .errors import NotSupportedError , OperationalError
19- from .models import ExecutionResult , Store , StoreResult
19+ from .models import ExecutionResult , ProgressInfo , Store , StoreResult
2020from .types import (
2121 RequestKind ,
2222 EventKind ,
2727)
2828
2929
30+ ProgressHandler = Callable [[ProgressInfo ], None ]
31+ """A callable invoked with a :class:`ProgressInfo` on every progress event."""
32+
33+
3034@dataclass
3135class Query :
3236 sql : str
@@ -64,6 +68,7 @@ def __init__(
6468 self .__results_format = results_format
6569 self .__data_compression = data_compression
6670 self .__geometry_representation = geometry_representation
71+ self .__progress_handler : ProgressHandler | None = None
6772
6873 self .__queries : dict [str , Query ] = {}
6974 self .__thread = threading .Thread (
@@ -89,6 +94,21 @@ def rollback(self) -> None:
8994 def cursor (self ) -> Cursor :
9095 return Cursor (self .__execute_sql , self .__cancel_query )
9196
97+ def set_progress_handler (self , handler : ProgressHandler | None ) -> None :
98+ """Register a callback invoked for execution progress events.
99+
100+ When a handler is set, every ``execute_sql`` request automatically
101+ includes ``enable_progress_events: true`` so the SQL session streams
102+ progress updates for running queries.
103+
104+ Pass ``None`` to disable progress reporting.
105+
106+ This follows the `sqlite3 Connection.set_progress_handler()
107+ <https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.set_progress_handler>`_
108+ pattern (PEP 249 vendor extension).
109+ """
110+ self .__progress_handler = handler
111+
92112 def __main_loop (self ) -> None :
93113 """Main background loop listening for messages from the SQL session."""
94114 logging .info ("Starting background connection handling loop..." )
@@ -116,6 +136,25 @@ def __listen(self) -> None:
116136 # Invalid event.
117137 return
118138
139+ # Progress events are independent of the query state machine and don't
140+ # require a tracked query — the handler is connection-level.
141+ if kind == EventKind .EXECUTION_PROGRESS :
142+ handler = self .__progress_handler
143+ if handler is None :
144+ return
145+ try :
146+ handler (
147+ ProgressInfo (
148+ execution_id = execution_id ,
149+ tasks_total = message .get ("tasks_total" , 0 ),
150+ tasks_completed = message .get ("tasks_completed" , 0 ),
151+ tasks_active = message .get ("tasks_active" , 0 ),
152+ )
153+ )
154+ except Exception :
155+ logging .exception ("Progress handler raised an exception" )
156+ return
157+
119158 query = self .__queries .get (execution_id )
120159 if not query :
121160 logging .warning (
@@ -236,6 +275,9 @@ def __execute_sql(
236275 "statement" : sql ,
237276 }
238277
278+ if self .__progress_handler is not None :
279+ request ["enable_progress_events" ] = True
280+
239281 if store :
240282 request ["store" ] = {
241283 "format" : store .format .value ,
0 commit comments