Skip to content

Commit 3f3192f

Browse files
authored
[BUG] skip _try_credentials check for to_gbq (#207)
* [BUG] skip _try_credentials check for to_gbq Don't do a query read when all you need to write is write credentials. * Fix auth tests. * Fix mock for Python 2.7.
1 parent 294c924 commit 3f3192f

File tree

6 files changed

+62
-14
lines changed

6 files changed

+62
-14
lines changed

docs/source/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Changelog
1212
(:issue:`128`)
1313
- Reduced verbosity of logging from ``read_gbq``, particularly for short
1414
queries. (:issue:`201`)
15+
- Avoid ``SELECT 1`` query when running ``to_gbq``. (:issue:`202`)
1516

1617
.. _changelog-0.6.0:
1718

pandas_gbq/auth.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,28 @@
1616

1717

1818
def get_credentials(
19-
private_key=None, project_id=None, reauth=False, auth_local_webserver=False
19+
private_key=None,
20+
project_id=None,
21+
reauth=False,
22+
auth_local_webserver=False,
23+
try_credentials=None,
2024
):
25+
if try_credentials is None:
26+
try_credentials = _try_credentials
27+
2128
if private_key:
2229
return get_service_account_credentials(private_key)
2330

2431
# Try to retrieve Application Default Credentials
2532
credentials, default_project = get_application_default_credentials(
26-
project_id=project_id
33+
try_credentials, project_id=project_id
2734
)
2835

2936
if credentials:
3037
return credentials, default_project
3138

3239
credentials = get_user_account_credentials(
40+
try_credentials,
3341
project_id=project_id,
3442
reauth=reauth,
3543
auth_local_webserver=auth_local_webserver,
@@ -79,7 +87,7 @@ def get_service_account_credentials(private_key):
7987
)
8088

8189

82-
def get_application_default_credentials(project_id=None):
90+
def get_application_default_credentials(try_credentials, project_id=None):
8391
"""
8492
This method tries to retrieve the "default application credentials".
8593
This could be useful for running code on Google Cloud Platform.
@@ -111,10 +119,11 @@ def get_application_default_credentials(project_id=None):
111119
# used with BigQuery. For example, we could be running on a GCE instance
112120
# that does not allow the BigQuery scopes.
113121
billing_project = project_id or default_project
114-
return _try_credentials(billing_project, credentials), billing_project
122+
return try_credentials(billing_project, credentials), billing_project
115123

116124

117125
def get_user_account_credentials(
126+
try_credentials,
118127
project_id=None,
119128
reauth=False,
120129
auth_local_webserver=False,
@@ -151,7 +160,9 @@ def get_user_account_credentials(
151160
os.rename("bigquery_credentials.dat", credentials_path)
152161

153162
credentials = load_user_account_credentials(
154-
project_id=project_id, credentials_path=credentials_path
163+
try_credentials,
164+
project_id=project_id,
165+
credentials_path=credentials_path,
155166
)
156167

157168
client_config = {
@@ -187,7 +198,9 @@ def get_user_account_credentials(
187198
return credentials
188199

189200

190-
def load_user_account_credentials(project_id=None, credentials_path=None):
201+
def load_user_account_credentials(
202+
try_credentials, project_id=None, credentials_path=None
203+
):
191204
"""
192205
Loads user account credentials from a local file.
193206
@@ -230,7 +243,7 @@ def load_user_account_credentials(project_id=None, credentials_path=None):
230243
request = google.auth.transport.requests.Request()
231244
credentials.refresh(request)
232245

233-
return _try_credentials(project_id, credentials)
246+
return try_credentials(project_id, credentials)
234247

235248

236249
def get_default_credentials_path():

pandas_gbq/gbq.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ def __init__(
171171
auth_local_webserver=False,
172172
dialect="legacy",
173173
location=None,
174+
try_credentials=None,
174175
):
175176
from google.api_core.exceptions import GoogleAPIError
176177
from google.api_core.exceptions import ClientError
@@ -189,6 +190,7 @@ def __init__(
189190
project_id=project_id,
190191
reauth=reauth,
191192
auth_local_webserver=auth_local_webserver,
193+
try_credentials=try_credentials,
192194
)
193195

194196
if self.project_id is None:
@@ -804,6 +806,9 @@ def to_gbq(
804806
private_key=private_key,
805807
auth_local_webserver=auth_local_webserver,
806808
location=location,
809+
# Avoid reads when writing tables.
810+
# https://github.com/pydata/pandas-gbq/issues/202
811+
try_credentials=lambda project, creds: creds,
807812
)
808813
dataset_id, table_id = destination_table.rsplit(".", 1)
809814

tests/system/test_auth.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,13 @@ def test_get_application_default_credentials_does_not_throw_error():
6666
with mock.patch(
6767
"google.auth.default", side_effect=DefaultCredentialsError()
6868
):
69-
credentials, _ = auth.get_application_default_credentials()
69+
credentials, _ = auth.get_application_default_credentials(
70+
try_credentials=auth._try_credentials
71+
)
7072
else:
71-
credentials, _ = auth.get_application_default_credentials()
73+
credentials, _ = auth.get_application_default_credentials(
74+
try_credentials=auth._try_credentials
75+
)
7276
assert credentials is None
7377

7478

@@ -77,7 +81,9 @@ def test_get_application_default_credentials_returns_credentials():
7781
pytest.skip("Cannot get default_credentials " "from the environment!")
7882
from google.auth.credentials import Credentials
7983

80-
credentials, default_project = auth.get_application_default_credentials()
84+
credentials, default_project = auth.get_application_default_credentials(
85+
try_credentials=auth._try_credentials
86+
)
8187

8288
assert isinstance(credentials, Credentials)
8389
assert default_project is not None
@@ -88,7 +94,9 @@ def test_get_user_account_credentials_bad_file_returns_credentials():
8894
from google.auth.credentials import Credentials
8995

9096
with mock.patch("__main__.open", side_effect=IOError()):
91-
credentials = auth.get_user_account_credentials()
97+
credentials = auth.get_user_account_credentials(
98+
try_credentials=auth._try_credentials
99+
)
92100
assert isinstance(credentials, Credentials)
93101

94102

@@ -97,7 +105,9 @@ def test_get_user_account_credentials_returns_credentials(project_id):
97105
from google.auth.credentials import Credentials
98106

99107
credentials = auth.get_user_account_credentials(
100-
project_id=project_id, auth_local_webserver=True
108+
project_id=project_id,
109+
auth_local_webserver=True,
110+
try_credentials=auth._try_credentials,
101111
)
102112
assert isinstance(credentials, Credentials)
103113

@@ -107,6 +117,9 @@ def test_get_user_account_credentials_reauth_returns_credentials(project_id):
107117
from google.auth.credentials import Credentials
108118

109119
credentials = auth.get_user_account_credentials(
110-
project_id=project_id, auth_local_webserver=True, reauth=True
120+
project_id=project_id,
121+
auth_local_webserver=True,
122+
reauth=True,
123+
try_credentials=auth._try_credentials,
111124
)
112125
assert isinstance(credentials, Credentials)

tests/unit/test_auth.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ def mock_default_credentials(scopes=None, request=None):
9595
google.auth.credentials.Credentials
9696
)
9797

