Skip to content

Commit

Permalink
[Quantum] Show required parameters in CLI Reference documentation (#5130
Browse files Browse the repository at this point in the history
)

* Flag required params in workspace commands

* Fix azdev style violations

* Restore _params.py

* Apply configured_default to workspace_name_type

* Delete required=False on workspace_name_type

* Show more parameters as Required

* Show job parameters as Required

* Show target parameters as Required

* Fix output of az quantum target show

* Delete commented-out lines

* Go back to using show_command for job and target

* Add required parameters in help examples

* Delete help example and trailing whitespace

* Add test_job_errors

* Add unit tests

* Delete unused imports and commented-out code

* Delete more unused imports and commented-out code

* Empty commit to trigger CI/CD check pipeline (like /azp run)

* Remove capsys from tests

* Ensure test failure if command with missing parameters doesn’t fail

* Add comment about TODO in utils.py

* Revise the 'az quantum target show' error message

* Restore version number to 0.16.0

* Correct 'az quantum workspace delete' help example

* Modify help example to pass CLI Linter check

* Add note to help example for consistency with related example
  • Loading branch information
Warren Jones authored Oct 12, 2022
1 parent d283ac4 commit a9d7efa
Show file tree
Hide file tree
Showing 13 changed files with 135 additions and 62 deletions.
18 changes: 6 additions & 12 deletions src/quantum/azext_quantum/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@
- name: Submit the Q# program from the current folder.
text: |-
az quantum job submit -g MyResourceGroup -w MyWorkspace -l MyLocation \\
--job-name MyJob
-t MyTarget --job-name MyJob
- name: Submit the Q# program from the current folder with job parameters for a target.
text: |-
az quantum job submit -g MyResourceGroup -w MyWorkspace -l MyLocation \\
--job-name MyJob --job-params param1=value1 param2=value2
-t MyTarget --job-name MyJob --job-params param1=value1 param2=value2
- name: Submit the Q# program with program parameters (e.g. n-qubits = 2).
text: |-
az quantum job submit -g MyResourceGroup -w MyWorkspace -l MyLocation \\
--job-name MyJob -- --n-qubits=2
-t MyTarget --job-name MyJob -- --n-qubits=2
- name: Submit a Q# program from the current folder with a target-capability parameter.
text: |-
az quantum job submit -g MyResourceGroup -w MyWorkspace -l MyLocation -t MyTarget \\
Expand Down Expand Up @@ -190,7 +190,7 @@

helps['quantum target show'] = """
type: command
short-summary: Get the details of the given (or current) target to use when submitting jobs to Azure Quantum.
short-summary: Get the Target ID of the current default target to use when submitting jobs to Azure Quantum.
examples:
- name: Show the currently selected default target
text: |-
Expand Down Expand Up @@ -231,12 +231,9 @@
type: command
short-summary: Delete the given (or current) Azure Quantum workspace.
examples:
- name: Delete an Azure Quantum workspace by name and group.
- name: Delete an Azure Quantum workspace by resource group and workspace name. If a default workspace has been set, the -g and -w parameters are not required.
text: |-
az quantum workspace delete -g MyResourceGroup -w MyWorkspace
- name: Delete and clear the default Azure Quantum workspace (if one has been set).
text: |-
az quantum workspace delete
"""

helps['quantum workspace list'] = """
Expand All @@ -256,10 +253,7 @@
type: command
short-summary: List the quotas for the given (or current) Azure Quantum workspace.
examples:
- name: List the quota information of the default workspace if set.
text: |-
az quantum workspace quotas
- name: List the quota information of a specified Azure Quantum workspace.
- name: List the quota information of a specified Azure Quantum workspace. If a default workspace has been set, the -g, -w, and -l parameters are not required.
text: |-
az quantum workspace quotas -g MyResourceGroup -w MyWorkspace -l MyLocation
"""
Expand Down
8 changes: 6 additions & 2 deletions src/quantum/azext_quantum/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ def get_action(self, values, option_string):


