|
5 | 5 | from __future__ import annotations |
6 | 6 |
|
7 | 7 | import concurrent.futures |
| 8 | +import functools |
8 | 9 | import logging |
9 | 10 | from typing import Optional |
10 | 11 |
|
| 12 | +import google.auth.exceptions |
11 | 13 | from google.cloud import bigquery |
12 | 14 |
|
13 | 15 | import pandas_gbq.exceptions |
@@ -78,6 +80,26 @@ def _wait_for_query_job( |
78 | 80 | connector.process_http_error(ex) |
79 | 81 |
|
80 | 82 |
|
| 83 | +def try_query(connector, query_fn): |
| 84 | + try: |
| 85 | + logger.debug("Requesting query... ") |
| 86 | + return query_fn() |
| 87 | + except concurrent.futures.TimeoutError as ex: |
| 88 | + raise pandas_gbq.exceptions.QueryTimeout("Reason: {0}".format(ex)) |
| 89 | + except (google.auth.exceptions.RefreshError, ValueError) as ex: |
| 90 | + if connector.private_key: |
| 91 | + raise pandas_gbq.exceptions.AccessDenied( |
| 92 | + f"The service account credentials are not valid: {ex}" |
| 93 | + ) |
| 94 | + else: |
| 95 | + raise pandas_gbq.exceptions.AccessDenied( |
| 96 | + "The credentials have been revoked or expired, " |
| 97 | + f"please re-run the application to re-authorize: {ex}" |
| 98 | + ) |
| 99 | + except connector.http_error as ex: |
| 100 | + connector.process_http_error(ex) |
| 101 | + |
| 102 | + |
81 | 103 | def query_and_wait( |
82 | 104 | connector, |
83 | 105 | client: bigquery.Client, |
@@ -122,29 +144,17 @@ def query_and_wait( |
122 | 144 | Result iterator from which we can download the results in the |
123 | 145 | desired format (pandas.DataFrame). |
124 | 146 | """ |
125 | | - from google.auth.exceptions import RefreshError |
126 | | - |
127 | | - try: |
128 | | - logger.debug("Requesting query... ") |
129 | | - query_reply = client.query( |
| 147 | + query_reply = try_query( |
| 148 | + connector, |
| 149 | + functools.partial( |
| 150 | + client.query, |
130 | 151 | query, |
131 | 152 | job_config=job_config, |
132 | 153 | location=location, |
133 | 154 | project=project_id, |
134 | | - ) |
135 | | - logger.debug("Query running...") |
136 | | - except (RefreshError, ValueError) as ex: |
137 | | - if connector.private_key: |
138 | | - raise pandas_gbq.exceptions.AccessDenied( |
139 | | - f"The service account credentials are not valid: {ex}" |
140 | | - ) |
141 | | - else: |
142 | | - raise pandas_gbq.exceptions.AccessDenied( |
143 | | - "The credentials have been revoked or expired, " |
144 | | - f"please re-run the application to re-authorize: {ex}" |
145 | | - ) |
146 | | - except connector.http_error as ex: |
147 | | - connector.process_http_error(ex) |
| 155 | + ), |
| 156 | + ) |
| 157 | + logger.debug("Query running...") |
148 | 158 |
|
149 | 159 | job_id = query_reply.job_id |
150 | 160 | logger.debug("Job ID: %s" % job_id) |
@@ -173,3 +183,30 @@ def query_and_wait( |
173 | 183 | return query_reply.result(max_results=max_results) |
174 | 184 | except connector.http_error as ex: |
175 | 185 | connector.process_http_error(ex) |
| 186 | + |
| 187 | + |
| 188 | +def query_and_wait_via_client_library( |
| 189 | + connector, |
| 190 | + client: bigquery.Client, |
| 191 | + query: str, |
| 192 | + *, |
| 193 | + job_config: bigquery.QueryJobConfig, |
| 194 | + location: Optional[str], |
| 195 | + project_id: Optional[str], |
| 196 | + max_results: Optional[int], |
| 197 | + timeout_ms: Optional[int], |
| 198 | +): |
| 199 | + rows_iter = try_query( |
| 200 | + connector, |
| 201 | + functools.partial( |
| 202 | + client.query_and_wait, |
| 203 | + query, |
| 204 | + job_config=job_config, |
| 205 | + location=location, |
| 206 | + project=project_id, |
| 207 | + max_results=max_results, |
| 208 | + wait_timeout=timeout_ms / 1000.0 if timeout_ms else None, |
| 209 | + ), |
| 210 | + ) |
| 211 | + logger.debug("Query done.\n") |
| 212 | + return rows_iter |
0 commit comments