@@ -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 ,
@@ -173,6 +232,7 @@ def __init__(
173
232
location = None ,
174
233
try_credentials = None ,
175
234
):
235
+ global context
176
236
from google .api_core .exceptions import GoogleAPIError
177
237
from google .api_core .exceptions import ClientError
178
238
from pandas_gbq import auth
@@ -185,13 +245,20 @@ def __init__(
185
245
self .auth_local_webserver = auth_local_webserver
186
246
self .dialect = dialect
187
247
self .credentials_path = _get_credentials_file ()
188
- self .credentials , default_project = auth .get_credentials (
189
- private_key = private_key ,
190
- project_id = project_id ,
191
- reauth = reauth ,
192
- auth_local_webserver = auth_local_webserver ,
193
- try_credentials = try_credentials ,
194
- )
248
+
249
+ # Load credentials from cache.
250
+ self .credentials = context .credentials
251
+ default_project = context .project
252
+
253
+ # Credentials were explicitly asked for, so don't use the cache.
254
+ if private_key or reauth or not self .credentials :
255
+ self .credentials , default_project = auth .get_credentials (
256
+ private_key = private_key ,
257
+ project_id = project_id ,
258
+ reauth = reauth ,
259
+ auth_local_webserver = auth_local_webserver ,
260
+ try_credentials = try_credentials ,
261
+ )
195
262
196
263
if self .project_id is None :
197
264
self .project_id = default_project
@@ -201,6 +268,12 @@ def __init__(
201
268
"Could not determine project ID and one was not supplied."
202
269
)
203
270
271
+ # Cache the credentials if they haven't been set yet.
272
+ if context .credentials is None :
273
+ context .credentials = self .credentials
274
+ if context .project is None :
275
+ context .project = self .project_id
276
+
204
277
self .client = self .get_client ()
205
278
206
279
# BQ Queries costs $5 per TB. First 1 TB per month is free
0 commit comments