def load_arguments(self, _):
workspace_name_type = CLIArgumentType(options_list=['--workspace-name', '-w'], help='Name of the Quantum Workspace. You can configure the default workspace using `az quantum workspace set`.', id_part=None, required=False)
workspace_name_type = CLIArgumentType(options_list=['--workspace-name', '-w'], help='Name of the Quantum Workspace. You can configure the default workspace using `az quantum workspace set`.', configured_default='workspace', id_part=None)
storage_account_name_type = CLIArgumentType(options_list=['--storage-account', '-a'], help='Name of the storage account to be used by a quantum workspace.')
program_args_type = CLIArgumentType(nargs='*', help='List of arguments expected by the Q# operation specified as --name=value after `--`.')
target_id_type = CLIArgumentType(options_list=['--target-id', '-t'], help='Execution engine for quantum computing jobs. When a workspace is configured with a set of provider, they each enable one or more targets. You can configure the default target using `az quantum target set`.')
target_id_type = CLIArgumentType(options_list=['--target-id', '-t'], help='Execution engine for quantum computing jobs. When a workspace is configured with a set of providers, they each enable one or more targets. You can configure the default target using `az quantum target set`.', configured_default='target_id')
project_type = CLIArgumentType(help='The location of the Q# project to submit. Defaults to current folder.')
job_name_type = CLIArgumentType(help='A friendly name to give to this run of the program.')
job_id_type = CLIArgumentType(options_list=['--job-id', '-j'], help='Job unique identifier in GUID format.')
Expand Down Expand Up @@ -60,6 +60,10 @@ def load_arguments(self, _):
c.argument('workspace_name', workspace_name_type)
c.argument('target_id', target_id_type)

with self.argument_context('quantum target show') as c:
c.argument('workspace_name', workspace_name_type)
c.argument('target_id', target_id_type, required=False)

with self.argument_context('quantum job') as c:
c.argument('workspace_name', workspace_name_type)
c.argument('job_id', job_id_type)
Expand Down
3 changes: 2 additions & 1 deletion src/quantum/azext_quantum/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def validate_target_info(cmd, namespace):
target = TargetInfo(cmd, target_id)

if not target.target_id:
raise ValueError("Missing target-id argument")
# raise ValueError("Missing target-id argument. Use `az quantum target set -t MyTarget` to set a default Target ID.")
raise ValueError("No default Target ID has been saved. Use `az quantum target set -t MyTarget` to set a default Target ID.")


def validate_workspace_and_target_info(cmd, namespace):
Expand Down
4 changes: 2 additions & 2 deletions src/quantum/azext_quantum/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,13 @@ def load_command_table(self, _):

with self.command_group('quantum target', target_ops) as t:
t.command('list', 'list', validator=validate_workspace_info, table_transformer=transform_targets)
t.show_command('show', validator=validate_target_info)
t.show_command('show', 'target_show', validator=validate_target_info)
t.command('set', 'set', validator=validate_target_info)
t.command('clear', 'clear')

with self.command_group('quantum job', job_ops) as j:
j.command('list', 'list', validator=validate_workspace_info, table_transformer=transform_jobs)
j.show_command('show', validator=validate_workspace_info, table_transformer=transform_job)
j.show_command('show', 'job_show', validator=validate_workspace_info, table_transformer=transform_job)
j.command('submit', 'submit', validator=validate_workspace_and_target_info, table_transformer=transform_job)
j.command('wait', 'wait', validator=validate_workspace_info, table_transformer=transform_job)
j.command('output', 'output', validator=validate_workspace_info, table_transformer=transform_output)
Expand Down
24 changes: 17 additions & 7 deletions src/quantum/azext_quantum/operations/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
knack_logger = knack.log.get_logger(__name__)


def list(cmd, resource_group_name=None, workspace_name=None, location=None):
def list(cmd, resource_group_name, workspace_name, location):
"""
Get the list of jobs in a Quantum Workspace.
"""
Expand Down Expand Up @@ -168,7 +168,7 @@ def _has_completed(job):
return job.status in ("Succeeded", "Failed", "Cancelled")


