Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: allow to read output for failed jobs #7067

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/quantum/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
azext_quantum/tests/latest/input_data/bin
azext_quantum/tests/latest/input_data/obj
azext_quantum/tests/latest/recordings/test_results.xml
2 changes: 1 addition & 1 deletion src/quantum/azext_quantum/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@

helps['quantum job output'] = """
type: command
short-summary: Get the results of running a Q# job.
short-summary: Get the results of running a job.
examples:
- name: Print the results of a successful Azure Quantum job.
text: |-
Expand Down
165 changes: 92 additions & 73 deletions src/quantum/azext_quantum/operations/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
logger = logging.getLogger(__name__)
knack_logger = knack.log.get_logger(__name__)

_targets_with_allowed_failure_output = {"microsoft.dft"}


def list(cmd, resource_group_name, workspace_name, location):
"""
Expand Down Expand Up @@ -535,83 +537,25 @@ def _validate_item(provided_value, num_items):

def output(cmd, job_id, resource_group_name, workspace_name, location, item=None):
"""
Get the results of running a Q# job.
Get the results of running a job.
"""
import tempfile
from azure.cli.command_modules.storage._client_factory import blob_data_service_factory

path = os.path.join(tempfile.gettempdir(), job_id)
info = WorkspaceInfo(cmd, resource_group_name, workspace_name, location)
client = cf_jobs(cmd.cli_ctx, info.subscription, info.resource_group, info.name, info.location)
job = client.get(job_id)

if os.path.exists(path):
logger.debug("Using existing blob from %s", path)
else:
logger.debug("Downloading job results blob into %s", path)
if job.status != "Succeeded":
if job.status == "Failed" and job.target in _targets_with_allowed_failure_output:
logger.debug("Job submitted against target \"%s\" failed, but the job output can still be returned. Trying to produce the output.", job.target)
job_output = _get_job_output(cmd, job, item)
if job_output is not None:
return job_output

if job.status != "Succeeded":
return job # If "-o table" is specified, this allows transform_output() in commands.py
# to format the output, so the error info is shown. If "-o json" or no "-o"
# parameter is specified, then the full JSON job output is displayed, being
# consistent with other commands.

args = _parse_blob_url(job.output_data_uri)
blob_service = blob_data_service_factory(cmd.cli_ctx, args)
blob_service.get_blob_to_path(args['container'], args['blob'], path)

with open(path) as json_file:
lines = [line.strip() for line in json_file.readlines()]

# Receiving an empty response is valid.
if len(lines) == 0:
return
return job # If "-o table" is specified, this allows transform_output() in commands.py
# to format the output, so the error info is shown. If "-o json" or no "-o"
# parameter is specified, then the full JSON job output is displayed, being
# consistent with other commands.

if job.target.startswith("microsoft.simulator") and job.target != "microsoft.simulator.resources-estimator":
result_start_line = len(lines) - 1
is_result_string = lines[-1].endswith('"')
if is_result_string:
while result_start_line >= 0 and not lines[result_start_line].startswith('"'):
result_start_line -= 1
if result_start_line < 0:
raise AzureResponseError("Job output is malformed, mismatched quote characters.")

# Print the job output and then the result of the operation as a histogram.
# If the result is a string, trim the quotation marks.
print('\n'.join(lines[:result_start_line]))
raw_result = ' '.join(lines[result_start_line:])
result = raw_result[1:-1] if is_result_string else raw_result
print('_' * len(result) + '\n')

json_string = '{ "histogram" : { "' + result + '" : 1 } }'
data = json.loads(json_string)
else:
json_file.seek(0) # Reset the file pointer before loading
data = json.load(json_file)

# Consider item if it's a batch job, otherwise ignore
import builtins # list has been overriden as a function above
if item and isinstance(data, builtins.list):
item = _validate_item(item, len(data))
return data[item]

return data


def _validate_max_poll_wait_secs(max_poll_wait_secs):
valid_max_poll_wait_secs = 0.0
error_message = f"--max-poll-wait-secs parameter is not valid: {max_poll_wait_secs}"
error_recommendation = f"Must be a number greater than or equal to {MINIMUM_MAX_POLL_WAIT_SECS}"

