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

Use qiskitrc credentials if default config doesn't exist #370

Merged
merged 19 commits into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from 10 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
23 changes: 22 additions & 1 deletion qiskit_ibm_runtime/accounts/management.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
from .exceptions import AccountNotFoundError
from .account import Account, ChannelType
from ..proxies import ProxyConfiguration
from .storage import save_config, read_config, delete_config
from .storage import save_config, read_config, delete_config, read_qiskitrc

_DEFAULT_ACCOUNT_CONFIG_JSON_FILE = os.path.join(
os.path.expanduser("~"), ".qiskit", "qiskit-ibm.json"
)
_QISKITRC_CONFIG_FILE = os.path.join(os.path.expanduser("~"), ".qiskit", "qiskitrc")
_DEFAULT_ACCOUNT_NAME = "default"
_DEFAULT_ACCOUNT_NAME_LEGACY = "default-legacy"
_DEFAULT_ACCOUNT_NAME_CLOUD = "default-cloud"
Expand Down Expand Up @@ -163,6 +164,26 @@ def get(
if account_name in all_config:
return Account.from_saved_format(all_config[account_name])

if os.path.isfile(_QISKITRC_CONFIG_FILE):
qiskitrc_data = read_qiskitrc(_QISKITRC_CONFIG_FILE)
save_config(
filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE,
name=_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM,
overwrite=True,
rathishcholarajan marked this conversation as resolved.
Show resolved Hide resolved
config=Account(
token=qiskitrc_data.get("token", None),
url=qiskitrc_data.get("url", None),
instance=qiskitrc_data.get("default_provider", None),
channel="ibm_quantum",
kt474 marked this conversation as resolved.
Show resolved Hide resolved
)
.validate()
.to_saved_format(),
)
default_config = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE)
return Account.from_saved_format(
default_config[_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM]
)

raise AccountNotFoundError("Unable to find account.")

@classmethod
Expand Down
11 changes: 11 additions & 0 deletions qiskit_ibm_runtime/accounts/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import logging
import os
from typing import Optional, Dict
from configparser import ConfigParser
from .exceptions import AccountAlreadyExistsError

logger = logging.getLogger(__name__)
Expand All @@ -40,6 +41,16 @@ def save_config(filename: str, name: str, config: dict, overwrite: bool) -> None
json.dump(data, json_out, sort_keys=True, indent=4)


def read_qiskitrc(qiskitrc_config_file: str) -> Dict[str, str]:
"""Read credentials from a qiskitrc config and return as a dictionary."""
config_parser = ConfigParser()
config_parser.read(qiskitrc_config_file)
account_data = {}
for name in config_parser.sections():
account_data = dict(config_parser.items(name))
return account_data


def read_config(
filename: str,
name: Optional[str] = None,
Expand Down
8 changes: 8 additions & 0 deletions releasenotes/notes/load-qiskitrc-creds-4aac54737333e248.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
upgrade:
- |
When initializing :class:`~qiskit_ibm_runtime.QiskitRuntimeService`, and there are no
accounts found, if a qiskitrc file exists, credentials from this file will be saved
as the ``default-ibm-quantum`` account.


25 changes: 24 additions & 1 deletion test/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def side_effect(self, filename_):


class temporary_account_config_file(ContextDecorator):
"""Context manager that uses a temporary qiskitrc."""
"""Context manager that uses a temporary json file."""

# pylint: disable=invalid-name

Expand All @@ -125,6 +125,29 @@ def __exit__(self, *exc):
management._DEFAULT_ACCOUNT_CONFIG_JSON_FILE = self.account_config_json_backup


class custom_qiskitrc(ContextDecorator):
"""Context manager that uses a temporary qiskitrc."""

# pylint: disable=invalid-name

def __init__(self, contents=b""):
# Create a temporary file with the contents.
self.tmp_file = NamedTemporaryFile()
self.tmp_file.write(contents)
self.tmp_file.flush()
self.default_qiskitrc_file_original = management._QISKITRC_CONFIG_FILE

def __enter__(self):
# Temporarily modify the default location of the qiskitrc file.
management._QISKITRC_CONFIG_FILE = self.tmp_file.name
return self

def __exit__(self, *exc):
# Delete the temporary file and restore the default location.
self.tmp_file.close()
management._QISKITRC_CONFIG_FILE = self.default_qiskitrc_file_original


def get_account_config_contents(
name=None,
channel="ibm_cloud",
Expand Down
17 changes: 17 additions & 0 deletions test/unit/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from ..account import (
get_account_config_contents,
temporary_account_config_file,
custom_qiskitrc,
no_envs,
custom_envs,
)
Expand Down Expand Up @@ -816,6 +817,22 @@ def test_enable_account_by_name_input_instance(self):
self.assertTrue(service._account)
self.assertEqual(service._account.instance, instance)

def test_enable_account_by_qiskitrc(self):
"""Test initializing account by a qiskitrc file."""
token = "token-x"
str_contents = f"""
[ibmq]
token = {token}
url = https://auth.quantum-computing.ibm.com/api
verify = True
default_provider = ibm-q/open/main
"""
with custom_qiskitrc(contents=str.encode(str_contents)):
with temporary_account_config_file(contents={}):
service = FakeRuntimeService()
self.assertTrue(service._account)
self.assertEqual(service._account.token, token)

def test_enable_account_by_channel_input_instance(self):
"""Test initializing account by channel and input instance."""
instance = uuid.uuid4().hex
Expand Down