diff --git a/doc/source/whatsnew/v0.18.2.txt b/doc/source/whatsnew/v0.18.2.txt index 64644bd9a7a26..ac5fceb672809 100644 --- a/doc/source/whatsnew/v0.18.2.txt +++ b/doc/source/whatsnew/v0.18.2.txt @@ -223,6 +223,7 @@ Other enhancements - A ``union_categorical`` function has been added for combining categoricals, see :ref:`Unioning Categoricals` (:issue:`13361`) - ``eval``'s upcasting rules for ``float32`` types have been updated to be more consistent with NumPy's rules. New behavior will not upcast to ``float64`` if you multiply a pandas ``float32`` object by a scalar float64. (:issue:`12388`) - ``Series`` has gained the properties ``.is_monotonic``, ``.is_monotonic_increasing``, ``.is_monotonic_decreasing``, similar to ``Index`` (:issue:`13336`) +- The ``.get_credentials()`` method of ``GbqConnector`` can now first try to fetch the default credentials for Google Compute Engine without the need to run ``OAuth2WebServerFlow`` - if private_key is not provided (:issue:`13577`) .. _whatsnew_0182.api: diff --git a/pandas/io/gbq.py b/pandas/io/gbq.py index 140f5cc6bb6e3..c26d8431ffe57 100644 --- a/pandas/io/gbq.py +++ b/pandas/io/gbq.py @@ -159,7 +159,33 @@ def get_credentials(self): if self.private_key: return self.get_service_account_credentials() else: - return self.get_user_account_credentials() + # Try to retrieve Application Default Credentials + credentials = self.get_application_default_credentials() + if not credentials: + credentials = self.get_user_account_credentials() + return credentials + + def get_application_default_credentials(self): + from oauth2client.client import GoogleCredentials + from oauth2client.client import AccessTokenRefreshError + from oauth2client.client import ApplicationDefaultCredentialsError + from apiclient.discovery import build + from apiclient.errors import HttpError + + credentials = None + try: + credentials = GoogleCredentials.get_application_default() + except ApplicationDefaultCredentialsError: + return None + # Check if the application has rights to the BigQuery project + bigquery_service = build('bigquery', 'v2', credentials=credentials) + job_collection = bigquery_service.jobs() + job_data = {'configuration': {'query': {'query': 'SELECT 1'}}} + try: + job_collection.insert(projectId=self.project_id, body=job_data).execute() + except (AccessTokenRefreshError, HttpError): + return None + return credentials def get_user_account_credentials(self): from oauth2client.client import OAuth2WebServerFlow @@ -576,7 +602,9 @@ def read_gbq(query, project_id=None, index_col=None, col_order=None, https://developers.google.com/api-client-library/python/apis/bigquery/v2 Authentication to the Google BigQuery service is via OAuth 2.0. - By default user account credentials are used. You will be asked to + By default "application default credentials" are used. + If default application credentials are not found or are restrictive - + User account credentials are used. You will be asked to grant permissions for product name 'pandas GBQ'. It is also posible to authenticate via service account credentials by using private_key parameter. @@ -672,7 +700,9 @@ def to_gbq(dataframe, destination_table, project_id, chunksize=10000, https://developers.google.com/api-client-library/python/apis/bigquery/v2 Authentication to the Google BigQuery service is via OAuth 2.0. - By default user account credentials are used. You will be asked to + By default "application default credentials" are used. + If default application credentials are not found or are restrictive - + User account credentials are used. You will be asked to grant permissions for product name 'pandas GBQ'. It is also posible to authenticate via service account credentials by using private_key parameter. diff --git a/pandas/io/tests/test_gbq.py b/pandas/io/tests/test_gbq.py index 278c5d7215624..0cbf682480678 100644 --- a/pandas/io/tests/test_gbq.py +++ b/pandas/io/tests/test_gbq.py @@ -80,8 +80,10 @@ def _test_imports(): from apiclient.discovery import build # noqa from apiclient.errors import HttpError # noqa + from oauth2client.client import GoogleCredentials # noqa from oauth2client.client import OAuth2WebServerFlow # noqa from oauth2client.client import AccessTokenRefreshError # noqa + from oauth2client.client import ApplicationDefaultCredentialsError # noqa from oauth2client.file import Storage # noqa from oauth2client.tools import run_flow # noqa