98-
def mock_load_credentials(project_id=None, credentials_path=None):
98+
def mock_load_credentials(
99+
try_credentials, project_id=None, credentials_path=None
100+
):
99101
return mock_user_credentials
100102

101103
monkeypatch.setattr(

tests/unit/test_gbq.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def mock_bigquery_client(monkeypatch):
4646
# Mock table creation.
4747
mock_client.get_table.side_effect = NotFound("nope")
4848
monkeypatch.setattr(gbq.GbqConnector, "get_client", lambda _: mock_client)
49+
return mock_client
4950

5051

5152
def mock_none_credentials(*args, **kwargs):
@@ -210,6 +211,19 @@ def test_to_gbq_with_verbose_old_pandas_no_warnings(recwarn, min_bq_version):
210211
assert len(recwarn) == 0
211212

212213

214+
def test_to_gbq_doesnt_run_query(
215+
recwarn, mock_bigquery_client, min_bq_version
216+
):
217+
try:
218+
gbq.to_gbq(
219+
DataFrame([[1]]), "dataset.tablename", project_id="my-project"
220+
)
221+
except gbq.TableCreationError:
222+
pass
223+
224+
mock_bigquery_client.query.assert_not_called()
225+
226+
213227
def test_read_gbq_with_no_project_id_given_should_fail(monkeypatch):
214228
from pandas_gbq import auth
215229

0 commit comments

Comments
 (0)