Skip to content

Commit

Permalink
Merge d6e7ca6 into 7af287c
Browse files Browse the repository at this point in the history
  • Loading branch information
QFer authored Apr 15, 2019
2 parents 7af287c + d6e7ca6 commit 4044df3
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 15 deletions.
11 changes: 6 additions & 5 deletions src/quantuminspire/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from coreapi.exceptions import CoreAPIException, ErrorMessage

from quantuminspire.credentials import load_account
from quantuminspire.exceptions import ApiError
from quantuminspire.exceptions import ApiError, AuthenticationError
from quantuminspire.job import QuantumInspireJob

QI_URL = 'https://api.quantum-inspire.com'
Expand Down Expand Up @@ -61,7 +61,7 @@ def __init__(self, base_uri: str = QI_URL, authentication: Optional[coreapi.auth
authentication: The authentication, can be one of the following coreapi authentications:
BasicAuthentication(email, password), HTTP authentication with valid email/password.
TokenAuthentication(token, scheme="token"), token authentication with a valid API-token.
When authentication is None, a token is read from the default resource.
When authentication is None, a token is read from the default location.
project_name: The project used for executing the jobs.
coreapi_client_class: Coreapi client to interact with the API through a schema.
Default set to coreapi.Client.
Expand All @@ -72,15 +72,16 @@ def __init__(self, base_uri: str = QI_URL, authentication: Optional[coreapi.auth
project name is supplied here.
Raises:
ApiError: An ApiError exception is raised when no authentication is given and the token could not be
loaded or the schema could not be loaded.
AuthenticationError: An AuthenticationError exception is raised when no authentication is given
and the token could not be loaded from the default location.
ApiError: An ApiError exception is raised when the schema could not be loaded.
"""
if authentication is None:
token = load_account()
if token is not None:
authentication = TokenAuthentication(token, scheme="token")
else:
raise ApiError('No credentials have been provided or found on disk')
raise AuthenticationError('No credentials have been provided or found on disk')
self.__client = coreapi_client_class(auth=authentication)
self.project_name = project_name
self.base_uri = base_uri
Expand Down
4 changes: 4 additions & 0 deletions src/quantuminspire/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,9 @@ class ProjectQBackendError(Exception):
""" Exception for SDK errors related to the projectq backend."""


class AuthenticationError(Exception):
""" Exception for SDK errors related to authentication."""


class ApiError(Exception):
""" Exception for SDK errors related to the API functionality."""
11 changes: 8 additions & 3 deletions src/quantuminspire/projectq/backend_qx.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
Measure, Ph, Rx, Ry, Rz, S, Sdag, Swap, T, Tdag, X,
Y, Z, Command)
from projectq.types import Qubit
from quantuminspire.exceptions import AuthenticationError
from quantuminspire.api import QuantumInspireAPI
from quantuminspire.exceptions import ProjectQBackendError

Expand All @@ -47,7 +48,7 @@ def __init__(self, num_runs: int = 1024, verbose: int = 0, quantum_inspire_api:
Args:
num_runs: Number of runs to collect statistics (default is 1024).
verbose: Verbosity level, defaults to 0, which produces no extra output.
quantum_inspire_api: Connection to QI platform, required parameter.
quantum_inspire_api: Connection to QI platform, optional parameter.
backend_type: Backend to use for execution. When no backend_type is provided, the default backend will be
used.
"""
Expand All @@ -63,8 +64,12 @@ def __init__(self, num_runs: int = 1024, verbose: int = 0, quantum_inspire_api:
self._measured_ids: List[int] = []
self._allocated_qubits: Set[int] = set()
self._max_qubit_id: int = -1
if not quantum_inspire_api:
raise RuntimeError("Api is required")
if quantum_inspire_api is None:
try:
quantum_inspire_api = QuantumInspireAPI()
except AuthenticationError as ex:
raise AuthenticationError('Make sure you have saved your token credentials on disk or '
'provide a QuantumInspireAPI instance as parameter to QIBackend') from ex
self.quantum_inspire_api: QuantumInspireAPI = quantum_inspire_api
self.backend_type: Optional[Union[Dict[str, Any], int, str]] = backend_type

Expand Down
25 changes: 21 additions & 4 deletions src/tests/quantuminspire/projectq/test_backend_qx.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import io
import unittest
import warnings
import json
import os
import coreapi
from collections import OrderedDict
from unittest.mock import MagicMock, patch

Expand All @@ -27,7 +30,7 @@
Ph, Rx, Ry, Rz, S, Sdag, Swap, T, Tdag, Toffoli, X,
Y, Z)

from quantuminspire.exceptions import ProjectQBackendError
from quantuminspire.exceptions import ProjectQBackendError, AuthenticationError
from quantuminspire.projectq.backend_qx import QIBackend


Expand All @@ -51,9 +54,23 @@ def test_init_has_correct_values(self):
self.assertEqual(backend.quantum_inspire_api, api)
self.assertIsNone(backend.backend_type)

def test_init_raises_runtime_error(self):
api = None
self.assertRaises(RuntimeError, QIBackend, quantum_inspire_api=api)
def test_init_without_api_has_correct_values(self):
os.environ.get = MagicMock()
os.environ.get.return_value = 'token'
coreapi.Client.get = MagicMock()
backend = QIBackend()
self.assertIsInstance(backend.qasm, str)
self.assertNotEqual(backend.quantum_inspire_api, None)
self.assertIsNone(backend.backend_type)

def test_init_raises_no_account_authentication_error(self):
json.load = MagicMock()
json.load.return_value = {'faulty_key': 'faulty_token'}
os.environ.get = MagicMock()
os.environ.get.return_value = None
self.assertRaisesRegex(AuthenticationError, 'Make sure you have saved your token credentials on disk '
'or provide a QuantumInspireAPI instance as parameter to QIBackend',
QIBackend)

def test_cqasm_returns_correct_cqasm_data(self):
api = MockApiClient()
Expand Down
6 changes: 3 additions & 3 deletions src/tests/quantuminspire/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from unittest.mock import Mock, patch, call, MagicMock, mock_open

from quantuminspire.api import QuantumInspireAPI
from quantuminspire.exceptions import ApiError
from quantuminspire.exceptions import ApiError, AuthenticationError
from quantuminspire.job import QuantumInspireJob


Expand Down Expand Up @@ -109,7 +109,7 @@ def test_no_authentication(self):
api = QuantumInspireAPI(base_url, coreapi_client_class=self.coreapi_client)
self.assertEqual(expected, api.document)

def test_no_authentication_raises_api_error(self):
def test_no_authentication_raises_authentication_error(self):
expected_token = 'secret'
json.load = MagicMock()
json.load.return_value = {'wrong_key': expected_token}
Expand All @@ -120,7 +120,7 @@ def test_no_authentication_raises_api_error(self):
url = ''.join([base_url, expected])
self.coreapi_client.getters[url] = expected
with patch("builtins.open", mock_open(read_data="secret_token")) as mock_file:
self.assertRaisesRegex(ApiError, 'No credentials have been provided', QuantumInspireAPI,
self.assertRaisesRegex(AuthenticationError, 'No credentials have been provided', QuantumInspireAPI,
base_url, coreapi_client_class=self.coreapi_client)

def test_load_schema_collects_correct_schema(self):
Expand Down

0 comments on commit 4044df3

Please sign in to comment.