From d00e069cc5007eba56c82f58fb48daee5542c8b8 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Mon, 13 Jun 2022 16:52:32 -0400 Subject: [PATCH 1/9] copy qiskitrc creds --- qiskit_ibm_runtime/accounts/management.py | 10 +++++++++- qiskit_ibm_runtime/accounts/storage.py | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index 3eb91e657..23ef82530 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -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" @@ -163,6 +164,13 @@ def get( if account_name in all_config: return Account.from_saved_format(all_config[account_name]) + if os.path.isfile(_QISKITRC_CONFIG_FILE): + read_qiskitrc(_QISKITRC_CONFIG_FILE, _DEFAULT_ACCOUNT_CONFIG_JSON_FILE) + all_config = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE) + return Account.from_saved_format( + all_config[_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM] + ) + raise AccountNotFoundError("Unable to find account.") @classmethod diff --git a/qiskit_ibm_runtime/accounts/storage.py b/qiskit_ibm_runtime/accounts/storage.py index 7e6fcdcee..bda4fcaa6 100644 --- a/qiskit_ibm_runtime/accounts/storage.py +++ b/qiskit_ibm_runtime/accounts/storage.py @@ -16,7 +16,9 @@ import logging import os from typing import Optional, Dict +from configparser import ConfigParser from .exceptions import AccountAlreadyExistsError +from .account import Account logger = logging.getLogger(__name__) @@ -40,6 +42,26 @@ 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, default_config_file: str) -> None: + """Save qiskitrc credentials into qiskit-ibm.json config file.""" + config_parser = ConfigParser() + config_parser.read(qiskitrc_config_file) + for name in config_parser.sections(): + account_data = dict(config_parser.items(name)) + save_config( + filename=default_config_file, + name="default-ibm-quantum", + overwrite=True, + config=Account( + token=account_data["token"], + url=account_data["url"], + channel="ibm_quantum", + ) + .validate() + .to_saved_format(), + ) + + def read_config( filename: str, name: Optional[str] = None, From 535e317286fcee9c3b8abc8a7a85933d567e25f8 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Tue, 14 Jun 2022 16:06:57 -0400 Subject: [PATCH 2/9] add reno --- qiskit_ibm_runtime/accounts/management.py | 4 ++-- .../notes/load-qiskitrc-creds-4aac54737333e248.yaml | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/load-qiskitrc-creds-4aac54737333e248.yaml diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index 23ef82530..ce2107d3b 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -166,9 +166,9 @@ def get( if os.path.isfile(_QISKITRC_CONFIG_FILE): read_qiskitrc(_QISKITRC_CONFIG_FILE, _DEFAULT_ACCOUNT_CONFIG_JSON_FILE) - all_config = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE) + default_config = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE) return Account.from_saved_format( - all_config[_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM] + default_config[_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM] ) raise AccountNotFoundError("Unable to find account.") diff --git a/releasenotes/notes/load-qiskitrc-creds-4aac54737333e248.yaml b/releasenotes/notes/load-qiskitrc-creds-4aac54737333e248.yaml new file mode 100644 index 000000000..f940c2c62 --- /dev/null +++ b/releasenotes/notes/load-qiskitrc-creds-4aac54737333e248.yaml @@ -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. + + From ad9d53d912ce168d82fac3d01e42398498cfd204 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Thu, 16 Jun 2022 12:17:45 -0400 Subject: [PATCH 3/9] add unit test --- test/account.py | 25 ++++++++++++++++++++++++- test/unit/test_account.py | 16 ++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/test/account.py b/test/account.py index 9ba25a82b..48262b497 100644 --- a/test/account.py +++ b/test/account.py @@ -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 @@ -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", diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 2eb978e3a..776498c49 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -39,6 +39,7 @@ from ..account import ( get_account_config_contents, temporary_account_config_file, + custom_qiskitrc, no_envs, custom_envs, ) @@ -816,6 +817,21 @@ 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 + """ + 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 From ce369f7d57d26b7a04f0d3334a2bbef3408fd8c1 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Thu, 16 Jun 2022 15:56:24 -0400 Subject: [PATCH 4/9] move save_config call --- qiskit_ibm_runtime/accounts/management.py | 14 +++++++++++++- qiskit_ibm_runtime/accounts/storage.py | 17 +++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index ce2107d3b..cd834030d 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -165,7 +165,19 @@ def get( return Account.from_saved_format(all_config[account_name]) if os.path.isfile(_QISKITRC_CONFIG_FILE): - read_qiskitrc(_QISKITRC_CONFIG_FILE, _DEFAULT_ACCOUNT_CONFIG_JSON_FILE) + qiskitrc_data = read_qiskitrc(_QISKITRC_CONFIG_FILE) + save_config( + filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, + name="default-ibm-quantum", + overwrite=True, + config=Account( + token=qiskitrc_data["token"], + url=qiskitrc_data["url"], + channel="ibm_quantum", + ) + .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] diff --git a/qiskit_ibm_runtime/accounts/storage.py b/qiskit_ibm_runtime/accounts/storage.py index bda4fcaa6..a119bc6c2 100644 --- a/qiskit_ibm_runtime/accounts/storage.py +++ b/qiskit_ibm_runtime/accounts/storage.py @@ -18,7 +18,6 @@ from typing import Optional, Dict from configparser import ConfigParser from .exceptions import AccountAlreadyExistsError -from .account import Account logger = logging.getLogger(__name__) @@ -42,24 +41,14 @@ 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, default_config_file: str) -> None: +def read_qiskitrc(qiskitrc_config_file: str) -> Dict[str, str]: """Save qiskitrc credentials into qiskit-ibm.json config file.""" config_parser = ConfigParser() config_parser.read(qiskitrc_config_file) + account_data = {} for name in config_parser.sections(): account_data = dict(config_parser.items(name)) - save_config( - filename=default_config_file, - name="default-ibm-quantum", - overwrite=True, - config=Account( - token=account_data["token"], - url=account_data["url"], - channel="ibm_quantum", - ) - .validate() - .to_saved_format(), - ) + return account_data def read_config( From 13f9dbdef64075c7e1c1f5b9f05132b474c0d332 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Fri, 17 Jun 2022 15:20:06 -0400 Subject: [PATCH 5/9] add instance --- qiskit_ibm_runtime/accounts/management.py | 7 ++++--- test/unit/test_account.py | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index cd834030d..c4a581cc5 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -168,11 +168,12 @@ def get( qiskitrc_data = read_qiskitrc(_QISKITRC_CONFIG_FILE) save_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, - name="default-ibm-quantum", + name=_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM, overwrite=True, config=Account( - token=qiskitrc_data["token"], - url=qiskitrc_data["url"], + token=qiskitrc_data.get("token", None), + url=qiskitrc_data.get("url", None), + instance=qiskitrc_data.get("default_provider", None), channel="ibm_quantum", ) .validate() diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 776498c49..56ae609cd 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -825,6 +825,7 @@ def test_enable_account_by_qiskitrc(self): 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={}): From 67169fa7da55a54aa150909eb6d7c447fb2c0c62 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Fri, 17 Jun 2022 15:22:28 -0400 Subject: [PATCH 6/9] update docstring --- qiskit_ibm_runtime/accounts/storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_ibm_runtime/accounts/storage.py b/qiskit_ibm_runtime/accounts/storage.py index a119bc6c2..bd331c5d5 100644 --- a/qiskit_ibm_runtime/accounts/storage.py +++ b/qiskit_ibm_runtime/accounts/storage.py @@ -42,7 +42,7 @@ def save_config(filename: str, name: str, config: dict, overwrite: bool) -> None def read_qiskitrc(qiskitrc_config_file: str) -> Dict[str, str]: - """Save qiskitrc credentials into qiskit-ibm.json config file.""" + """Read credentials from a qiskitrc config and return as a dictionary.""" config_parser = ConfigParser() config_parser.read(qiskitrc_config_file) account_data = {} From 99c0fc5e26d00da43f30afcd92264868ea7f9bb0 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Sun, 19 Jun 2022 19:59:35 -0400 Subject: [PATCH 7/9] add proxies and verify to config --- qiskit_ibm_runtime/accounts/management.py | 8 ++++++++ test/unit/test_account.py | 1 + 2 files changed, 9 insertions(+) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index c4a581cc5..876ff0cf2 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -13,6 +13,7 @@ """Account management related classes and functions.""" import os +import ast from typing import Optional, Dict from .exceptions import AccountNotFoundError from .account import Account, ChannelType @@ -166,6 +167,11 @@ def get( if os.path.isfile(_QISKITRC_CONFIG_FILE): qiskitrc_data = read_qiskitrc(_QISKITRC_CONFIG_FILE) + proxies = ( + ProxyConfiguration(ast.literal_eval(qiskitrc_data["proxies"])) + if "proxies" in qiskitrc_data + else None + ) save_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM, @@ -174,6 +180,8 @@ def get( token=qiskitrc_data.get("token", None), url=qiskitrc_data.get("url", None), instance=qiskitrc_data.get("default_provider", None), + verify=bool(qiskitrc_data.get("verify", None)), + proxies=proxies, channel="ibm_quantum", ) .validate() diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 56ae609cd..c0218fe9e 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -826,6 +826,7 @@ def test_enable_account_by_qiskitrc(self): url = https://auth.quantum-computing.ibm.com/api verify = True default_provider = ibm-q/open/main + proxies = {{'https': 'localhost:8080'}} """ with custom_qiskitrc(contents=str.encode(str_contents)): with temporary_account_config_file(contents={}): From 8889855c7d03ff1289100de5f7c87eec94dd5b01 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Mon, 20 Jun 2022 14:52:43 -0400 Subject: [PATCH 8/9] update test case --- test/unit/test_account.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/test_account.py b/test/unit/test_account.py index c0218fe9e..d298170ec 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -820,13 +820,14 @@ def test_enable_account_by_name_input_instance(self): def test_enable_account_by_qiskitrc(self): """Test initializing account by a qiskitrc file.""" token = "token-x" + proxies = {"urls": {"https": "localhost:8080"}} str_contents = f""" [ibmq] token = {token} url = https://auth.quantum-computing.ibm.com/api verify = True default_provider = ibm-q/open/main - proxies = {{'https': 'localhost:8080'}} + proxies = {proxies} """ with custom_qiskitrc(contents=str.encode(str_contents)): with temporary_account_config_file(contents={}): From b2c89a23f83f596ae3baa886a838663580ad1a46 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Tue, 21 Jun 2022 20:31:31 -0400 Subject: [PATCH 9/9] set overwrite to false --- qiskit_ibm_runtime/accounts/management.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index 876ff0cf2..c4275f1fe 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -175,7 +175,7 @@ def get( save_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM, - overwrite=True, + overwrite=False, config=Account( token=qiskitrc_data.get("token", None), url=qiskitrc_data.get("url", None),