2020import datetime
2121import threading
2222import uuid
23- from typing import Any , Callable , Optional , Set
23+ from typing import Any , Callable , Literal , Set
2424
2525import google .cloud .bigquery ._job_helpers
2626import google .cloud .bigquery .job .query
2727import google .cloud .bigquery .table
2828
2929import bigframes .session .executor
3030
31+ _DEFAULT : Literal ["default" ] = "default"
32+
33+ ProgressBarType = Literal ["default" , "auto" , "notebook" , "terminal" ] | None
34+ QueryPlanType = list [google .cloud .bigquery .job .query .QueryPlanEntry ] | None
35+
3136
3237class Subscriber :
33- def __init__ (self , callback : Callable [[Event ], None ], * , publisher : Publisher ):
38+ def __init__ (
39+ self ,
40+ callback : Callable [[EventEnvelope ], None ],
41+ * ,
42+ publisher : Publisher ,
43+ ):
3444 self ._publisher = publisher
3545 self ._callback = callback
3646 self ._subscriber_id = uuid .uuid4 ()
@@ -57,10 +67,12 @@ def __enter__(self):
5767 def __exit__ (self , exc_type , exc_value , traceback ):
5868 if exc_value is not None :
5969 self (
60- UnknownErrorEvent (
61- exc_type = exc_type ,
62- exc_value = exc_value ,
63- traceback = traceback ,
70+ EventEnvelope (
71+ UnknownErrorEvent (
72+ exc_type = exc_type ,
73+ exc_value = exc_value ,
74+ traceback = traceback ,
75+ )
6476 )
6577 )
6678 self .close ()
@@ -74,7 +86,10 @@ def __init__(self):
7486 concurrent .futures .ThreadPoolExecutor ()
7587 )
7688
77- def subscribe (self , callback : Callable [[Event ], None ]) -> Subscriber :
89+ def subscribe (
90+ self ,
91+ callback : Callable [[EventEnvelope ], None ],
92+ ) -> Subscriber :
7893 # TODO(b/448176657): figure out how to handle subscribers/publishers in
7994 # a background thread. Maybe subscribers should be thread-local?
8095 subscriber = Subscriber (callback , publisher = self )
@@ -86,17 +101,21 @@ def unsubscribe(self, subscriber: Subscriber):
86101 with self ._subscribers_lock :
87102 self ._subscribers .remove (subscriber )
88103
89- def publish (self , event : Event ):
104+ def publish (self , envelope : EventEnvelope | Event ):
105+ if not isinstance (envelope , EventEnvelope ):
106+ envelope = EventEnvelope (event = envelope )
90107 with self ._subscribers_lock :
91108 for subscriber in self ._subscribers :
92- subscriber (event )
109+ subscriber (envelope )
93110
94- async def publish_async (self , event : Event ):
111+ async def publish_async (self , envelope : EventEnvelope | Event ):
112+ if not isinstance (envelope , EventEnvelope ):
113+ envelope = EventEnvelope (event = envelope )
95114 with self ._subscribers_lock :
96115 subscribers_snapshot = list (self ._subscribers )
97116 loop = asyncio .get_running_loop ()
98117 tasks = [
99- loop .run_in_executor (self ._executor , subscriber , event )
118+ loop .run_in_executor (self ._executor , subscriber , envelope )
100119 for subscriber in subscribers_snapshot
101120 ]
102121 return await asyncio .gather (* tasks , return_exceptions = True )
@@ -106,6 +125,12 @@ class Event:
106125 pass
107126
108127
128+ @dataclasses .dataclass (frozen = True )
129+ class EventEnvelope :
130+ event : Event
131+ progress_bar : ProgressBarType = _DEFAULT
132+
133+
109134@dataclasses .dataclass (frozen = True )
110135class SessionClosed (Event ):
111136 session_id : str
@@ -121,7 +146,7 @@ class ExecutionRunning(Event):
121146
122147@dataclasses .dataclass (frozen = True )
123148class ExecutionFinished (Event ):
124- result : Optional [ bigframes .session .executor .ExecuteResult ] = None
149+ result : bigframes .session .executor .ExecuteResult | None = None
125150
126151
127152@dataclasses .dataclass (frozen = True )
@@ -136,13 +161,16 @@ class BigQuerySentEvent(ExecutionRunning):
136161 """Query sent to BigQuery."""
137162
138163 query : str
139- billing_project : Optional [ str ] = None
140- location : Optional [ str ] = None
141- job_id : Optional [ str ] = None
142- request_id : Optional [ str ] = None
164+ billing_project : str | None = None
165+ location : str | None = None
166+ job_id : str | None = None
167+ request_id : str | None = None
143168
144169 @classmethod
145- def from_bqclient (cls , event : google .cloud .bigquery ._job_helpers .QuerySentEvent ):
170+ def from_bqclient (
171+ cls ,
172+ event : google .cloud .bigquery ._job_helpers .QuerySentEvent ,
173+ ):
146174 return cls (
147175 query = event .query ,
148176 billing_project = event .billing_project ,
@@ -157,13 +185,16 @@ class BigQueryRetryEvent(ExecutionRunning):
157185 """Query sent another time because the previous attempt failed."""
158186
159187 query : str
160- billing_project : Optional [ str ] = None
161- location : Optional [ str ] = None
162- job_id : Optional [ str ] = None
163- request_id : Optional [ str ] = None
188+ billing_project : str | None = None
189+ location : str | None = None
190+ job_id : str | None = None
191+ request_id : str | None = None
164192
165193 @classmethod
166- def from_bqclient (cls , event : google .cloud .bigquery ._job_helpers .QueryRetryEvent ):
194+ def from_bqclient (
195+ cls ,
196+ event : google .cloud .bigquery ._job_helpers .QueryRetryEvent ,
197+ ):
167198 return cls (
168199 query = event .query ,
169200 billing_project = event .billing_project ,
@@ -177,19 +208,20 @@ def from_bqclient(cls, event: google.cloud.bigquery._job_helpers.QueryRetryEvent
177208class BigQueryReceivedEvent (ExecutionRunning ):
178209 """Query received and acknowledged by the BigQuery API."""
179210
180- billing_project : Optional [ str ] = None
181- location : Optional [ str ] = None
182- job_id : Optional [ str ] = None
183- statement_type : Optional [ str ] = None
184- state : Optional [ str ] = None
185- query_plan : Optional [ list [ google . cloud . bigquery . job . query . QueryPlanEntry ]] = None
186- created : Optional [ datetime .datetime ] = None
187- started : Optional [ datetime .datetime ] = None
188- ended : Optional [ datetime .datetime ] = None
211+ billing_project : str | None = None
212+ location : str | None = None
213+ job_id : str | None = None
214+ statement_type : str | None = None
215+ state : str | None = None
216+ query_plan : QueryPlanType = None
217+ created : datetime .datetime | None = None
218+ started : datetime .datetime | None = None
219+ ended : datetime .datetime | None = None
189220
190221 @classmethod
191222 def from_bqclient (
192- cls , event : google .cloud .bigquery ._job_helpers .QueryReceivedEvent
223+ cls ,
224+ event : google .cloud .bigquery ._job_helpers .QueryReceivedEvent ,
193225 ):
194226 return cls (
195227 billing_project = event .billing_project ,
@@ -208,21 +240,22 @@ def from_bqclient(
208240class BigQueryFinishedEvent (ExecutionRunning ):
209241 """Query finished successfully."""
210242
211- billing_project : Optional [ str ] = None
212- location : Optional [ str ] = None
213- query_id : Optional [ str ] = None
214- job_id : Optional [ str ] = None
215- destination : Optional [ google .cloud .bigquery .table .TableReference ] = None
216- total_rows : Optional [ int ] = None
217- total_bytes_processed : Optional [ int ] = None
218- slot_millis : Optional [ int ] = None
219- created : Optional [ datetime .datetime ] = None
220- started : Optional [ datetime .datetime ] = None
221- ended : Optional [ datetime .datetime ] = None
243+ billing_project : str | None = None
244+ location : str | None = None
245+ query_id : str | None = None
246+ job_id : str | None = None
247+ destination : google .cloud .bigquery .table .TableReference | None = None
248+ total_rows : int | None = None
249+ total_bytes_processed : int | None = None
250+ slot_millis : int | None = None
251+ created : datetime .datetime | None = None
252+ started : datetime .datetime | None = None
253+ ended : datetime .datetime | None = None
222254
223255 @classmethod
224256 def from_bqclient (
225- cls , event : google .cloud .bigquery ._job_helpers .QueryFinishedEvent
257+ cls ,
258+ event : google .cloud .bigquery ._job_helpers .QueryFinishedEvent ,
226259 ):
227260 return cls (
228261 billing_project = event .billing_project ,
0 commit comments