Skip to content
Merged
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
5 changes: 3 additions & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ disable=no-self-use, # disabled as it is too verbose
import-outside-toplevel,
docstring-first-line-empty,
no-name-in-module, # remove when pylint behaves
import-error # remove when pylint behaves
import-error, # remove when pylint behaves
bad-continuation, bad-whitespace # differences of opinion with black


[REPORTS]
Expand Down Expand Up @@ -212,7 +213,7 @@ max-nested-blocks=5
[FORMAT]

# Maximum number of characters on a single line.
max-line-length=100
max-line-length=105
Comment thread
rathishcholarajan marked this conversation as resolved.

# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mypy:
mypy --module qiskit_ibm_runtime

style:
pycodestyle qiskit_ibm_runtime test
black --check qiskit_ibm_runtime test

test:
python -m unittest -v
Expand All @@ -36,4 +36,7 @@ test3:
python -m unittest -v test/ibm/test_ibm_job_attributes.py test/ibm/test_ibm_job.py test/ibm/websocket/test_websocket.py test/ibm/websocket/test_websocket_integration.py

runtime_integration:
python -m unittest -v test/ibm/runtime/test_runtime_integration.py
python -m unittest -v test/ibm/runtime/test_runtime_integration.py

black:
black qiskit_ibm_runtime test
32 changes: 19 additions & 13 deletions qiskit_ibm_runtime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,17 +296,17 @@ def interim_result_callback(job_id, interim_result):
setup_logger(logger)

# Constants used by the IBM Quantum logger.
QISKIT_IBM_RUNTIME_LOGGER_NAME = 'qiskit_ibm_runtime'
QISKIT_IBM_RUNTIME_LOGGER_NAME = "qiskit_ibm_runtime"
"""The name of the IBM Quantum logger."""
QISKIT_IBM_RUNTIME_LOG_LEVEL = 'QISKIT_IBM_RUNTIME_LOG_LEVEL'
QISKIT_IBM_RUNTIME_LOG_LEVEL = "QISKIT_IBM_RUNTIME_LOG_LEVEL"
"""The environment variable name that is used to set the level for the IBM Quantum logger."""
QISKIT_IBM_RUNTIME_LOG_FILE = 'QISKIT_IBM_RUNTIME_LOG_FILE'
QISKIT_IBM_RUNTIME_LOG_FILE = "QISKIT_IBM_RUNTIME_LOG_FILE"
"""The environment variable name that is used to set the file for the IBM Quantum logger."""


def least_busy(
backends: List[Union[Backend, BaseBackend]],
reservation_lookahead: Optional[int] = 60
backends: List[Union[Backend, BaseBackend]],
reservation_lookahead: Optional[int] = 60,
) -> Union[Backend, BaseBackend]:
"""Return the least busy backend from a list.

Expand All @@ -330,27 +330,33 @@ def least_busy(
does not have the ``pending_jobs`` attribute in its status.
"""
if not backends:
raise IBMError('Unable to find the least_busy '
'backend from an empty list.') from None
raise IBMError(
"Unable to find the least_busy backend from an empty list."
) from None
try:
candidates = []
now = datetime.now()
for back in backends:
backend_status = back.status()
if not backend_status.operational or backend_status.status_msg != 'active':
if not backend_status.operational or backend_status.status_msg != "active":
continue
if reservation_lookahead and isinstance(back, IBMBackend):
end_time = now + timedelta(minutes=reservation_lookahead)
try:
if back.reservations(now, end_time):
continue
except Exception as err: # pylint: disable=broad-except
logger.warning("Unable to find backend reservation information. "
"It will not be taken into consideration. %s", str(err))
logger.warning(
"Unable to find backend reservation information. "
"It will not be taken into consideration. %s",
str(err),
)
candidates.append(back)
if not candidates:
raise IBMError('No backend matches the criteria.')
raise IBMError("No backend matches the criteria.")
return min(candidates, key=lambda b: b.status().pending_jobs)
except AttributeError as ex:
raise IBMError('A backend in the list does not have the `pending_jobs` '
'attribute in its status.') from ex
raise IBMError(
"A backend in the list does not have the `pending_jobs` "
"attribute in its status."
) from ex
31 changes: 15 additions & 16 deletions qiskit_ibm_runtime/api/clients/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,25 @@
class AccountClient(BaseClient):
"""Client for accessing an individual IBM Quantum account."""

def __init__(
self,
credentials: Credentials,
**request_kwargs: Any
) -> None:
def __init__(self, credentials: Credentials, **request_kwargs: Any) -> None:
"""AccountClient constructor.

Args:
credentials: Account credentials.
**request_kwargs: Arguments for the request ``Session``.
"""
self._session = RetrySession(
credentials.base_url, credentials.access_token, **request_kwargs)
credentials.base_url, credentials.access_token, **request_kwargs
)
# base_api is used to handle endpoints that don't include h/g/p.
# account_api is for h/g/p.
self.base_api = Api(self._session)
self.account_api = Account(session=self._session, hub=credentials.hub,
group=credentials.group, project=credentials.project)
self.account_api = Account(
session=self._session,
hub=credentials.hub,
group=credentials.group,
project=credentials.project,
)
self._credentials = credentials

# Backend-related public functions.
Expand Down Expand Up @@ -75,9 +76,7 @@ def backend_status(self, backend_name: str) -> Dict[str, Any]:
return self.account_api.backend(backend_name).status()

def backend_properties(
self,
backend_name: str,
datetime: Optional[datetime] = None
self, backend_name: str, datetime: Optional[datetime] = None
) -> Dict[str, Any]:
"""Return the properties of the backend.

Expand Down Expand Up @@ -114,10 +113,10 @@ def backend_job_limit(self, backend_name: str) -> Dict[str, Any]:
return self.account_api.backend(backend_name).job_limit()

def backend_reservations(
self,
backend_name: str,
start_datetime: Optional[datetime] = None,
end_datetime: Optional[datetime] = None
self,
backend_name: str,
start_datetime: Optional[datetime] = None,
end_datetime: Optional[datetime] = None,
) -> List:
"""Return backend reservation information.

Expand All @@ -129,7 +128,7 @@ def backend_reservations(
Returns:
Backend reservation information.
"""
backend_api = Backend(self._session, backend_name, '/Network')
backend_api = Backend(self._session, backend_name, "/Network")
return backend_api.reservations(start_datetime, end_datetime)

def my_reservations(self) -> List:
Expand Down
36 changes: 21 additions & 15 deletions qiskit_ibm_runtime/api/clients/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ def _init_service_clients(self, **request_kwargs: Any) -> Api:
self._service_urls = self.user_urls()

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

return base_api

Expand All @@ -73,19 +74,22 @@ def _request_access_token(self) -> str:
"""
try:
response = self.auth_api.login(self.api_token)
return response['id']
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 = original_exception.response # pylint: disable=no-member
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']
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.
Expand All @@ -106,7 +110,7 @@ def user_urls(self) -> Dict[str, str]:
* ``services`: The API URL for additional services.
"""
response = self.auth_api.user_info()
return response['urls']
return response["urls"]

def user_hubs(self) -> List[Dict[str, str]]:
"""Retrieve the hub/group/project sets available to the user.
Expand All @@ -122,15 +126,17 @@ def user_hubs(self) -> List[Dict[str, str]]:

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}
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'):
if project.get("isDefault"):
hubs.insert(0, entry)
else:
hubs.append(entry)
Expand Down
Loading