Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge qiskit-ibm-provider into qiskit-ibm-runtime #1285

Merged
merged 14 commits into from
Feb 1, 2024
4 changes: 2 additions & 2 deletions qiskit_ibm_runtime/accounts/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
from urllib.parse import urlparse

from requests.auth import AuthBase
from qiskit_ibm_provider.proxies import ProxyConfiguration
from qiskit_ibm_provider.utils.hgp import from_instance_format
from ..proxies import ProxyConfiguration
from ..utils.hgp import from_instance_format

from .exceptions import InvalidAccountError, CloudResourceNameResolutionError
from ..api.auth import QuantumAuth, CloudAuth
Expand Down
2 changes: 1 addition & 1 deletion qiskit_ibm_runtime/accounts/management.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import os
from typing import Optional, Dict

from qiskit_ibm_provider.proxies import ProxyConfiguration
from ..proxies import ProxyConfiguration

from .exceptions import AccountNotFoundError
from .account import Account, ChannelType
Expand Down
2 changes: 1 addition & 1 deletion qiskit_ibm_runtime/api/client_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""Represent IBM Quantum account client parameters."""

from typing import Dict, Optional, Any, Union
from qiskit_ibm_provider.proxies import ProxyConfiguration
from ..proxies import ProxyConfiguration

from ..utils import get_runtime_api_base_url
from ..api.auth import QuantumAuth, CloudAuth
Expand Down
8 changes: 4 additions & 4 deletions qiskit_ibm_runtime/api/clients/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

"""IBM Quantum API clients."""

from qiskit_ibm_provider.api.clients.base import BaseClient, WebsocketClientCloseCode
from qiskit_ibm_provider.api.clients.auth import AuthClient
from qiskit_ibm_provider.api.clients.version import VersionClient
from qiskit_ibm_provider.api.clients.runtime_ws import RuntimeWebsocketClient
from .base_websocket_client import WebsocketClientCloseCode
from .auth import AuthClient
from .version import VersionClient
from .runtime_ws import RuntimeWebsocketClient
from .runtime import RuntimeClient
172 changes: 172 additions & 0 deletions qiskit_ibm_runtime/api/clients/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Client for accessing IBM Quantum authentication services."""

from typing import Dict, List, Optional, Any, Union
from requests.exceptions import RequestException

from ..auth import QuantumAuth
from ..exceptions import AuthenticationLicenseError, RequestsApiError
from ..rest import Api
from ..session import RetrySession
from ..client_parameters import ClientParameters


class AuthClient:
"""Client for accessing IBM Quantum authentication services."""

def __init__(self, client_params: ClientParameters) -> None:
"""AuthClient constructor.

Args:
client_params: Parameters used for server connection.
"""
self.api_token = client_params.token
self.auth_url = client_params.url
self._service_urls = {} # type: ignore[var-annotated]

self.auth_api = Api(RetrySession(self.auth_url, **client_params.connection_parameters()))
self.base_api = self._init_service_clients(**client_params.connection_parameters())

def _init_service_clients(self, **request_kwargs: Any) -> Api:
"""Initialize the clients used for communicating with the API.

Args:
**request_kwargs: Arguments for the request ``Session``.

Returns:
Client for the API server.
"""
# Request an access token.
self.access_token = self._request_access_token()
self.auth_api.session.auth = QuantumAuth(access_token=self.access_token)
self._service_urls = self.user_urls()

# Create the api server client, using the access token.
base_api = Api(
RetrySession(
self._service_urls["http"],
auth=QuantumAuth(access_token=self.access_token),
**request_kwargs,
)
)

return base_api

def _request_access_token(self) -> str:
"""Request a new access token from the API authentication service.

Returns:
A new access token.

Raises:
AuthenticationLicenseError: If the user hasn't accepted the license agreement.
RequestsApiError: If the request failed.
"""
try:
response = self.auth_api.login(self.api_token)
return response["id"]
except RequestsApiError as ex:
# Get the original exception that raised.
original_exception = ex.__cause__

if isinstance(original_exception, RequestException):
# Get the response from the original request exception.
error_response = (
# pylint: disable=no-member
original_exception.response
)
if error_response is not None and error_response.status_code == 401:
try:
error_code = error_response.json()["error"]["name"]
if error_code == "ACCEPT_LICENSE_REQUIRED":
message = error_response.json()["error"]["message"]
raise AuthenticationLicenseError(message)
except (ValueError, KeyError):
# the response did not contain the expected json.
pass
raise

# User account-related public functions.

def user_urls(self) -> Dict[str, str]:
"""Retrieve the API URLs from the authentication service.

Returns:
A dict with the base URLs for the services. Currently
supported keys are:

* ``http``: The API URL for HTTP communication.
* ``ws``: The API URL for websocket communication.
* ``services`: The API URL for additional services.
"""
response = self.auth_api.user_info()
return response["urls"]

def user_hubs(self) -> List[Dict[str, str]]:
"""Retrieve the hub/group/project sets available to the user.

The first entry in the list will be the default set, as indicated by
the ``isDefault`` field from the API.

Returns:
A list of dictionaries with the hub, group, and project values keyed by
``hub``, ``group``, and ``project``, respectively.
"""
response = self.base_api.hubs()

hubs = [] # type: ignore[var-annotated]
for hub in response:
hub_name = hub["name"]
for group_name, group in hub["groups"].items():
for project_name, project in group["projects"].items():
entry = {
"hub": hub_name,
"group": group_name,
"project": project_name,
}

# Move to the top if it is the default h/g/p.
if project.get("isDefault"):
hubs.insert(0, entry)
else:
hubs.append(entry)

return hubs

# Miscellaneous public functions.

def api_version(self) -> Dict[str, Union[str, bool]]:
"""Return the version of the API.

Returns:
API version.
"""
return self.base_api.version()

def current_access_token(self) -> Optional[str]:
"""Return the current access token.

Returns:
The access token in use.
"""
return self.access_token

def current_service_urls(self) -> Dict:
"""Return the current service URLs.

Returns:
A dict with the base URLs for the services, in the same
format as :meth:`user_urls()`.
"""
return self._service_urls
4 changes: 1 addition & 3 deletions qiskit_ibm_runtime/api/clients/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@
from datetime import datetime as python_datetime
from abc import ABC, abstractmethod

from qiskit_ibm_provider.api.clients.base import BaseClient

logger = logging.getLogger(__name__)


class BaseBackendClient(BaseClient, ABC):
class BaseBackendClient(ABC):
"""Client for accessing backend information."""

@abstractmethod
Expand Down
Loading
Loading