6
6
import time
7
7
import sys
8
8
9
- import google .auth
10
- import google .oauth2 .credentials
11
- from google .oauth2 import service_account
12
- import google_auth_httplib2
13
- from google_auth_oauthlib import flow
14
- import httplib2
15
9
import numpy as np
16
10
17
11
from distutils .version import StrictVersion
18
12
from pandas import compat , DataFrame , concat
19
13
from pandas .compat import lzip , bytes_to_str
20
14
21
15
16
+ def _check_google_client_version ():
17
+
18
+ try :
19
+ import pkg_resources
20
+
21
+ except ImportError :
22
+ raise ImportError ('Could not import pkg_resources (setuptools).' )
23
+
24
+ # Version 1.6.0 is the first version to support google-auth.
25
+ # https://github.com/google/google-api-python-client/blob/master/CHANGELOG
26
+ google_api_minimum_version = '1.6.0'
27
+
28
+ _GOOGLE_API_CLIENT_VERSION = pkg_resources .get_distribution (
29
+ 'google-api-python-client' ).version
30
+
31
+ if (StrictVersion (_GOOGLE_API_CLIENT_VERSION ) <
32
+ StrictVersion (google_api_minimum_version )):
33
+ raise ImportError ("pandas requires google-api-python-client >= {0} "
34
+ "for Google BigQuery support, "
35
+ "current version {1}"
36
+ .format (google_api_minimum_version ,
37
+ _GOOGLE_API_CLIENT_VERSION ))
38
+
39
+
40
+ def _test_google_api_imports ():
41
+
42
+ try :
43
+ import httplib2 # noqa
44
+ from googleapiclient .discovery import build # noqa
45
+ from googleapiclient .errors import HttpError # noqa
46
+ import google .auth #noqa
47
+ from google_auth_oauthlib .flow import InstalledAppFlow #noqa
48
+ import google_auth_httplib2 #noqa
49
+ except ImportError as e :
50
+ raise ImportError ("Missing module required for Google BigQuery "
51
+ "support: {0}" .format (str (e )))
52
+
53
+
54
+ def _try_credentials (project_id , credentials ):
55
+ import httplib2
56
+ from googleapiclient .discovery import build
57
+ import googleapiclient .errors
58
+ from google_auth_httplib2 import AuthorizedHttp
59
+
60
+ if credentials is None :
61
+ return None
62
+
63
+ http = httplib2 .Http ()
64
+ try :
65
+ http = AuthorizedHttp (credentials , http = http )
66
+ bigquery_service = build ('bigquery' , 'v2' , http = http )
67
+ # Check if the application has rights to the BigQuery project
68
+ jobs = bigquery_service .jobs ()
69
+ job_data = {'configuration' : {'query' : {'query' : 'SELECT 1' }}}
70
+ jobs .insert (projectId = project_id , body = job_data ).execute ()
71
+ return credentials
72
+ except googleapiclient .errors .Error :
73
+ return None
74
+
75
+
22
76
class InvalidPrivateKeyFormat (ValueError ):
23
77
"""
24
78
Raised when provided private key has invalid format.
@@ -109,7 +163,7 @@ class GbqConnector(object):
109
163
scope = 'https://www.googleapis.com/auth/bigquery'
110
164
111
165
def __init__ (self , project_id , reauth = False , verbose = False ,
112
- private_key = None , auth_local_webserver = True ,
166
+ private_key = None , auth_local_webserver = False ,
113
167
dialect = 'legacy' ):
114
168
self .project_id = project_id
115
169
self .reauth = reauth
@@ -120,7 +174,7 @@ def __init__(self, project_id, reauth=False, verbose=False,
120
174
auth_local_webserver = auth_local_webserver )
121
175
self .service = self .get_service ()
122
176
123
- def get_credentials (self , auth_local_webserver = True ):
177
+ def get_credentials (self , auth_local_webserver = False ):
124
178
if self .private_key :
125
179
return self .get_service_account_credentials ()
126
180
else :
@@ -131,27 +185,6 @@ def get_credentials(self, auth_local_webserver=True):
131
185
auth_local_webserver = auth_local_webserver )
132
186
return credentials
133
187
134
- def try_credentials (self , credentials ):
135
- try :
136
- from googleapiclient .discovery import build
137
- except :
138
- from apiclient .discovery import build
139
-
140
- if credentials is None :
141
- return None
142
-
143
- http = httplib2 .Http ()
144
- try :
145
- http = google_auth_httplib2 .AuthorizedHttp (credentials , http = http )
146
- bigquery_service = build ('bigquery' , 'v2' , http = http )
147
- # Check if the application has rights to the BigQuery project
148
- jobs = bigquery_service .jobs ()
149
- job_data = {'configuration' : {'query' : {'query' : 'SELECT 1' }}}
150
- jobs .insert (projectId = self .project_id , body = job_data ).execute ()
151
- return credentials
152
- except :
153
- return None
154
-
155
188
def get_application_default_credentials (self ):
156
189
"""
157
190
This method tries to retrieve the "default application credentials".
@@ -172,8 +205,15 @@ def get_application_default_credentials(self):
172
205
from the environment. Or, the retrieved credentials do not
173
206
have access to the project (self.project_id) on BigQuery.
174
207
"""
175
- credentials , _ = google .auth .default (scopes = [self .scope ])
176
- return self .try_credentials (credentials )
208
+ import google .auth
209
+ from google .auth .exceptions import DefaultCredentialsError
210
+
211
+ try :
212
+ credentials , _ = google .auth .default (scopes = [self .scope ])
213
+ except DefaultCredentialsError :
214
+ return None
215
+
216
+ return _try_credentials (self .project_id , credentials )
177
217
178
218
def load_user_account_credentials (self ):
179
219
"""
@@ -193,55 +233,68 @@ def load_user_account_credentials(self):
193
233
credentials do not have access to the project (self.project_id)
194
234
on BigQuery.
195
235
"""
196
- with open ('bigquery_credentials.dat' ) as credentials_file :
197
- credentials_json = json .load (credentials_file )
198
- credentials = google .oauth2 .credentials .Credentials (
199
- token = credentials_json .get ('access_token' ),
200
- refresh_token = credentials_json .get ('refresh_token' ),
201
- id_token = credentials_json .get ('id_token' ),
202
- token_uri = credentials_json .get ('token_uri' ),
203
- client_id = credentials_json .get ('client_id' ),
204
- client_secret = credentials_json .get ('client_secret' ),
205
- scopes = credentials_json .get ('scopes' ))
236
+ import httplib2
237
+ from google_auth_httplib2 import Request
238
+ from google .oauth2 .credentials import Credentials
206
239
207
- # Refresh the token before trying to use it.
208
- http = httplib2 .Http ()
209
- request = google_auth_httplib2 .Request (http )
210
- credentials .refresh (request )
240
+ try :
241
+ with open ('bigquery_credentials.dat' ) as credentials_file :
242
+ credentials_json = json .load (credentials_file )
243
+ except (IOError , ValueError ):
244
+ return None
245
+
246
+ credentials = Credentials (
247
+ token = credentials_json .get ('access_token' ),
248
+ refresh_token = credentials_json .get ('refresh_token' ),
249
+ id_token = credentials_json .get ('id_token' ),
250
+ token_uri = credentials_json .get ('token_uri' ),
251
+ client_id = credentials_json .get ('client_id' ),
252
+ client_secret = credentials_json .get ('client_secret' ),
253
+ scopes = credentials_json .get ('scopes' ))
211
254
212
- return self .try_credentials (credentials )
255
+ # Refresh the token before trying to use it.
256
+ http = httplib2 .Http ()
257
+ request = Request (http )
258
+ credentials .refresh (request )
259
+
260
+ return _try_credentials (self .project_id , credentials )
213
261
214
262
def save_user_account_credentials (self , credentials ):
215
263
"""
216
264
Saves user account credentials to a local file.
217
265
"""
218
- with open ('bigquery_credentials.dat' , 'wb' ) as credentials_file :
219
- credentials_json = {
220
- 'refresh_token' : credentials .refresh_token ,
221
- 'id_token' : credentials .id_token ,
222
- 'token_uri' : credentials .token_uri ,
223
- 'client_id' : credentials .client_id ,
224
- 'client_secret' : credentials .client_secret ,
225
- 'scopes' : credentials .scopes ,
226
- }
227
- json .dump (credentials_file , credentials_json )
228
-
229
- def get_user_account_credentials (self , auth_local_webserver = True ):
266
+ try :
267
+ with open ('bigquery_credentials.dat' , 'w' ) as credentials_file :
268
+ credentials_json = {
269
+ 'refresh_token' : credentials .refresh_token ,
270
+ 'id_token' : credentials .id_token ,
271
+ 'token_uri' : credentials .token_uri ,
272
+ 'client_id' : credentials .client_id ,
273
+ 'client_secret' : credentials .client_secret ,
274
+ 'scopes' : credentials .scopes ,
275
+ }
276
+ json .dump (credentials_json , credentials_file )
277
+ except IOError :
278
+ self ._print ('Unable to save credentials.' )
279
+
280
+ def get_user_account_credentials (self , auth_local_webserver = False ):
230
281
"""Gets user account credentials.
231
282
232
283
This method authenticates using user credentials, either loading saved
233
284
credentials from a file or by going through the OAuth flow.
234
285
235
286
Parameters
236
287
----------
237
- auth_local_webserver : boolean (default True )
288
+ auth_local_webserver : boolean (default False )
238
289
Use a local webserver to complete the OAuth flow.
239
290
240
291
Returns
241
292
-------
242
293
GoogleCredentials : credentials
243
294
Credentials for the user with BigQuery access.
244
295
"""
296
+ from google_auth_oauthlib .flow import InstalledAppFlow
297
+
245
298
credentials = self .load_user_account_credentials ()
246
299
247
300
client_config = {
@@ -256,7 +309,7 @@ def get_user_account_credentials(self, auth_local_webserver=True):
256
309
}
257
310
258
311
if credentials is None or self .reauth :
259
- app_flow = flow . InstalledAppFlow .from_client_config (
312
+ app_flow = InstalledAppFlow .from_client_config (
260
313
client_config , scopes = [self .scope ])
261
314
262
315
if auth_local_webserver :
@@ -269,6 +322,9 @@ def get_user_account_credentials(self, auth_local_webserver=True):
269
322
return credentials
270
323
271
324
def get_service_account_credentials (self ):
325
+ import httplib2
326
+ from google_auth_httplib2 import Request
327
+ from google .oauth2 .service_account import Credentials
272
328
from os .path import isfile
273
329
274
330
try :
@@ -286,13 +342,12 @@ def get_service_account_credentials(self):
286
342
json_key ['private_key' ] = bytes (
287
343
json_key ['private_key' ], 'UTF-8' )
288
344
289
- credentials = service_account .Credentials .from_service_account_info (
290
- json_key )
345
+ credentials = Credentials .from_service_account_info (json_key )
291
346
credentials = credentials .with_scopes ([self .scope ])
292
347
293
348
# Refresh the token before trying to use it.
294
349
http = httplib2 .Http ()
295
- request = google_auth_httplib2 . Request (http )
350
+ request = Request (http )
296
351
credentials .refresh (request )
297
352
298
353
return credentials
@@ -333,13 +388,11 @@ def sizeof_fmt(num, suffix='B'):
333
388
334
389
def get_service (self ):
335
390
import httplib2
336
- try :
337
- from googleapiclient .discovery import build
338
- except :
339
- from apiclient .discovery import build
391
+ from google_auth_httplib2 import AuthorizedHttp
392
+ from googleapiclient .discovery import build
340
393
341
394
http = httplib2 .Http ()
342
- http = google_auth_httplib2 . AuthorizedHttp (
395
+ http = AuthorizedHttp (
343
396
self .credentials , http = http )
344
397
bigquery_service = build ('bigquery' , 'v2' , http = http )
345
398
@@ -650,7 +703,7 @@ def _parse_entry(field_value, field_type):
650
703
651
704
def read_gbq (query , project_id = None , index_col = None , col_order = None ,
652
705
reauth = False , verbose = True , private_key = None ,
653
- auth_local_webserver = True , dialect = 'legacy' , ** kwargs ):
706
+ auth_local_webserver = False , dialect = 'legacy' , ** kwargs ):
654
707
r"""Load data from Google BigQuery.
655
708
656
709
The main method a user calls to execute a Query in Google BigQuery
@@ -694,7 +747,7 @@ def read_gbq(query, project_id=None, index_col=None, col_order=None,
694
747
Service account private key in JSON format. Can be file path
695
748
or string contents. This is useful for remote server
696
749
authentication (eg. jupyter iPython notebook on remote host)
697
- auth_local_webserver : boolean (default True )
750
+ auth_local_webserver : boolean (default False )
698
751
Use a local webserver when getting user credentials to handle
699
752
OAuth authorization flow redirects.
700
753
@@ -779,7 +832,8 @@ def read_gbq(query, project_id=None, index_col=None, col_order=None,
779
832
780
833
781
834
def to_gbq (dataframe , destination_table , project_id , chunksize = 10000 ,
782
- verbose = True , reauth = False , if_exists = 'fail' , private_key = None ):
835
+ verbose = True , reauth = False , if_exists = 'fail' , private_key = None ,
836
+ auth_local_webserver = False ):
783
837
"""Write a DataFrame to a Google BigQuery table.
784
838
785
839
The main method a user calls to export pandas DataFrame contents to
@@ -826,6 +880,9 @@ def to_gbq(dataframe, destination_table, project_id, chunksize=10000,
826
880
Service account private key in JSON format. Can be file path
827
881
or string contents. This is useful for remote server
828
882
authentication (eg. jupyter iPython notebook on remote host)
883
+ auth_local_webserver : boolean (default False)
884
+ Use a local webserver when getting user credentials to handle
885
+ OAuth authorization flow redirects.
829
886
"""
830
887
831
888
if if_exists not in ('fail' , 'replace' , 'append' ):
@@ -835,8 +892,9 @@ def to_gbq(dataframe, destination_table, project_id, chunksize=10000,
835
892
raise NotFoundException (
836
893
"Invalid Table Name. Should be of the form 'datasetId.tableId' " )
837
894
838
- connector = GbqConnector (project_id , reauth = reauth , verbose = verbose ,
839
- private_key = private_key )
895
+ connector = GbqConnector (
896
+ project_id , reauth = reauth , verbose = verbose , private_key = private_key ,
897
+ auth_local_webserver = auth_local_webserver )
840
898
dataset_id , table_id = destination_table .rsplit ('.' , 1 )
841
899
842
900
table = _Table (project_id , dataset_id , reauth = reauth ,
0 commit comments