Skip to content

Commit

Permalink
feat: unify exception handling of requests library error and website …
Browse files Browse the repository at this point in the history
…execution error. close #142
  • Loading branch information
Zhaoyilunnn committed Mar 23, 2024
1 parent ab9f4ac commit c60f4c9
Show file tree
Hide file tree
Showing 9 changed files with 377 additions and 34 deletions.
3 changes: 3 additions & 0 deletions quafu/exceptions/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
from .circuit_error import *
from .quafu_error import *
from .user_error import *
from .utils import validate_server_resp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ..exceptions import QuafuError
from .quafu_error import QuafuError


class UserError(QuafuError):
Expand Down
39 changes: 39 additions & 0 deletions quafu/exceptions/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# (C) Copyright 2023 Beijing Academy of Quantum Information Sciences
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from .circuit_error import IndexOutOfRangeError, InvalidParaError, UnsupportedYet
from .quafu_error import CircuitError, CompileError, QuafuError, ServerError
from .user_error import APITokenNotFound, BackendNotAvailable, UserError


def validate_server_resp(res):
"""Check results returned by backend service"""

status_code = res["status"] if "status" in res else res["code"]
err_msg = (
res["message"]
if "message" in res
else res["msg"]
if "msg" in res
else "No error message"
)

if status_code in [201, 205, 400]:
raise UserError(err_msg)
if status_code == 5001:
raise CircuitError(err_msg)
if status_code == 5003:
raise ServerError(err_msg)
if status_code == 5004:
raise CompileError(err_msg)
43 changes: 18 additions & 25 deletions quafu/tasks/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@
from urllib import parse

import numpy as np
import requests
from quafu.circuits.quantum_circuit import QuantumCircuit
from quafu.users.userapi import User

from ..exceptions import CircuitError, CompileError, ServerError
from ..exceptions import CircuitError, UserError, validate_server_resp
from ..results.results import ExecResult, merge_measure
from ..users.exceptions import UserError
from ..utils.client_wrapper import ClientWrapper


class Task(object):
class Task:
"""
Class for submitting quantum computation task to the backend.
Expand Down Expand Up @@ -233,11 +232,12 @@ def send(
}
data = parse.urlencode(data)
data = data.replace("%27", "'")
response = requests.post(
response = ClientWrapper.post(
url, headers=headers, data=data
) # type: requests.models.Response

# TODO: completing status code checks
# FIXME: Maybe we need to delete below code
if not response.ok:
logging.warning("Received a non-200 response from the server.\n")
if response.status_code == 502:
Expand All @@ -246,26 +246,19 @@ def send(
"If there is persistent failure, please report it on our github page."
)
raise UserError("502 Bad Gateway response")
# FIXME: Maybe we need to delete above code

res_dict = response.json() # type: dict
validate_server_resp(res_dict)

task_id = res_dict["task_id"]

if group not in self.submit_history:
self.submit_history[group] = [task_id]
else:
res_dict = response.json() # type: dict
quafu_status = res_dict["status"]
if quafu_status in [201, 205]:
raise UserError(res_dict["message"])
elif quafu_status == 5001:
raise CircuitError(res_dict["message"])
elif quafu_status == 5003:
raise ServerError(res_dict["message"])
elif quafu_status == 5004:
raise CompileError(res_dict["message"])
else:
task_id = res_dict["task_id"]

if group not in self.submit_history:
self.submit_history[group] = [task_id]
else:
self.submit_history[group].append(task_id)

return ExecResult(res_dict)
self.submit_history[group].append(task_id)

return ExecResult(res_dict)

def retrieve(self, taskid: str) -> ExecResult:
"""
Expand All @@ -281,7 +274,7 @@ def retrieve(self, taskid: str) -> ExecResult:
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
"api_token": self.user.api_token,
}
response = requests.post(url, headers=headers, data=data)
response = ClientWrapper.post(url, headers=headers, data=data)

res_dict = response.json()
return ExecResult(res_dict)
Expand Down
13 changes: 5 additions & 8 deletions quafu/users/userapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@
import os
from typing import Optional

import requests

from ..exceptions import APITokenNotFound, UserError, validate_server_resp
from ..utils.client_wrapper import ClientWrapper
from ..utils.platform import get_homedir
from .exceptions import APITokenNotFound, UserError


