Skip to content

Commit

Permalink
[SDS-335] Resolve import cycles using postponed annotations (#137)
Browse files Browse the repository at this point in the history
* [SDS-335] Resolve import cycles using postponed annotations

Postponed annotations allow us to use forward code references without having access to them statically, but we can have access at runtime. Using the TYPE_CHECKING constant from typing makes sure that we don't have circular dependencies in the actual code.

The "correct" approach would be to extract interfaces. For example, we could have a generic API interface or a generic Job interface that exposes the methods and properties that we need access to. However, this is not particularly "Pythonese", so this approach seemed more lightweight.

* Address review comments

* Add a .coveragerc so we can ignore TYPE_CHECKING warning lines for coverage
  • Loading branch information
tomrijnbeek authored Mar 17, 2022
1 parent c2e6546 commit 3848324
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 10 deletions.
3 changes: 3 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[report]
exclude_lines =
if TYPE_CHECKING:
13 changes: 9 additions & 4 deletions src/quantuminspire/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,18 @@
:members:
"""

from typing import Dict, Any
from __future__ import annotations

from typing import Dict, Any, TYPE_CHECKING
from coreapi.exceptions import ErrorMessage

if TYPE_CHECKING:
from api import QuantumInspireAPI


class QuantumInspireJob:

def __init__(self, api: Any, job_identifier: int) -> None:
def __init__(self, api: QuantumInspireAPI, job_identifier: int) -> None:
""" Encapsulation of a job.
The :py:class:`QuantumInspireJob` class encapsulates the base job of the API and has
Expand All @@ -39,10 +44,10 @@ def __init__(self, api: Any, job_identifier: int) -> None:
"""
QuantumInspireJob.__check_arguments(api, job_identifier)
self.__job_identifier: int = job_identifier
self.__api: Any = api
self.__api: QuantumInspireAPI = api

@staticmethod
def __check_arguments(api: Any, job_identifier: int) -> None:
def __check_arguments(api: QuantumInspireAPI, job_identifier: int) -> None:
""" Checks whether the supplied arguments are of correct type.
:param api: An instance to the API.
Expand Down
11 changes: 8 additions & 3 deletions src/quantuminspire/qiskit/backend_qx.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

import copy
import io
import json
import uuid
import warnings
from collections import defaultdict, OrderedDict, Counter
from typing import Any, Dict, List, Tuple, Optional, Union
from collections import defaultdict, Counter
from typing import Any, Dict, List, Tuple, Optional, Union, TYPE_CHECKING

import numpy as np
from qiskit.circuit import QuantumCircuit
Expand All @@ -39,6 +41,9 @@
from quantuminspire.qiskit.qi_job import QIJob
from quantuminspire.version import __version__ as quantum_inspire_version

if TYPE_CHECKING:
from quantum_inspire_provider import QuantumInspireProvider


class QuantumInspireBackend(Backend): # type: ignore
DEFAULT_CONFIGURATION = QasmBackendConfiguration(
Expand All @@ -59,7 +64,7 @@ class QuantumInspireBackend(Backend): # type: ignore
)
qobj_warning_issued = False

def __init__(self, api: QuantumInspireAPI, provider: Any,
def __init__(self, api: QuantumInspireAPI, provider: QuantumInspireProvider,
configuration: Optional[QasmBackendConfiguration] = None) -> None:
""" Python implementation of a quantum simulator using Quantum Inspire API.
Expand Down
11 changes: 8 additions & 3 deletions src/quantuminspire/qiskit/qi_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

import time
from typing import List, Optional, Any, Dict, Callable
from typing import List, Optional, Any, Dict, Callable, TYPE_CHECKING

from qiskit.providers import JobError, JobTimeoutError, JobV1 as Job
from qiskit.providers.backend import Backend
from qiskit.providers.jobstatus import JobStatus, JOB_FINAL_STATES
from qiskit.result.models import ExperimentResult
from qiskit.qobj import QasmQobj, QasmQobjExperiment
Expand All @@ -27,6 +28,9 @@
from quantuminspire.qiskit.qi_result import QIResult
from quantuminspire.version import __version__ as quantum_inspire_version

if TYPE_CHECKING:
from backend_qx import QuantumInspireBackend


class QIJob(Job): # type: ignore
"""
Expand Down Expand Up @@ -68,7 +72,8 @@ class QIJob(Job): # type: ignore
"""

def __init__(self, backend: Backend, job_id: str, api: QuantumInspireAPI, qobj: Optional[QasmQobj] = None) -> None:
def __init__(self, backend: QuantumInspireBackend, job_id: str, api: QuantumInspireAPI,
qobj: Optional[QasmQobj] = None) -> None:
"""
A QIJob object is normally not constructed directly.
Expand Down

0 comments on commit 3848324

Please sign in to comment.