Skip to content

Commit

Permalink
Centralize all backend imports from a single pulser.backends module (
Browse files Browse the repository at this point in the history
…#678)

* Centralize all backend imports

* Unit tests

* Update backends tutorial

* Fix error message
  • Loading branch information
HGSilveri authored Apr 25, 2024
1 parent 34b41b4 commit 716b86b
Show file tree
Hide file tree
Showing 5 changed files with 546 additions and 455 deletions.
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ per-file-ignores =
# F401 Module imported but unused
tests/*: D100, D101, D102, D103
__init__.py: F401
pulser-core/pulser/backends.py: F401
setup.py: D100
1 change: 1 addition & 0 deletions pulser-core/pulser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
devices as devices,
sampler as sampler,
backend as backend,
backends as backends,
)

__all__ = [
Expand Down
52 changes: 52 additions & 0 deletions pulser-core/pulser/backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright 2024 Pulser Development Team
#
# 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.
"""A module gathering all available backends."""
from __future__ import annotations

import importlib
from typing import TYPE_CHECKING, Type

from pulser.backend.abc import Backend

if TYPE_CHECKING:
from pulser.backend import QPUBackend as QPUBackend
from pulser_pasqal import EmuFreeBackend as EmuFreeBackend
from pulser_pasqal import EmuTNBackend as EmuTNBackend
from pulser_simulation import QutipBackend as QutipBackend

_BACKENDS = {
"QPUBackend": "pulser.backend",
"QutipBackend": "pulser_simulation",
"EmuFreeBackend": "pulser_pasqal",
"EmuTNBackend": "pulser_pasqal",
}


# This prevents * imports to attempt importing unavailable backends
__all__: list[str] = []


def __getattr__(name: str) -> Type[Backend]:
if name not in _BACKENDS:
raise AttributeError(f"Module {__name__!r} has no attribute {name!r}.")
try:
return getattr( # type: ignore
importlib.import_module(_BACKENDS[name]),
name,
)
except ModuleNotFoundError:
raise AttributeError(
f"{name!r} requires the {_BACKENDS[name]!r} package. To install "
f"it, run `pip install {_BACKENDS[name]}`."
)
46 changes: 46 additions & 0 deletions tests/test_backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright 2024 Pulser Development Team
#
# 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 sys

import pytest

import pulser
from pulser.backend.abc import Backend
from pulser.backends import _BACKENDS


@pytest.mark.parametrize("backend, missing_package", list(_BACKENDS.items()))
def test_missing_package(monkeypatch, backend, missing_package):
monkeypatch.setitem(sys.modules, missing_package, None)
with pytest.raises(
AttributeError,
match=f"{backend!r} requires the {missing_package!r} package. "
f"To install it, run `pip install {missing_package}`",
):
getattr(pulser.backends, backend)


def test_missing_backend():
with pytest.raises(
AttributeError,
match="Module 'pulser.backends' has no attribute 'SpecialBackend'",
):
pulser.backends.SpecialBackend


@pytest.mark.parametrize("backend_name", list(_BACKENDS))
def test_succesful_imports(backend_name):
backend = getattr(pulser.backends, backend_name)
assert issubclass(backend, Backend)
901 changes: 446 additions & 455 deletions tutorials/advanced_features/Backends for Sequence Execution.ipynb

Large diffs are not rendered by default.

0 comments on commit 716b86b

Please sign in to comment.