Skip to content
Open
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
8 changes: 8 additions & 0 deletions azure-quantum/azure/quantum/job/base_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def from_input_data(
input_params: Dict[str, Any] = None,
session_id: Optional[str] = None,
priority: Optional[str] = None,
tags: Optional[list[str]] = None,
**kwargs
) -> "BaseJob":
"""Create a new Azure Quantum job based on a raw input_data payload.
Expand Down Expand Up @@ -120,6 +121,8 @@ def from_input_data(
:type input_params: Dict[str, Any]
:param priority: Priority of job.
:type priority: str
:param tags: Tags for the job.
:type tags: list[str]
:return: Azure Quantum Job
:rtype: Job
"""
Expand Down Expand Up @@ -157,6 +160,7 @@ def from_input_data(
input_params=input_params,
session_id=session_id,
priority=priority,
tags=tags,
**kwargs
)

Expand All @@ -176,6 +180,7 @@ def from_storage_uri(
submit_job: bool = True,
session_id: Optional[str] = None,
priority: Optional[str] = None,
tags: Optional[list[str]] = None,
**kwargs
) -> "BaseJob":
"""Create new Job from URI if input data is already uploaded
Expand Down Expand Up @@ -205,6 +210,8 @@ def from_storage_uri(
:type submit_job: bool
:param priority: Priority of job.
:type priority: str
:param tags: Tags for the job.
:type tags: list[str]
:return: Job instance
:rtype: Job
"""
Expand All @@ -231,6 +238,7 @@ def from_storage_uri(
input_params=input_params,
session_id=session_id,
priority=priority,
tags=tags,
**kwargs
)
job = cls(workspace, details, **kwargs)
Expand Down
10 changes: 10 additions & 0 deletions azure-quantum/azure/quantum/job/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ class Session(WorkspaceItem):
:param priority: Priority of the session.
:type priority: Optional[str]

:param tags: Tags for the session.
:type tags: Optional[list[str]]

:raises ValueError: if details is passed along individual parameters,
or if required parameters are missing.
"""
Expand All @@ -65,6 +68,7 @@ def __init__(
name: Optional[str] = None,
job_failure_policy: Union[str, SessionJobFailurePolicy, None] = None,
priority: Optional[str] = None,
tags: Optional[list[str]] = None,
**kwargs):
from azure.quantum.target import Target
target_name = target.name if isinstance(target, Target) else target
Expand Down Expand Up @@ -97,6 +101,7 @@ def __init__(
target=target_name,
job_failure_policy=job_failure_policy,
priority=priority,
tags=tags,
**kwargs)

super().__init__(
Expand Down Expand Up @@ -265,6 +270,7 @@ def open_session(
name: Optional[str] = None,
job_failure_policy: Union[str, SessionJobFailurePolicy, None] = None,
priority: Optional[str] = None,
tags: Optional[list[str]] = None,
**kwargs
) -> Session:
"""Opens a session and associates all future job submissions to that
Expand Down Expand Up @@ -306,6 +312,9 @@ def open_session(
:param priority: Priority of the session.
:type priority: Optional[str]

:param tags: Tags for the session.
:type tags: Optional[list[str]]

:return: The session object with updated details after its opening.
:rtype: Session
"""
Expand All @@ -320,6 +329,7 @@ def open_session(
target=self._get_azure_target_id(),
provider_id=self._get_azure_provider_id(),
priority=priority,
tags=tags,
**kwargs)
self.latest_session = session
return session.open()
4 changes: 4 additions & 0 deletions azure-quantum/azure/quantum/target/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ def submit(
shots: int = None,
input_params: Union[Dict[str, Any], InputParams, None] = None,
priority: Optional[str] = None,
tags: Optional[list[str]] = None,
**kwargs
) -> Job:
"""Submit input data and return Job.
Expand All @@ -228,6 +229,8 @@ def submit(
:type input_params: Dict[str, Any]
:param priority: Priority of job.
:type priority: str
:param tags: Tags for the job.
:type tags: list[str]
:return: Azure Quantum job
:rtype: azure.quantum.job.Job
"""
Expand Down Expand Up @@ -324,6 +327,7 @@ def _get_entrypoint(input_data):
input_params=input_params,
session_id=self.get_latest_session_id(),
priority=priority,
tags=tags,
**kwargs
)

Expand Down
19 changes: 14 additions & 5 deletions azure-quantum/azure/quantum/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
Union,
)
from typing_extensions import Self
from azure.core.exceptions import HttpResponseError
from azure.core.paging import ItemPaged
from azure.quantum._client import WorkspaceClient
from azure.quantum._client.models import JobDetails, ItemDetails, SessionDetails
Expand Down Expand Up @@ -443,11 +444,19 @@ def cancel_job(self, job: Job) -> Job:
:rtype: Job
"""
client = self._get_jobs_client()
client.delete(
self.subscription_id,
self.resource_group,
self.name,
job.details.id)

try:
client.cancel(
self.subscription_id,
self.resource_group,
self.name,
job.details.id)
except HttpResponseError as e:
# because of historical behavior of the service, the 204 No Content response is returned when cancellation request is succeeded.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is cancellation supposed to return 200 with empty body? I can make that change if needed for latest api-versions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# while backend is not updated to return 200 according to a guideline, let's handle that here to align with typespecs
if e.status_code != 204:
raise

details = client.get(
self.subscription_id,
self.resource_group,
Expand Down
16 changes: 16 additions & 0 deletions azure-quantum/tests/mock_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from typing import List, Optional
from datetime import datetime, UTC, timedelta

from azure.core.exceptions import HttpResponseError
from azure.core.paging import ItemPaged
from azure.quantum.workspace import Workspace
from types import SimpleNamespace
Expand Down Expand Up @@ -200,6 +201,21 @@ def delete(
return None
raise KeyError(job_id)

def cancel(
self,
subscription_id: str,
resource_group_name: str,
workspace_name: str,
job_id: str,
) -> None:
for jd in self._store:
if jd.id == job_id:
jd.status = "Cancelled"
error = HttpResponseError("204 No Content")
error.status_code = 204
raise error
raise KeyError(job_id)

def list(
self,
subscription_id: str,
Expand Down
28 changes: 28 additions & 0 deletions azure-quantum/tests/test_workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import os
from unittest import mock
from azure.quantum.job.job import Job
from azure.quantum._client.models import JobDetails
from azure.quantum._constants import EnvironmentVariables, ConnectionConstants
from azure.core.credentials import AzureKeyCredential
from azure.core.pipeline.policies import AzureKeyCredentialPolicy
Expand Down Expand Up @@ -357,6 +359,32 @@ def assert_value_error(exception: Exception):
assert "Invalid resource id" in e.args[0]


def test_workspace_cancel_job_success():
ws = WorkspaceMock(
subscription_id=SUBSCRIPTION_ID,
resource_group=RESOURCE_GROUP,
name=WORKSPACE,
)

job_id = "test-cancel-success"
details = JobDetails(
id=job_id,
name=f"job-{job_id}",
container_uri="https://example.com/container",
input_data_format="microsoft.resource-estimate.v2",
provider_id="ionq",
target="ionq.simulator",
status="Executing",
)
ws._client.services.jobs._store.append(details)

job = Job(ws, details)
result = ws.cancel_job(job)

assert result.details.status == "Cancelled"
assert result.id == job_id


def test_workspace_user_agent_appid():
app_id = "MyEnvVarAppId"
user_agent = "MyUserAgent"
Expand Down