Skip to content
Closed
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
4 changes: 4 additions & 0 deletions qiskit/providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@
:toctree: ../stubs/

Provider
ProviderV2
ProviderV1
BackendList

Backend
-------
Expand Down Expand Up @@ -152,6 +154,8 @@
# Providers interface
from qiskit.providers.provider import Provider
from qiskit.providers.provider import ProviderV1
from qiskit.providers.provider import ProviderV2
from qiskit.providers.provider import BackendList
from qiskit.providers.backend import Backend
from qiskit.providers.backend import BackendV1
from qiskit.providers.options import Options
Expand Down
105 changes: 105 additions & 0 deletions qiskit/providers/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,108 @@ def __eq__(self, other):
equal. Subclassed providers can override this behavior.
"""
return type(self).__name__ == type(other).__name__


class ProviderV2(Provider, ABC):
"""Base class for a Backend Provider."""
version = 2

def get_backend(self, name=None, **kwargs):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Jay wants to move away from getters and setters - e.g. use provider.backned() for a single backend and provider.backends() for multiple.

"""Return a single backend matching the specified filtering.

Args:
name (str): name of the backend.
**kwargs: dict used for filtering.

Returns:
Backend: a backend matching the filtering.

Raises:
QiskitBackendNotFoundError: if no backend could be found or
more than one backend matches the filtering criteria.
"""
backends = self.backends(name, **kwargs)
if len(backends) > 1:
raise QiskitBackendNotFoundError('More than one backend matches the criteria')
if not backends:
raise QiskitBackendNotFoundError('No backend matches the criteria')

return backends[0]

@abstractmethod
@property
def backends(self):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the docstring, shouldn't this be backend (without the s)? ibmq also just changed from provider.backends.<backend_name> to provider.backend.<backend_name>.

"""The backends provided by this provider.

This can be accessed by name via attributes for example::

my_provider = Provider()
my_provider.backend.backend_name

or called with the signature that matches
:meth:`~qiskit.providers.ProviderV1.backends`, for example::

my_provider = Provider()
my_provider.backend(name='backend_name')

Returns:
BackendsList: A :class:`~qiskit.providers.BackendsList` object
"""
pass

def __eq__(self, other):
"""Equality comparison.

By default, it is assumed that two `Providers` from the same class are
equal. Subclassed providers can override this behavior.
"""
return type(self).__name__ == type(other).__name__


class BackendList:
"""A service class that allows for autocompletion of backends from a provider.

Each backend can be accessed as attribute by backend directly for example for two
backends ``BackendA`` (with a name ``'backend_a'``) and ``BackendB`` (with
a name ``'backend_b'``)::

from qiskit.providers import BackendV1

backends = BackendList([BackendA, BackendB])
backend_a = backends.backend_a.configuration()

would get the backend configuration object for backend_a. For backwards
compatibility a ``BackendList`` object is callable just as
:meth:`qiskit.providers.ProviderV1.backends`. For example::

backends = BackendList([BackendA, BackendB])
backend_list = backends(name='backend_a')

"""

def __init__(self, backends):
"""Initialize a new ``BackendList`` object.

Args:
backends (list): List of :class:`~qiskit.providers.Backend` instances.
"""
self._backends = backends
for backend in backends:
setattr(self, backend.name(), backend)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really a risk right now, but it's possible the backend name doesn't conform to Python identifier standard. ibmq has an to_python_identifier function to deal with that. If a user is using autocomplete, they should hopefully still recognize My@Backend is the same as my_backend.


def __call__(self, name=None, filters=None, **kwargs):
"""A listing of all backends from this provider.

Args:
name (str): The name of a given backend.
filters (callable): A filter function.
Returns:
list: A list of backends, if any.
"""
# pylint: disable=arguments-differ
backends = self._backends
if name:
backends = [
backend for backend in backends if backend.name() == name]

return filter_backends(backends, filters=filters, **kwargs)
13 changes: 13 additions & 0 deletions releasenotes/notes/add-provider-v2-b00575214369d6df.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
features:
- |
A new version of the :class:`~qiskit.providers.Provider` abstract interface,
:class:`~qiskit.providers.ProviderV2`. This new version is fully compatible
with v1 except that :attr:`~qiskit.providers.ProviderV2.backends` is an
attribute that contains a :class:`~qiskit.providers.BackendsList` object
which is callable to retain the behavior of the
:class:`~qiskit.providers.ProviderV1` method
:meth:`~qiskit.providers.ProviderV1.backends` but also enables attribute
access by name for all the backends provided by the ``Provider``.
Implementers of :class:`~qiskit.providers.Provider` should upgrade to
this new version to add the extra functionality.