class User(object):
Expand Down Expand Up @@ -103,12 +102,10 @@ def _get_backends_info(self):
"""
headers = {"api_token": self.api_token}
url = self.url + self.backends_api
response = requests.post(url=url, headers=headers)
response = ClientWrapper.post(url=url, headers=headers)
backends_info = response.json()
if backends_info["status"] == 201:
raise UserError(backends_info["message"])
else:
return backends_info["data"]
validate_server_resp(backends_info)
return backends_info["data"]

def get_available_backends(self, print_info=True):
"""
Expand Down
31 changes: 31 additions & 0 deletions quafu/utils/client_wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# (C) Copyright 2023 Beijing Academy of Quantum Information Sciences
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import requests
from requests.exceptions import RequestException

from ..exceptions import ServerError


class ClientWrapper:
@staticmethod
def post(*args, **kwargs):
try:
response = requests.post(*args, **kwargs)
response.raise_for_status()
except RequestException as err:
raise ServerError(
f"Failed to communicate with quafu website, please retry later or submit an issue, err: {err}"
) from err
return response
169 changes: 169 additions & 0 deletions tests/quafu/tasks/data/fake_backends.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
{
"data": [
{
"QV": 0,
"clops": 0.0,
"priority_qubits": null,
"qubits": 10,
"status": "Offline",
"system_id": 0,
"system_name": "ScQ-P10",
"valid_gates": [
"cx",
"cz",
"rx",
"ry",
"rz",
"x",
"y",
"z",
"h",
"sx",
"sy",
"id",
"delay",
"barrier",
"cy",
"cnot",
"swap"
]
},
{
"QV": 0,
"clops": 0.0,
"priority_qubits": null,
"qubits": 8,
"status": "Maintenance",
"system_id": 1,
"system_name": "ScQ-P18",
"valid_gates": [
"cx",
"cz",
"rx",
"ry",
"rz",
"x",
"y",
"z",
"h",
"sx",
"sy",
"id",
"delay",
"barrier",
"cy",
"cnot",
"swap"
]
},
{
"QV": 0,
"clops": 0.0,
"priority_qubits": "[108, 109, 119, 120, 121, 110, 111, 122, 123]",
"qubits": 136,
"status": "Online",
"system_id": 2,
"system_name": "ScQ-P136",
"valid_gates": [
"cx",
"cz",
"rx",
"ry",
"rz",
"x",
"y",
"z",
"h",
"delay",
"barrier"
]
},
{
"QV": 0,
"clops": 0.0,
"priority_qubits": null,
"qubits": 102,
"status": "Maintenance",
"system_id": 3,
"system_name": "ScQ-P102",
"valid_gates": [
"cx",
"cz",
"rx",
"ry",
"rz",
"x",
"y",
"z",
"h",
"sx",
"sy",
"id",
"delay",
"barrier",
"cy",
"cnot",
"swap"
]
},
{
"QV": 0,
"clops": 0.0,
"priority_qubits": null,
"qubits": 10,
"status": "Maintenance",
"system_id": 4,
"system_name": "ScQ-P10C",
"valid_gates": [
"cx",
"cz",
"rx",
"ry",
"rz",
"x",
"y",
"z",
"h",
"delay",
"barrier"
]
},
{
"QV": 1,
"clops": 1.0,
"priority_qubits": null,
"qubits": 2,
"status": "Offline",
"system_id": 5,
"system_name": "ScQ-XXX",
"valid_gates": [
"cx",
"cz",
"rx",
"ry",
"rz",
"x",
"y",
"z",
"h",
"delay",
"barrier"
]
},
{
"QV": 0,
"clops": 0.0,
"priority_qubits": "[1]",
"qubits": 156,
"status": "None Status",
"system_id": 6,
"system_name": "ScQ-P156",
"valid_gates": [
"cx"
]
}
],
"message": "success",
"ok": true,
"status": 200
}
9 changes: 9 additions & 0 deletions tests/quafu/tasks/data/fake_task_res.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"measure": "{108: 0, 109: 1}",
"openqasm": "OPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[136];\ncreg c[2];\nh q[108];\ncx q[108],q[109];\nbarrier q[108],q[109];\nmeasure q[108] -> c[0];\nmeasure q[109] -> c[1];\n",
"raw": "{\"11\": 851, \"00\": 998, \"10\": 90, \"01\": 61}",
"res": "{\"11\": 851, \"00\": 998, \"10\": 90, \"01\": 61}",
"status": 2,
"task_id": "42FBBE201BA554DB",
"task_name": ""
}
Loading

0 comments on commit c60f4c9

Please sign in to comment.