Skip to content

Commit 9eb9d77

Browse files
authored
ENH: Save BigQuery account credentials in a hidden user folder (#83)
* ENH: Save BigQuery account credentials in a hidden user folder instead of cwd * Revert version bump
1 parent dbfb4e9 commit 9eb9d77

File tree

4 files changed

+49
-5
lines changed

4 files changed

+49
-5
lines changed

docs/source/changelog.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Changelog
66

77
- :func:`read_gbq` now raises ``QueryTimeout`` if the request exceeds the ``query.timeoutMs`` value specified in the BigQuery configuration. (:issue:`76`)
88
- Environment variable ``PANDAS_GBQ_CREDENTIALS_FILE`` can now be used to override the default location where the BigQuery user account credentials are stored. (:issue:`86`)
9-
9+
- BigQuery user account credentials are now stored in an application-specific hidden user folder on the operating system. (:issue:`41`)
1010

1111
0.2.0 / 2017-07-24
1212
------------------

docs/source/intro.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ is possible with either user or service account credentials.
3737
Authentication via user account credentials is as simple as following the prompts in a browser window
3838
which will automatically open for you. You authenticate to the specified
3939
``BigQuery`` account using the product name ``pandas GBQ``.
40-
The remote authentication is supported via specifying ``auth_local_webserver`` in ``read_gbq``.
40+
The remote authentication is supported via the ``auth_local_webserver`` in ``read_gbq``. By default,
41+
account credentials are stored in an application-specific hidden user folder on the operating system. You
42+
can override the default credentials location via the ``PANDAS_GBQ_CREDENTIALS_FILE`` environment variable.
4143
Additional information on the authentication mechanism can be found
4244
`here <https://developers.google.com/identity/protocols/OAuth2#clientside/>`__.
4345

pandas_gbq/gbq.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ def __init__(self, project_id, reauth=False, verbose=False,
208208
self.private_key = private_key
209209
self.auth_local_webserver = auth_local_webserver
210210
self.dialect = dialect
211+
self.credentials_path = _get_credentials_file()
211212
self.credentials = self.get_credentials()
212213
self.service = self.get_service()
213214

@@ -279,8 +280,21 @@ def load_user_account_credentials(self):
279280
from google_auth_httplib2 import Request
280281
from google.oauth2.credentials import Credentials
281282

283+
# Use the default credentials location under ~/.config and the
284+
# equivalent directory on windows if the user has not specified a
285+
# credentials path.
286+
if not self.credentials_path:
287+
self.credentials_path = self.get_default_credentials_path()
288+
289+
# Previously, pandas-gbq saved user account credentials in the
290+
# current working directory. If the bigquery_credentials.dat file
291+
# exists in the current working directory, move the credentials to
292+
# the new default location.
293+
if os.path.isfile('bigquery_credentials.dat'):
294+
os.rename('bigquery_credentials.dat', self.credentials_path)
295+
282296
try:
283-
with open(_get_credentials_file()) as credentials_file:
297+
with open(self.credentials_path) as credentials_file:
284298
credentials_json = json.load(credentials_file)
285299
except (IOError, ValueError):
286300
return None
@@ -301,14 +315,41 @@ def load_user_account_credentials(self):
301315

302316
return _try_credentials(self.project_id, credentials)
303317

318+
def get_default_credentials_path(self):
319+
"""
320+
Gets the default path to the BigQuery credentials
321+
322+
.. versionadded 0.3.0
323+
324+
Returns
325+
-------
326+
Path to the BigQuery credentials
327+
"""
328+
329+
import os
330+
331+
if os.name == 'nt':
332+
config_path = os.environ['APPDATA']
333+
else:
334+
config_path = os.path.join(os.path.expanduser('~'), '.config')
335+
336+
config_path = os.path.join(config_path, 'pandas_gbq')
337+
338+
# Create a pandas_gbq directory in an application-specific hidden
339+
# user folder on the operating system.
340+
if not os.path.exists(config_path):
341+
os.makedirs(config_path)
342+
343+
return os.path.join(config_path, 'bigquery_credentials.dat')
344+
304345
def save_user_account_credentials(self, credentials):
305346
"""
306347
Saves user account credentials to a local file.
307348
308349
.. versionadded 0.2.0
309350
"""
310351
try:
311-
with open(_get_credentials_file(), 'w') as credentials_file:
352+
with open(self.credentials_path, 'w') as credentials_file:
312353
credentials_json = {
313354
'refresh_token': credentials.refresh_token,
314355
'id_token': credentials.id_token,
@@ -793,7 +834,7 @@ def delete_and_recreate_table(self, dataset_id, table_id, table_schema):
793834

794835
def _get_credentials_file():
795836
return os.environ.get(
796-
'PANDAS_GBQ_CREDENTIALS_FILE', 'bigquery_credentials.dat')
837+
'PANDAS_GBQ_CREDENTIALS_FILE')
797838

798839

799840
def _parse_data(schema, rows):

pandas_gbq/tests/test_gbq.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,7 @@ def setup_method(self, method):
13881388
# put here any instruction you want to be run *BEFORE* *EVERY* test
13891389
# is executed.
13901390

1391+
gbq.GbqConnector(_get_project_id(), auth_local_webserver=True)
13911392
self.dataset_prefix = _get_dataset_prefix_random()
13921393
clean_gbq_environment(self.dataset_prefix)
13931394
self.destination_table = "{0}{1}.{2}".format(self.dataset_prefix, "2",

0 commit comments

Comments
 (0)