try:
valid_max_poll_wait_secs = float(max_poll_wait_secs)
except ValueError as e:
raise InvalidArgumentValueError(error_message, error_recommendation) from e

if valid_max_poll_wait_secs < MINIMUM_MAX_POLL_WAIT_SECS:
raise InvalidArgumentValueError(error_message, error_recommendation)

return valid_max_poll_wait_secs
return _get_job_output(cmd, job, item)


def wait(cmd, job_id, resource_group_name, workspace_name, location, max_poll_wait_secs=5):
Expand Down Expand Up @@ -668,9 +612,6 @@ def run(cmd, program_args, resource_group_name, workspace_name, location, target
job = wait(cmd, job.id, resource_group_name, workspace_name, location)
logger.debug(job)

if not job.status == "Succeeded":
return job

return output(cmd, job.id, resource_group_name, workspace_name, location)


Expand All @@ -691,3 +632,81 @@ def cancel(cmd, job_id, resource_group_name, workspace_name, location):

# Wait for the job status to complete or be reported as cancelled
return wait(cmd, job_id, info.resource_group, info.name, info.location)


def _get_job_output(cmd, job, item=None):

import tempfile
path = os.path.join(tempfile.gettempdir(), job.id)

if os.path.exists(path):
logger.debug("Using existing blob from %s", path)
else:
logger.debug("Downloading job results blob into %s", path)

from azure.cli.command_modules.storage._client_factory import blob_data_service_factory

args = _parse_blob_url(job.output_data_uri)
blob_service = blob_data_service_factory(cmd.cli_ctx, args)

containerName = args['container']
blobName = args['blob']
blobProperties = blob_service.get_blob_properties(containerName, blobName)

if blobProperties.properties.content_length == 0:
return

blob_service.get_blob_to_path(containerName, blobName, path)

with open(path) as json_file:
lines = [line.strip() for line in json_file.readlines()]

# Receiving an empty response is valid.
if len(lines) == 0:
return

if job.target.startswith("microsoft.simulator") and job.target != "microsoft.simulator.resources-estimator":
result_start_line = len(lines) - 1
is_result_string = lines[-1].endswith('"')
if is_result_string:
while result_start_line >= 0 and not lines[result_start_line].startswith('"'):
result_start_line -= 1
if result_start_line < 0:
raise AzureResponseError("Job output is malformed, mismatched quote characters.")

# Print the job output and then the result of the operation as a histogram.
# If the result is a string, trim the quotation marks.
print('\n'.join(lines[:result_start_line]))
raw_result = ' '.join(lines[result_start_line:])
result = raw_result[1:-1] if is_result_string else raw_result
print('_' * len(result) + '\n')

json_string = '{ "histogram" : { "' + result + '" : 1 } }'
data = json.loads(json_string)
else:
json_file.seek(0) # Reset the file pointer before loading
data = json.load(json_file)

# Consider item if it's a batch job, otherwise ignore
import builtins # list has been overriden as a function above
if item and isinstance(data, builtins.list):
item = _validate_item(item, len(data))
return data[item]

return data


def _validate_max_poll_wait_secs(max_poll_wait_secs):
valid_max_poll_wait_secs = 0.0
error_message = f"--max-poll-wait-secs parameter is not valid: {max_poll_wait_secs}"
error_recommendation = f"Must be a number greater than or equal to {MINIMUM_MAX_POLL_WAIT_SECS}"

try:
valid_max_poll_wait_secs = float(max_poll_wait_secs)
except ValueError as e:
raise InvalidArgumentValueError(error_message, error_recommendation) from e

if valid_max_poll_wait_secs < MINIMUM_MAX_POLL_WAIT_SECS:
raise InvalidArgumentValueError(error_message, error_recommendation)

return valid_max_poll_wait_secs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
31

C 0.178137 0.023139 0.412368
H 0.835553 0.524899 -0.301980
H 0.782031 -0.601689 1.073610
C -0.598103 1.050400 1.225652
H -1.210310 0.552618 1.979206
H -1.266376 1.627120 0.573655
C -0.027758 2.625324 3.022268
C 0.497943 4.539918 4.462321
H 1.423171 4.969348 4.853950
H 0.000025 4.019113 5.283332
C -0.401626 5.639009 3.900566
H -1.335017 5.213661 3.526287
H 0.104762 6.162247 3.086168
S -0.791628 6.843304 5.230175
H -1.627570 7.616867 4.497656
S -1.006687 -1.030090 -0.507799
N 0.335023 1.932662 1.905919
H 1.076110 2.322214 1.337153
N 0.865882 3.568581 3.448681
H 1.581480 3.859943 2.795574
O -1.073352 2.375031 3.651291
H -0.087670 -1.812849 -1.096189
O -2.865982 8.576796 3.230102
H -2.607661 9.505735 3.243622
H -3.711020 8.550691 3.694031
O 2.173214 8.263152 4.538554
H 2.667328 8.395473 5.355399
H 1.310047 7.923676 4.827433
O -3.015476 4.585503 6.407597
H -2.385804 5.284068 6.165503
H -3.619558 5.007262 7.028684
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
60

O 97.873900000 103.017000000 100.816000000
H 98.128600000 103.038000000 99.848800000
H 97.173800000 102.317000000 100.960000000
O 100.645000000 100.169000000 95.891500000
H 101.491000000 100.305000000 96.406200000
H 99.888700000 100.618000000 96.367800000
O 99.814000000 100.835000000 101.232000000
H 99.329200000 99.976800000 101.063000000
H 99.151600000 101.561000000 101.414000000
O 98.804000000 98.512200000 97.758100000
H 99.782100000 98.646900000 97.916700000
H 98.421800000 99.326500000 97.321300000
O 100.747000000 100.164000000 103.736000000
H 100.658000000 100.628000000 102.855000000
H 100.105000000 99.398600000 103.776000000
O 98.070300000 98.516900000 100.438000000
H 97.172800000 98.878600000 100.690000000
H 98.194000000 98.592200000 99.448100000
O 98.548000000 101.265000000 97.248600000
H 98.688900000 102.140000000 97.711000000
H 97.919900000 101.391000000 96.480800000
O 103.898000000 98.427900000 99.984500000
H 103.015000000 98.654900000 99.573700000
H 104.128000000 97.477300000 99.776100000
O 99.166600000 96.442100000 101.723000000
H 98.843200000 97.206600000 101.166000000
H 99.643900000 95.783700000 101.141000000
O 102.891000000 100.842000000 97.477600000
H 103.837000000 100.662000000 97.209700000
H 102.868000000 101.166000000 98.423400000
O 96.227200000 100.990000000 101.698000000
H 96.148800000 100.422000000 102.517000000
H 95.313600000 101.237000000 101.375000000
O 98.864800000 98.222500000 103.917000000
H 98.949800000 97.463000000 103.272000000
H 99.054800000 97.896400000 104.843000000
O 104.578000000 100.035000000 101.952000000
H 104.419000000 101.011000000 101.802000000
H 104.206000000 99.514900000 101.184000000
O 102.429000000 104.060000000 101.348000000
H 101.757000000 103.665000000 101.974000000
H 102.209000000 105.021000000 101.185000000
O 98.708200000 103.752000000 98.244300000
H 98.397100000 104.234000000 97.425400000
H 99.598500000 104.111000000 98.524400000
O 95.630300000 99.996600000 98.245400000
H 96.540400000 100.410000000 98.268900000
H 94.982900000 100.638000000 97.834500000
O 102.360000000 101.551000000 99.964500000
H 102.675000000 102.370000000 100.444000000
H 101.556000000 101.180000000 100.430000000
O 101.836000000 97.446700000 102.110000000
H 100.860000000 97.397400000 101.898000000
H 101.991000000 97.133400000 103.047000000
O 101.665000000 98.316100000 98.319400000
H 101.904000000 99.233800000 98.002000000
H 102.224000000 97.640900000 97.837700000
O 99.984700000 103.272000000 102.307000000
H 99.640700000 103.104000000 103.231000000
H 99.216500000 103.453000000 101.693000000
Loading