@@ -162,6 +162,65 @@ class TableCreationError(ValueError):
162
162
pass
163
163
164
164
165
+ class Context (object ):
166
+ """Storage for objects to be used throughout a session.
167
+
168
+ A Context object is initialized when the ``pandas_gbq`` module is
169
+ imported, and can be found at :attr:`pandas_gbq.context`.
170
+ """
171
+
172
+ def __init__ (self ):
173
+ self ._credentials = None
174
+ self ._project = None
175
+
176
+ @property
177
+ def credentials (self ):
178
+ """google.auth.credentials.Credentials: Credentials to use for Google
179
+ APIs.
180
+
181
+ Note:
182
+ These credentials are automatically cached in memory by calls to
183
+ :func:`pandas_gbq.read_gbq` and :func:`pandas_gbq.to_gbq`. To
184
+ manually set the credentials, construct an
185
+ :class:`google.auth.credentials.Credentials` object and set it as
186
+ the context credentials as demonstrated in the example below. See
187
+ `auth docs`_ for more information on obtaining credentials.
188
+ Example:
189
+ Manually setting the context credentials:
190
+ >>> import pandas_gbq
191
+ >>> from google.oauth2 import service_account
192
+ >>> credentials = (service_account
193
+ ... .Credentials.from_service_account_file(
194
+ ... '/path/to/key.json'))
195
+ >>> pandas_gbq.context.credentials = credentials
196
+ .. _auth docs: http://google-auth.readthedocs.io
197
+ /en/latest/user-guide.html#obtaining-credentials
198
+ """
199
+ return self ._credentials
200
+
201
+ @credentials .setter
202
+ def credentials (self , value ):
203
+ self ._credentials = value
204
+
205
+ @property
206
+ def project (self ):
207
+ """str: Default project to use for calls to Google APIs.
208
+ Example:
209
+ Manually setting the context project:
210
+ >>> import pandas_gbq
211
+ >>> pandas_gbq.context.project = 'my-project'
212
+ """
213
+ return self ._project
214
+
215
+ @project .setter
216
+ def project (self , value ):
217
+ self ._project = value
218
+
219
+
220
+ # Create an empty context, used to cache credentials.
221
+ context = Context ()
222
+
223
+
165
224
class GbqConnector (object ):
166
225
def __init__ (
167
226
self ,
@@ -172,6 +231,7 @@ def __init__(
172
231
dialect = "legacy" ,
173
232
location = None ,
174
233
):
234
+ global context
175
235
from google .api_core .exceptions import GoogleAPIError
176
236
from google .api_core .exceptions import ClientError
177
237
from pandas_gbq import auth
@@ -184,12 +244,19 @@ def __init__(
184
244
self .auth_local_webserver = auth_local_webserver
185
245
self .dialect = dialect
186
246
self .credentials_path = _get_credentials_file ()
187
- self .credentials , default_project = auth .get_credentials (
188
- private_key = private_key ,
189
- project_id = project_id ,
190
- reauth = reauth ,
191
- auth_local_webserver = auth_local_webserver ,
192
- )
247
+
248
+ # Load credentials from cache.
249
+ self .credentials = context .credentials
250
+ default_project = context .project
251
+
252
+ # Credentials were explicitly asked for, so don't use the cache.
253
+ if private_key or reauth or not self .credentials :
254
+ self .credentials , default_project = auth .get_credentials (
255
+ private_key = private_key ,
256
+ project_id = project_id ,
257
+ reauth = reauth ,
258
+ auth_local_webserver = auth_local_webserver ,
259
+ )
193
260
194
261
if self .project_id is None :
195
262
self .project_id = default_project
@@ -199,6 +266,12 @@ def __init__(
199
266
"Could not determine project ID and one was not supplied."
200
267
)
201
268
269
+ # Cache the credentials if they haven't been set yet.
270
+ if context .credentials is None :
271
+ context .credentials = self .credentials
272
+ if context .project is None :
273
+ context .project = self .project_id
274
+
202
275
self .client = self .get_client ()
203
276
204
277
# BQ Queries costs $5 per TB. First 1 TB per month is free
0 commit comments