Skip to content

Commit 6dbac6b

Browse files
authored
Add new get_hardware_engine_and_authenticate_user (#6485)
* Created using Colaboratory * Created using Colaboratory * add new public api for getting a prod engine instance which does authentication * add new public api for getting a prod engine instance which does authentication * expose authenticate_user to notebook * add coverage
1 parent d75d43f commit 6dbac6b

File tree

2 files changed

+89
-24
lines changed

2 files changed

+89
-24
lines changed

cirq-google/cirq_google/engine/qcs_notebook.py

+48-23
Original file line numberDiff line numberDiff line change
@@ -82,32 +82,14 @@ def get_qcs_objects_for_notebook(
8282
ValueError: if processor_id is not specified and no processors are available.
8383
"""
8484

85-
# Check for Google Application Default Credentials and run
86-
# interactive login if the notebook is executed in Colab. In
87-
# case the notebook is executed in Jupyter notebook or other
88-
# IPython runtimes, no interactive login is provided, it is
89-
# assumed that the `GOOGLE_APPLICATION_CREDENTIALS` env var is
90-
# set or `gcloud auth application-default login` was executed
91-
# already. For more information on using Application Default Credentials
92-
# see https://cloud.google.com/docs/authentication/production
93-
# Attempt to connect to the Quantum Engine API, and use a simulator if unable to connect.
9485
if not virtual:
9586
# Set up auth
9687
try:
97-
from google.colab import auth
98-
except ImportError:
99-
print("Not running in a colab kernel. Will use Application Default Credentials.")
100-
else:
101-
print("Getting OAuth2 credentials.")
102-
print("Press enter after entering the verification code.")
103-
try:
104-
a = auth.authenticate_user(clear_output=False)
105-
print(a)
106-
print("Authentication complete.")
107-
except Exception as exc:
108-
print(f"Authentication failed: {exc}")
109-
print("Using virtual engine instead.")
110-
virtual = True
88+
authenticate_user()
89+
except Exception as exc:
90+
print(f"Authentication failed: {exc}")
91+
print("Using virtual engine instead.")
92+
virtual = True
11193

11294
if not virtual:
11395
# Set up production engine
@@ -152,3 +134,46 @@ def get_qcs_objects_for_notebook(
152134
processor_id=processor_id,
153135
is_simulator=is_simulator,
154136
)
137+
138+
139+
def authenticate_user(clear_output: bool = False) -> None:
140+
"""Authenticates on Google Cloud.
141+
142+
Args:
143+
clear_output: Optional bool for whether to clear output before
144+
authenticating. Defaults to false.
145+
146+
Returns:
147+
None.
148+
149+
Raises:
150+
Exception: if authentication fails.
151+
"""
152+
153+
# Check for Google Application Default Credentials and run
154+
# interactive login if the notebook is executed in Colab. In
155+
# case the notebook is executed in Jupyter notebook or other
156+
# IPython runtimes, no interactive login is provided, it is
157+
# assumed that the `GOOGLE_APPLICATION_CREDENTIALS` env var is
158+
# set or `gcloud auth application-default login` was executed
159+
# already. For more information on using Application Default Credentials
160+
# see https://cloud.google.com/docs/authentication/production
161+
# Attempt to connect to the Quantum Engine API, and use a simulator if unable to connect.
162+
try:
163+
from google.colab import auth
164+
except ImportError:
165+
print("Not running in a colab kernel. Will use Application Default Credentials.")
166+
return
167+
168+
try:
169+
print("Getting OAuth2 credentials.")
170+
print("Press enter after entering the verification code.")
171+
a = auth.authenticate_user(clear_output=clear_output)
172+
print(a)
173+
print("Authentication complete.")
174+
except Exception as exc:
175+
print(
176+
"Authentication failed, you may not have permission to access a"
177+
+ " hardware Engine. Use a virtual Engine instead."
178+
)
179+
raise exc

cirq-google/cirq_google/engine/qcs_notebook_test.py

+41-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
import pytest
1818

1919
import cirq_google as cg
20-
from cirq_google.engine.qcs_notebook import get_qcs_objects_for_notebook, QCSObjectsForNotebook
20+
from cirq_google.engine.qcs_notebook import (
21+
get_qcs_objects_for_notebook,
22+
QCSObjectsForNotebook,
23+
authenticate_user,
24+
)
2125

2226

2327
def _assert_correct_types(result: QCSObjectsForNotebook):
@@ -132,3 +136,39 @@ def test_get_qcs_objects_for_notebook_auth_fails(engine_mock):
132136
assert not result.signed_in
133137
assert result.is_simulator
134138
assert result.project_id == 'fake_project'
139+
140+
141+
class TestAuthenticateUser:
142+
"""Tests for the public API `get_hardware_engine_and_authenticate_user` which
143+
authenticates the user and returns a production engine instance ."""
144+
145+
@mock.patch.dict('sys.modules', {'google.colab': mock.Mock()})
146+
def test_authentication_succeeds_no_exceptions_thrown(self):
147+
auth_mock = sys.modules['google.colab']
148+
149+
authenticate_user()
150+
151+
assert auth_mock.auth.authenticate_user.called
152+
153+
@mock.patch.dict('sys.modules', {'google.colab': mock.Mock()})
154+
def test_authentication_failure(self):
155+
project_id = "invalid-project"
156+
# Mock authentication failure
157+
auth_mock = sys.modules['google.colab']
158+
159+
auth_mock.auth.authenticate_user = mock.Mock(side_effect=Exception('mock auth failure'))
160+
161+
with pytest.raises(Exception, match="mock auth failure"):
162+
authenticate_user(project_id)
163+
164+
@mock.patch.dict('sys.modules', {'google.colab': mock.Mock()})
165+
@pytest.mark.parametrize('clear_output', ([True, False]))
166+
def test_clear_output_is_passed(self, clear_output):
167+
auth_mock = sys.modules['google.colab']
168+
169+
with mock.patch.object(
170+
auth_mock.auth, 'authenticate_user', return_value=None
171+
) as mock_authenticate_user:
172+
authenticate_user(clear_output)
173+
174+
mock_authenticate_user.assert_called_with(clear_output=clear_output)

0 commit comments

Comments
 (0)