def submit(cmd, program_args, resource_group_name=None, workspace_name=None, location=None, target_id=None,
def submit(cmd, program_args, resource_group_name, workspace_name, location, target_id,
project=None, job_name=None, shots=None, storage=None, no_build=False, job_params=None,
target_capability=None):
"""
Expand Down Expand Up @@ -203,7 +203,7 @@ def submit(cmd, program_args, resource_group_name=None, workspace_name=None, loc
# Query for the job and return status to caller.
return get(cmd, job_id, resource_group_name, workspace_name, location)

# The program compiled succesfully, but executing the stand-alone .exe failed to run.
# The program compiled successfully, but executing the stand-alone .exe failed to run.
logger.error("Submission of job failed with error code %s", result.returncode)
print(result.stdout.decode('ascii'))
raise AzureInternalError("Failed to submit job.")
Expand All @@ -229,7 +229,7 @@ def _parse_blob_url(url):
}


def output(cmd, job_id, resource_group_name=None, workspace_name=None, location=None):
def output(cmd, job_id, resource_group_name, workspace_name, location):
"""
Get the results of running a Q# job.
"""
Expand Down Expand Up @@ -305,7 +305,7 @@ def _validate_max_poll_wait_secs(max_poll_wait_secs):
return valid_max_poll_wait_secs


def wait(cmd, job_id, resource_group_name=None, workspace_name=None, location=None, max_poll_wait_secs=5):
def wait(cmd, job_id, resource_group_name, workspace_name, location, max_poll_wait_secs=5):
"""
Place the CLI in a waiting state until the job finishes running.
"""
Expand Down Expand Up @@ -334,7 +334,17 @@ def wait(cmd, job_id, resource_group_name=None, workspace_name=None, location=No
return job


def run(cmd, program_args, resource_group_name=None, workspace_name=None, location=None, target_id=None,
def job_show(cmd, job_id, resource_group_name, workspace_name, location):
"""
Get the job's status and details.
"""
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)
return job


def run(cmd, program_args, resource_group_name, workspace_name, location, target_id,
project=None, job_name=None, shots=None, storage=None, no_build=False, job_params=None, target_capability=None):
"""
Submit a job to run on Azure Quantum, and waits for the result.
Expand All @@ -353,7 +363,7 @@ def run(cmd, program_args, resource_group_name=None, workspace_name=None, locati
return output(cmd, job.id, resource_group_name, workspace_name, location)


def cancel(cmd, job_id, resource_group_name=None, workspace_name=None, location=None):
def cancel(cmd, job_id, resource_group_name, workspace_name, location):
"""
Request to cancel a job on Azure Quantum if it hasn't completed.
"""
Expand Down
6 changes: 3 additions & 3 deletions src/quantum/azext_quantum/operations/offerings.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def _valid_publisher_and_offer(provider, publisher, offer):
return True


def list_offerings(cmd, location=None, autoadd_only=False):
def list_offerings(cmd, location, autoadd_only=False):
"""
Get the list of all provider offerings available on the given location.
"""
Expand All @@ -70,7 +70,7 @@ def list_offerings(cmd, location=None, autoadd_only=False):
return offerings


def show_terms(cmd, provider_id=None, sku=None, location=None):
def show_terms(cmd, provider_id, sku, location):
"""
Show the terms of a provider and SKU combination including license URL and acceptance status.
"""
Expand All @@ -82,7 +82,7 @@ def show_terms(cmd, provider_id=None, sku=None, location=None):
return _get_terms_from_marketplace(cmd, publisher_id, offer_id, sku)


def accept_terms(cmd, provider_id=None, sku=None, location=None):
def accept_terms(cmd, provider_id, sku, location):
"""
Accept the terms of a provider and SKU combination to enable it for workspace creation.
"""
Expand Down
20 changes: 14 additions & 6 deletions src/quantum/azext_quantum/operations/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ class TargetInfo:
def __init__(self, cmd, target_id=None):

def select_value(key, value):
if value is not None:
return value
value = cmd.cli_ctx.config.get('quantum', key, None)
if value is not None:
return value
value = cmd.cli_ctx.config.get(cmd.cli_ctx.config.defaults_section_name, key, None)
Expand All @@ -30,7 +27,7 @@ def save(self, cmd):
from azure.cli.core.util import ConfiguredDefaultSetter

with ConfiguredDefaultSetter(cmd.cli_ctx.config, False):
cmd.cli_ctx.config.set_value('quantum', 'target_id', self.target_id)
cmd.cli_ctx.config.set_value(cmd.cli_ctx.config.defaults_section_name, 'target_id', self.target_id)


def get(cmd, target_id=None):
Expand All @@ -41,7 +38,7 @@ def get(cmd, target_id=None):
return info


def set(cmd, target_id=None):
def set(cmd, target_id):
"""
Select the default target to use when submitting jobs to Azure Quantum.
"""
Expand All @@ -51,7 +48,7 @@ def set(cmd, target_id=None):
return info


def list(cmd, resource_group_name=None, workspace_name=None, location=None):
def list(cmd, resource_group_name, workspace_name, location):
"""
Get the list of providers and their targets in an Azure Quantum workspace.
"""
Expand All @@ -67,3 +64,14 @@ def clear(cmd):
info = TargetInfo(cmd)
info.clear()
info.save(cmd)


# Added to fix output problem
# def show(cmd, target_id):
def target_show(cmd, target_id):
"""
Show the currently selected default target.
"""
info = TargetInfo(cmd, target_id)
info.target_id += "" # Kludge excuse: Without this the only output we ever get is "targetId": {"isDefault": true}
return info
23 changes: 10 additions & 13 deletions src/quantum/azext_quantum/operations/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

# pylint: disable=line-too-long,redefined-builtin,unnecessary-comprehension, too-many-locals, too-many-statements
# pylint: disable=line-too-long,redefined-builtin,unnecessary-comprehension, too-many-locals, too-many-statements, too-many-nested-blocks

import os.path
import json
Expand Down Expand Up @@ -50,12 +50,8 @@ def __init__(self, cmd, resource_group_name=None, workspace_name=None, location=

# Hierarchically selects the value for the given key.
# First tries the value provided as argument, as that represents the value from the command line
# then it checks if the key exists in the 'quantum' section in config, and uses that if available.
# finally, it checks in the 'global' section in the config.
# then it checks if the key exists in the [defaults] section in config, and uses that if available.
def select_value(key, value):
if value is not None:
return value
value = cmd.cli_ctx.config.get('quantum', key, None)
if value is not None:
return value
value = cmd.cli_ctx.config.get(cmd.cli_ctx.config.defaults_section_name, key, None)
Expand All @@ -75,10 +71,11 @@ def clear(self):
def save(self, cmd):
from azure.cli.core.util import ConfiguredDefaultSetter

# Save in the global [defaults] section of the .azure\config file
with ConfiguredDefaultSetter(cmd.cli_ctx.config, False):
cmd.cli_ctx.config.set_value('quantum', 'group', self.resource_group)
cmd.cli_ctx.config.set_value('quantum', 'workspace', self.name)
cmd.cli_ctx.config.set_value('quantum', 'location', self.location)
cmd.cli_ctx.config.set_value(cmd.cli_ctx.config.defaults_section_name, 'group', self.resource_group)
cmd.cli_ctx.config.set_value(cmd.cli_ctx.config.defaults_section_name, 'workspace', self.name)
cmd.cli_ctx.config.set_value(cmd.cli_ctx.config.defaults_section_name, 'location', self.location)


def _show_tip(msg):
Expand Down Expand Up @@ -211,7 +208,7 @@ def _validate_storage_account(tier_or_kind_msg_text, tier_or_kind, supported_tie
f"Storage account {tier_or_kind_msg_text}{plural} currently supported: {tier_or_kind_list[:-2]}")


def create(cmd, resource_group_name=None, workspace_name=None, location=None, storage_account=None, skip_role_assignment=False, provider_sku_list=None, auto_accept=False):
def create(cmd, resource_group_name, workspace_name, location, storage_account, skip_role_assignment=False, provider_sku_list=None, auto_accept=False):
"""
Create a new Azure Quantum workspace.
"""
Expand Down Expand Up @@ -315,7 +312,7 @@ def create(cmd, resource_group_name=None, workspace_name=None, location=None, st
return quantum_workspace


def delete(cmd, resource_group_name=None, workspace_name=None):
def delete(cmd, resource_group_name, workspace_name):
"""
Delete the given (or current) Azure Quantum workspace.
"""
Expand Down Expand Up @@ -354,7 +351,7 @@ def get(cmd, resource_group_name=None, workspace_name=None):
return ws


def quotas(cmd, resource_group_name=None, workspace_name=None, location=None):
def quotas(cmd, resource_group_name, workspace_name, location):
"""
List the quotas for the given (or current) Azure Quantum workspace.
"""
Expand All @@ -363,7 +360,7 @@ def quotas(cmd, resource_group_name=None, workspace_name=None, location=None):
return client.list()


def set(cmd, workspace_name, resource_group_name=None, location=None):
def set(cmd, workspace_name, resource_group_name, location):
"""
Set the default Azure Quantum workspace.
"""
Expand Down
16 changes: 14 additions & 2 deletions src/quantum/azext_quantum/tests/latest/test_quantum_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

import json
import os
import pytest
import unittest

from azure.cli.testsdk.scenario_tests import AllowLargeResponse, live_only
from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer)
from azure.cli.testsdk import ScenarioTest
from azure.cli.core.azclierror import InvalidArgumentValueError, AzureInternalError

from .utils import get_test_subscription_id, get_test_resource_group, get_test_workspace, get_test_workspace_location
from .utils import get_test_subscription_id, get_test_resource_group, get_test_workspace, get_test_workspace_location, issue_cmd_with_param_missing
from ..._client_factory import _get_data_credentials
from ...commands import transform_output
from ...operations.workspace import WorkspaceInfo
Expand All @@ -31,6 +32,17 @@ def test_jobs(self):
targets = self.cmd('az quantum target list -o json').get_output_in_json()
assert len(targets) > 0

# @pytest.fixture(autouse=True)
# def _pass_fixtures(self, capsys):
# self.capsys = capsys
# # See "TODO" in issue_cmd_with_param_missing un utils.py

def test_job_errors(self):
issue_cmd_with_param_missing(self, "az quantum job cancel", "az quantum job cancel -g MyResourceGroup -w MyWorkspace -l MyLocation -j yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy\nCancel an Azure Quantum job by id.")
issue_cmd_with_param_missing(self, "az quantum job output", "az quantum job output -g MyResourceGroup -w MyWorkspace -l MyLocation -j yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy -o table\nPrint the results of a successful Azure Quantum job.")
issue_cmd_with_param_missing(self, "az quantum job show", "az quantum job show -g MyResourceGroup -w MyWorkspace -l MyLocation -j yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy --query status\nGet the status of an Azure Quantum job.")
issue_cmd_with_param_missing(self, "az quantum job wait", "az quantum job wait -g MyResourceGroup -w MyWorkspace -l MyLocation -j yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy --max-poll-wait-secs 60 -o table\nWait for completion of a job, check at 60 second intervals.")

def test_build(self):
result = build(self, target_id='ionq.simulator', project='src\\quantum\\azext_quantum\\tests\\latest\\source_for_build_test\\QuantumRNG.csproj', target_capability='BasicQuantumFunctionality')
assert result == {'result': 'ok'}
Expand Down
21 changes: 21 additions & 0 deletions src/quantum/azext_quantum/tests/latest/test_quantum_offerings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import pytest

from azure.cli.testsdk import ScenarioTest
from .utils import issue_cmd_with_param_missing


class QuantumOfferingsScenarioTest(ScenarioTest):

# @pytest.fixture(autouse=True)
# def _pass_fixtures(self, capsys):
# self.capsys = capsys
# # See "TODO" in issue_cmd_with_param_missing un utils.py

def test_offerings_errors(self):
issue_cmd_with_param_missing(self, "az quantum offerings accept-terms", "az quantum offerings accept-terms -p MyProviderId -k MySKU -l MyLocation\nOnce terms have been reviewed, accept the invoking this command.")
issue_cmd_with_param_missing(self, "az quantum offerings show-terms", "az quantum offerings show-terms -p MyProviderId -k MySKU -l MyLocation\nUse a Provider Id and SKU from `az quantum offerings list` to review the terms.")
Loading

0 comments on commit a9d7efa

Please sign in to comment.