Skip to content

Commit

Permalink
feat: store QueryJob to destination var on error
Browse files Browse the repository at this point in the history
  • Loading branch information
plamut committed Sep 18, 2019
1 parent b4ea85c commit 02e17ea
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 7 deletions.
2 changes: 2 additions & 0 deletions bigquery/google/cloud/bigquery/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -2903,6 +2903,7 @@ def _begin(self, client=None, retry=DEFAULT_RETRY):
super(QueryJob, self)._begin(client=client, retry=retry)
except exceptions.GoogleCloudError as exc:
exc.message += self._format_for_exception(self.query, self.job_id)
exc.query_job = self
raise

def result(
Expand Down Expand Up @@ -2945,6 +2946,7 @@ def result(
)
except exceptions.GoogleCloudError as exc:
exc.message += self._format_for_exception(self.query, self.job_id)
exc.query_job = self
raise

# If the query job is complete but there are no query results, this was
Expand Down
18 changes: 13 additions & 5 deletions bigquery/google/cloud/bigquery/magics.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
* ``<destination_var>`` (optional, line argument):
variable to store the query results. The results are not displayed if
this parameter is used.
this parameter is used. If an error occurs during the query execution,
the corresponding ``QueryJob`` instance (if available) is stored in
the variable instead.
* ``--project <project>`` (optional, line argument):
Project to use for running the query. Defaults to the context
:attr:`~google.cloud.bigquery.magics.Context.project`.
Expand Down Expand Up @@ -450,16 +452,22 @@ def _cell_magic(line, query):
query_job = _run_query(client, query, job_config=job_config)
except Exception as ex:
error = str(ex)
query_job = getattr(ex, "query_job", None)

if not args.verbose:
display.clear_output()

if error:
if args.destination_var:
print(
"Could not save output to variable '{}'.".format(args.destination_var),
file=sys.stderr,
)
if query_job is not None:
IPython.get_ipython().push({args.destination_var: query_job})
else:
print(
"Could not save output to variable '{}'.".format(
args.destination_var
),
file=sys.stderr,
)
print("\nERROR:\n", error, file=sys.stderr)
return

Expand Down
8 changes: 6 additions & 2 deletions bigquery/tests/unit/test_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -4337,8 +4337,10 @@ def test_result_error(self):
self.assertIsInstance(exc_info.exception, exceptions.GoogleCloudError)
self.assertEqual(exc_info.exception.code, http_client.BAD_REQUEST)

full_text = str(exc_info.exception)
exc_job_instance = getattr(exc_info.exception, "query_job", None)
self.assertIs(exc_job_instance, job)

full_text = str(exc_info.exception)
assert job.job_id in full_text
assert "Query Job SQL Follows" in full_text

Expand Down Expand Up @@ -4370,8 +4372,10 @@ def test__begin_error(self):
self.assertIsInstance(exc_info.exception, exceptions.GoogleCloudError)
self.assertEqual(exc_info.exception.code, http_client.BAD_REQUEST)

full_text = str(exc_info.exception)
exc_job_instance = getattr(exc_info.exception, "query_job", None)
self.assertIs(exc_job_instance, job)

full_text = str(exc_info.exception)
assert job.job_id in full_text
assert "Query Job SQL Follows" in full_text

Expand Down
31 changes: 31 additions & 0 deletions bigquery/tests/unit/test_magics.py
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,37 @@ def test_bigquery_magic_dryrun_option_saves_query_job_to_variable():
assert isinstance(q_job, job.QueryJob)


@pytest.mark.usefixtures("ipython_interactive")
def test_bigquery_magic_saves_query_job_to_variable_on_error():
ip = IPython.get_ipython()
ip.extension_manager.load_extension("google.cloud.bigquery")
magics.context.credentials = mock.create_autospec(
google.auth.credentials.Credentials, instance=True
)

client_query_patch = mock.patch(
"google.cloud.bigquery.client.Client.query", autospec=True
)

query_job = mock.create_autospec(job.QueryJob, instance=True)
exception = Exception("Unexpected SELECT")
exception.query_job = query_job
query_job.result.side_effect = exception

sql = "SELECT SELECT 17 AS num"

assert "result" not in ip.user_ns

with client_query_patch as client_query_mock:
client_query_mock.return_value = query_job
return_value = ip.run_cell_magic("bigquery", "result", sql)

assert return_value is None
assert "result" in ip.user_ns
result = ip.user_ns["result"]
assert isinstance(result, job.QueryJob)


@pytest.mark.usefixtures("ipython_interactive")
def test_bigquery_magic_w_maximum_bytes_billed_invalid():
ip = IPython.get_ipython()
Expand Down

0 comments on commit 02e17ea

Please sign in to comment.