From 2b8a48ff99a65b52089e327001bd319be71115ea Mon Sep 17 00:00:00 2001 From: Etienne Wodey <44871469+airwoodix@users.noreply.github.com> Date: Mon, 6 May 2024 12:38:09 +0200 Subject: [PATCH] Improve docstring coverage (#154) --- .pre-commit-config.yaml | 6 ++++++ pyproject.toml | 3 ++- qiskit_aqt_provider/aqt_provider.py | 5 +++++ qiskit_aqt_provider/aqt_resource.py | 25 ++++++++++++++---------- qiskit_aqt_provider/persistence.py | 1 + qiskit_aqt_provider/test/fixtures.py | 1 + qiskit_aqt_provider/test/resources.py | 14 +++++++++++++ qiskit_aqt_provider/transpiler_plugin.py | 4 ++++ test/test_job_persistence.py | 3 ++- test/test_options.py | 2 ++ test/test_resource.py | 2 ++ 11 files changed, 54 insertions(+), 12 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9b51448..e8d2b78 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,6 +27,12 @@ repos: rev: "v1.21.0" hooks: - id: typos + - repo: https://github.com/econchick/interrogate + rev: "1.7.0" + hooks: + - id: interrogate + args: [-v, qiskit_aqt_provider, test] + pass_filenames: false # needed if excluding files with pyproject.toml or setup.cfg - repo: local hooks: - id: check-api-models diff --git a/pyproject.toml b/pyproject.toml index eaec4da..b1e74c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -231,8 +231,9 @@ fail_under = 99 [tool.interrogate] ignore-module = true ignore-nested-functions = true +ignore-magic = true exclude = ["qiskit_aqt_provider/api_models_generated.py"] -fail-under = 88 +fail-under = 100 [tool.typos.files] ignore-hidden = false diff --git a/qiskit_aqt_provider/aqt_provider.py b/qiskit_aqt_provider/aqt_provider.py index 385e326..303b21a 100644 --- a/qiskit_aqt_provider/aqt_provider.py +++ b/qiskit_aqt_provider/aqt_provider.py @@ -75,6 +75,11 @@ class BackendsTable(Sequence[AQTResource]): """ def __init__(self, backends: list[AQTResource]): + """Initialize the table. + + Args: + backends: list of available backends. + """ self.backends = backends self.headers = ["Workspace ID", "Resource ID", "Description", "Resource type"] diff --git a/qiskit_aqt_provider/aqt_resource.py b/qiskit_aqt_provider/aqt_resource.py index b7fc8f1..26fba11 100644 --- a/qiskit_aqt_provider/aqt_resource.py +++ b/qiskit_aqt_provider/aqt_resource.py @@ -161,6 +161,7 @@ def result(self, job_id: UUID) -> api_models.JobResponse: return api_models.Response.model_validate(resp.json()) def configuration(self) -> BackendConfiguration: + """Legacy Qiskit backend configuration.""" warnings.warn( "The configuration() method is deprecated and will be removed in a " "future release. Instead you should access these attributes directly " @@ -171,36 +172,32 @@ def configuration(self) -> BackendConfiguration: ) return self._configuration - def properties(self) -> None: - warnings.warn( # pragma: no cover - "The properties() method is deprecated and will be removed in a " - "future release. Instead you should access these attributes directly " - "off the object or via the .target attribute. You can refer to qiskit " - "backend interface transition guide for the exact changes: " - "https://docs.quantum.ibm.com/api/qiskit/providers#migrating-between-backend-api-versions", - DeprecationWarning, - ) - @property def max_circuits(self) -> int: + """Maximum number of circuits per batch.""" return 2000 @property def target(self) -> Target: + """Transpilation target for this backend.""" return self._target @classmethod def _default_options(cls) -> QiskitOptions: + """Default backend options, in Qiskit format.""" return QiskitOptions() @property def options(self) -> AQTOptions: + """Configured backend options.""" return self._options def get_scheduling_stage_plugin(self) -> str: + """Name of the custom scheduling stage plugin for the Qiskit transpiler.""" return "aqt" def get_translation_stage_plugin(self) -> str: + """Name of the custom translation stage plugin for the Qiskit transpiler.""" return "aqt" def run(self, circuits: Union[QuantumCircuit, list[QuantumCircuit]], **options: Any) -> AQTJob: @@ -279,12 +276,20 @@ def qubit_states_from_int(state: int, num_qubits: int) -> list[int]: @dataclass(frozen=True) class SimulatorJob: + """Data for a job running on a local simulator.""" + job: AerJob + """Simulation backend job handle.""" + circuits: list[QuantumCircuit] + """Quantum circuits to evaluate.""" + shots: int + """Number of repetitions of each circuit.""" @property def job_id(self) -> UUID: + """The job's unique identifier.""" return UUID(hex=self.job.job_id()) diff --git a/qiskit_aqt_provider/persistence.py b/qiskit_aqt_provider/persistence.py index 6c6fad4..e179426 100644 --- a/qiskit_aqt_provider/persistence.py +++ b/qiskit_aqt_provider/persistence.py @@ -42,6 +42,7 @@ class Circuits: """ def __init__(self, circuits: list[QuantumCircuit]) -> None: + """Initialize a container filled with the given circuits.""" self.circuits = circuits @classmethod diff --git a/qiskit_aqt_provider/test/fixtures.py b/qiskit_aqt_provider/test/fixtures.py index 3ba48b9..c6dbfeb 100644 --- a/qiskit_aqt_provider/test/fixtures.py +++ b/qiskit_aqt_provider/test/fixtures.py @@ -31,6 +31,7 @@ class MockSimulator(OfflineSimulatorResource): """Offline simulator that keeps track of the submitted circuits.""" def __init__(self, *, noisy: bool) -> None: + """Initialize the mocked simulator backend.""" super().__init__( AQTProvider(""), resource_id=api_models.ResourceId( diff --git a/qiskit_aqt_provider/test/resources.py b/qiskit_aqt_provider/test/resources.py index d93b580..be1d038 100644 --- a/qiskit_aqt_provider/test/resources.py +++ b/qiskit_aqt_provider/test/resources.py @@ -169,6 +169,11 @@ def __init__( @override def submit(self, job: AQTJob) -> uuid.UUID: + """Handle an execution request for a given job. + + If the backend always cancels job, the job is immediately cancelled. + Otherwise, register the passed job as the active one on the backend. + """ test_job = TestJob(job.circuits, job.options.shots, error_message=self.error_message) if self.always_cancel: @@ -179,6 +184,14 @@ def submit(self, job: AQTJob) -> uuid.UUID: @override def result(self, job_id: uuid.UUID) -> api_models.JobResponse: + """Handle a results request for a given job. + + Apply the logic configured when initializing the backend to + build an API result payload. + + Raises: + UnknownJobError: the given job ID doesn't correspond to the active job's ID. + """ if self.job is None or self.job.job_id != job_id: # pragma: no cover raise api_models.UnknownJobError(str(job_id)) @@ -206,6 +219,7 @@ class DummyResource(AQTResource): """A non-functional resource, for testing purposes.""" def __init__(self, token: str) -> None: + """Initialize the dummy backend.""" super().__init__( AQTProvider(token), resource_id=api_models.ResourceId( diff --git a/qiskit_aqt_provider/transpiler_plugin.py b/qiskit_aqt_provider/transpiler_plugin.py index 131c7af..7624343 100644 --- a/qiskit_aqt_provider/transpiler_plugin.py +++ b/qiskit_aqt_provider/transpiler_plugin.py @@ -72,6 +72,7 @@ class RewriteRxAsR(TransformationPass): @map_exceptions(TranspilerError) def run(self, dag: DAGCircuit) -> DAGCircuit: + """Apply the transformation pass.""" for node in dag.gate_nodes(): if node.name == "rx": (theta,) = node.op.params @@ -91,6 +92,7 @@ def pass_manager( pass_manager_config: PassManagerConfig, optimization_level: Optional[int] = None, # noqa: ARG002 ) -> PassManager: + """Pass manager for the scheduling phase.""" if isinstance(pass_manager_config.target, UnboundParametersTarget): return PassManager([]) @@ -174,6 +176,7 @@ class WrapRxxAngles(TransformationPass): @map_exceptions(TranspilerError) def run(self, dag: DAGCircuit) -> DAGCircuit: + """Apply the transformation pass.""" for node in dag.gate_nodes(): if node.name == "rxx": (theta,) = node.op.params @@ -200,6 +203,7 @@ def pass_manager( pass_manager_config: PassManagerConfig, optimization_level: Optional[int] = None, ) -> PassManager: + """Pass manager for the translation stage.""" translation_pm = common.generate_translation_passmanager( target=pass_manager_config.target, basis_gates=pass_manager_config.basis_gates, diff --git a/test/test_job_persistence.py b/test/test_job_persistence.py index a71e8b1..72dffb5 100644 --- a/test/test_job_persistence.py +++ b/test/test_job_persistence.py @@ -102,8 +102,9 @@ def test_job_persistence_transaction_online_backend(httpx_mock: HTTPXMock, tmp_p ) backend = AQTResource(provider, resource_id) - # Mocked portal state: holds details of the submitted jobs class PortalJob(NamedTuple): + """Mocked portal state: holds details of the submitted jobs.""" + circuits: list[api_models_generated.QuantumCircuit] workspace_id: str resource_id: str diff --git a/test/test_options.py b/test/test_options.py index e58f499..2768ff7 100644 --- a/test/test_options.py +++ b/test/test_options.py @@ -20,6 +20,8 @@ class OptionsFactory(ModelFactory[AQTOptions]): + """Factory of random but well-formed options data.""" + __model__ = AQTOptions query_timeout_seconds = 10.0 diff --git a/test/test_resource.py b/test/test_resource.py index ed5c265..75f391a 100644 --- a/test/test_resource.py +++ b/test/test_resource.py @@ -40,6 +40,8 @@ class OptionsFactory(ModelFactory[AQTOptions]): + """Factory of random but well-formed options data.""" + __model__ = AQTOptions query_timeout_seconds = 10.0