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

feat: unify exception handling of requests library error and website … #147

Merged
merged 1 commit into from
Mar 23, 2024
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 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
Loading