Skip to content
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c753386
Allow user to define a default account as an environment variable
merav-aharoni Aug 15, 2023
2e2a825
Merge branch 'main' into default_channel
merav-aharoni Aug 15, 2023
4e72475
Fixed test
merav-aharoni Aug 20, 2023
7a3c84a
Merge branch 'default_channel' of github.com:merav-aharoni/qiskit-ibm…
merav-aharoni Aug 20, 2023
e4b3151
Merge branch 'main' into default_channel
merav-aharoni Aug 20, 2023
f576063
Fixed mistaken paste
merav-aharoni Aug 20, 2023
204af31
Merge branch 'default_channel' of github.com:merav-aharoni/qiskit-ibm…
merav-aharoni Aug 20, 2023
d0f4027
Cleaned up test
merav-aharoni Aug 20, 2023
60fedee
Moved test to TestAccountManager
merav-aharoni Aug 21, 2023
3b12715
Merge branch 'main' into default_channel
merav-aharoni Aug 22, 2023
70298ba
Merge branch 'main' into default_channel
merav-aharoni Aug 27, 2023
66ea746
Added ability to define default channel in save_account
merav-aharoni Aug 29, 2023
0818a03
Merge branch 'default_channel' of github.com:merav-aharoni/qiskit-ibm…
merav-aharoni Aug 29, 2023
c28302a
Cleaned up code, fixed bugs
merav-aharoni Aug 29, 2023
5bdf57b
Merge branch 'main' into default_channel
merav-aharoni Aug 29, 2023
6569a53
Changed name of parameter
merav-aharoni Aug 29, 2023
4259573
Merge with main
merav-aharoni Aug 29, 2023
bc88652
Merge branch 'main' into default_channel
merav-aharoni Aug 30, 2023
27a56cb
Added test. Cleaned up code surrounding preferences of channel selection
merav-aharoni Aug 30, 2023
08c48eb
Merge branch 'default_channel' of github.com:merav-aharoni/qiskit-ibm…
merav-aharoni Aug 30, 2023
e50975c
black and lint
merav-aharoni Aug 30, 2023
778108a
Fixed bug when json file was empty
merav-aharoni Aug 30, 2023
22f58b6
Merge branch 'main' into default_channel
merav-aharoni Aug 31, 2023
892e6ce
Code cleanup and documentation
merav-aharoni Aug 31, 2023
d84f939
Documentation
merav-aharoni Aug 31, 2023
c57dec9
Merge branch 'main' into default_channel
merav-aharoni Sep 5, 2023
52d91bf
Merge branch 'main' into default_channel
kt474 Sep 6, 2023
ef5df4a
Merge branch 'main' into default_channel
merav-aharoni Sep 7, 2023
ad9a4f8
Merge branch 'main' into default_channel
merav-aharoni Sep 10, 2023
194947c
Removed channel from condition, because unnecessary
merav-aharoni Sep 10, 2023
4fb6b33
Merge branch 'default_channel' of github.com:merav-aharoni/qiskit-ibm…
merav-aharoni Sep 10, 2023
ea19e4c
changed default_channel to default_account
merav-aharoni Sep 13, 2023
c619f9d
Changed saving and getting default channel to default account
merav-aharoni Sep 14, 2023
e756e9e
black
merav-aharoni Sep 14, 2023
17cd965
Documentation
merav-aharoni Sep 14, 2023
ed2408c
Release notes
merav-aharoni Sep 14, 2023
db00c10
Reverted diff that was unnecessary
merav-aharoni Sep 14, 2023
56d1c72
Merge branch 'main' into default_channel
merav-aharoni Sep 14, 2023
094e977
Merge branch 'main' into default_channel
merav-aharoni Sep 18, 2023
be4776a
Merge branch 'main' into default_channel
merav-aharoni Sep 19, 2023
f20466e
Merge branch 'main' into default_channel
kt474 Sep 19, 2023
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
25 changes: 23 additions & 2 deletions qiskit_ibm_runtime/accounts/management.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ def save(
verify: Optional[bool] = None,
overwrite: Optional[bool] = False,
channel_strategy: Optional[str] = None,
set_default_channel: Optional[bool] = True,
) -> None:
"""Save account on disk."""
default_channel = channel if set_default_channel else None
cls.migrate(filename=filename)
channel = channel or os.getenv("QISKIT_IBM_CHANNEL") or _DEFAULT_CHANNEL_TYPE
name = name or cls._get_default_account_name(channel)
Expand All @@ -72,6 +74,7 @@ def save(
)
# avoid storing invalid accounts
.validate().to_saved_format(),
default_channel=default_channel,
)

@staticmethod
Expand Down Expand Up @@ -105,13 +108,17 @@ def _matching_default(account_name: str) -> bool:
else:
return account_name in default_accounts

account_dict = read_config(filename=filename)
if "default_channel" in account_dict:
del account_dict["default_channel"]

# load all accounts
all_accounts = map(
lambda kv: (
kv[0],
Account.from_saved_format(kv[1]),
),
read_config(filename=filename).items(),
account_dict.items(),
)

# filter based on input parameters
Expand Down Expand Up @@ -148,6 +155,7 @@ def get(
Raises:
AccountNotFoundError: If the input value cannot be found on disk.
"""
# pylint: disable=too-many-return-statements
filename = filename if filename else _DEFAULT_ACCOUNT_CONFIG_JSON_FILE
filename = os.path.expanduser(filename)
cls.migrate(filename)
Expand All @@ -157,7 +165,13 @@ def get(
raise AccountNotFoundError(f"Account with the name {name} does not exist on disk.")
return Account.from_saved_format(saved_account)

channel_ = channel or os.getenv("QISKIT_IBM_CHANNEL") or _DEFAULT_CHANNEL_TYPE
default_channel = None
if os.path.isfile(_DEFAULT_ACCOUNT_CONFIG_JSON_FILE):
qiskit_json_data = read_config(_DEFAULT_ACCOUNT_CONFIG_JSON_FILE)
default_channel = qiskit_json_data.get("default_channel")
channel_ = (
channel or default_channel or os.getenv("QISKIT_IBM_CHANNEL") or _DEFAULT_CHANNEL_TYPE
)
env_account = cls._from_env_variables(channel_)
if env_account is not None:
return env_account
Expand All @@ -174,6 +188,13 @@ def get(
return Account.from_saved_format(saved_account)

all_config = read_config(filename=filename)

# When 'channel' parameter is not defined, check for an account with the default channel
account_name = cls._get_default_account_name(channel=channel_)
if account_name in all_config:
return Account.from_saved_format(all_config[account_name])

# check for any account
for channel_type in _CHANNEL_TYPES:
account_name = cls._get_default_account_name(channel=channel_type)
if account_name in all_config:
Expand Down
14 changes: 13 additions & 1 deletion qiskit_ibm_runtime/accounts/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
logger = logging.getLogger(__name__)


def save_config(filename: str, name: str, config: dict, overwrite: bool) -> None:
def save_config(
filename: str, name: str, config: dict, overwrite: bool, default_channel: Optional[str] = None
) -> None:
"""Save configuration data in a JSON file under the given name."""
logger.debug("Save configuration data for '%s' in '%s'", name, filename)
_ensure_file_exists(filename)
Expand All @@ -34,9 +36,19 @@ def save_config(filename: str, name: str, config: dict, overwrite: bool) -> None
raise AccountAlreadyExistsError(
f"Named account ({name}) already exists. " f"Set overwrite=True to overwrite."
)
if (
data.get("default_channel")
and data.get("default_channel") != default_channel
and not overwrite
):
raise AccountAlreadyExistsError(
f"default_channel ({name}) already exists. " f"Set overwrite=True to overwrite."
)

with open(filename, mode="w", encoding="utf-8") as json_out:
data[name] = config
if default_channel:
data["default_channel"] = default_channel
json.dump(data, json_out, sort_keys=True, indent=4)


Expand Down
8 changes: 7 additions & 1 deletion qiskit_ibm_runtime/qiskit_runtime_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def __init__(
- Account with the input `name`, if specified.
- Default account for the `channel` type, if `channel` is specified but `token` is not.
- Account defined by the input `channel` and `token`, if specified.
- Account defined by the `default_channel` if defined in filename
- Account defined by the environment variables, if defined.
- Default account for the ``ibm_cloud`` account, if one is available.
- Default account for the ``ibm_quantum`` account, if one is available.
Expand Down Expand Up @@ -271,7 +272,7 @@ def _discover_account(
if channel and channel not in ["ibm_cloud", "ibm_quantum"]:
raise ValueError("'channel' can only be 'ibm_cloud' or 'ibm_quantum'")
channel = channel or self._get_channel_for_auth(auth=auth)
if token:
if channel and token:
Comment thread
kt474 marked this conversation as resolved.
Outdated
account = Account(
channel=channel,
token=token,
Expand All @@ -291,6 +292,7 @@ def _discover_account(
"'channel' or 'auth' is required if 'token', or 'url' is specified but 'name' is not."
)

# channel is not defined yet, get it from the AccountManager
if account is None:
account = AccountManager.get(filename=filename)

Expand Down Expand Up @@ -700,6 +702,7 @@ def save_account(
verify: Optional[bool] = None,
overwrite: Optional[bool] = False,
channel_strategy: Optional[str] = None,
set_default_channel: Optional[bool] = True,
Comment thread
kt474 marked this conversation as resolved.
Outdated
) -> None:
"""Save the account to disk for future use.

Expand All @@ -720,6 +723,8 @@ def save_account(
verify: Verify the server's TLS certificate.
overwrite: ``True`` if the existing account is to be overwritten.
channel_strategy: Error mitigation strategy.
set_default_channel: If ``True``, the channel parameter is saved in filename,
as the default channel to use.
"""

AccountManager.save(
Expand All @@ -733,6 +738,7 @@ def save_account(
verify=verify,
overwrite=overwrite,
channel_strategy=channel_strategy,
set_default_channel=set_default_channel,
)

@staticmethod
Expand Down
3 changes: 3 additions & 0 deletions test/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def get_account_config_contents(
instance=None,
verify=None,
proxies=None,
set_default=None,
):
"""Generate qiskitrc content"""
if instance is None:
Expand All @@ -177,4 +178,6 @@ def get_account_config_contents(
out[name]["verify"] = verify
if proxies is not None:
out[name]["proxies"] = proxies
if set_default:
out["default_channel"] = channel
return out
89 changes: 85 additions & 4 deletions test/unit/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ def test_delete_auth(self):
def test_delete_filename(self):
"""Test delete accounts with filename parameter."""

filename = "~/account_to_delete.json"
filename = _TEST_FILENAME
name = "key1"
channel = "ibm_quantum"
AccountManager.save(channel=channel, filename=filename, name=name, token="temp_token")
Expand All @@ -606,6 +606,84 @@ def test_account_with_filename(self):
)
self.assertEqual(account.token, dummy_token)

@temporary_account_config_file()
def test_default_env_channel(self):
"""Test that if QISKIT_IBM_CHANNEL is set in the environment, this channel will be used"""
token = uuid.uuid4().hex
# unset default_channel in the environment
with temporary_account_config_file(token=token), no_envs("QISKIT_IBM_CHANNEL"):
service = FakeRuntimeService()
self.assertEqual(service.channel, "ibm_cloud")

# set channel to default channel in the environment
subtests = ["ibm_quantum", "ibm_cloud"]
for channel in subtests:
channel_env = {"QISKIT_IBM_CHANNEL": channel}
with temporary_account_config_file(channel=channel, token=token), custom_envs(
channel_env
):
service = FakeRuntimeService()
self.assertEqual(service.channel, channel)

@temporary_account_config_file()
def test_save_default_channel(self):
"""Test that if a default_channel is defined in the qiskit-ibm.json file,
this channel will be used"""
token = uuid.uuid4().hex
subtests = ["ibm_quantum", "ibm_cloud"]
for channel in subtests:
account_name = "default-" + channel.replace("_", "-")
account_fields = {"channel": channel, "token": token}
if channel == "ibm_cloud":
account_fields["instance"] = "some_instance"
with temporary_account_config_file(
contents={account_name: account_fields, "default_channel": channel}
), no_envs("QISKIT_IBM_CHANNEL"):
service = FakeRuntimeService()
self.assertEqual(service.channel, channel)

@temporary_account_config_file()
def test_set_channel_precedence(self):
"""Test the precedence of the various methods to set the channel:
parameter > json file > env variable > default channel"""
token = uuid.uuid4().hex
channel_env = {"QISKIT_IBM_CHANNEL": "ibm_cloud"}
contents = {
"default-ibm-cloud": {
"channel": "ibm_cloud",
"token": token,
"instance": "some_instance",
},
"default-ibm-quantum": {
"channel": "ibm_quantum",
"token": token,
},
}
contents["default_channel"] = "ibm_cloud"

# parameter channel is selected
with temporary_account_config_file(contents=contents), custom_envs(channel_env):
service = FakeRuntimeService(channel="ibm_quantum")
self.assertEqual(service.channel, "ibm_quantum")

# default channel from json file
contents["default_channel"] = "ibm_quantum"
with temporary_account_config_file(contents=contents), custom_envs(channel_env):
service = FakeRuntimeService()
self.assertEqual(service.channel, "ibm_quantum")

# channel from environment variable
del contents["default_channel"]
channel_env = {"QISKIT_IBM_CHANNEL": "ibm_quantum"}
with temporary_account_config_file(contents=contents), custom_envs(channel_env):
service = FakeRuntimeService()
self.assertEqual(service.channel, "ibm_quantum")

# default channel
with temporary_account_config_file(contents=contents), no_envs("QISKIT_IBM_CHANNEL"):
service = FakeRuntimeService()
self.assertEqual(service.channel, "ibm_cloud")

def tearDown(self) -> None:
"""Test level tear down."""
super().tearDown()
Expand Down Expand Up @@ -735,7 +813,10 @@ def test_enable_account_both_channel(self):
token = uuid.uuid4().hex
contents = get_account_config_contents(channel="ibm_cloud", token=token)
contents.update(get_account_config_contents(channel="ibm_quantum", token=uuid.uuid4().hex))
with temporary_account_config_file(contents=contents), no_envs(["QISKIT_IBM_TOKEN"]):

with temporary_account_config_file(contents=contents), no_envs(
["QISKIT_IBM_TOKEN", "QISKIT_IBM_CHANNEL"]
):
service = FakeRuntimeService()
self.assertTrue(service._account)
self.assertEqual(service._account.token, token)
Expand All @@ -754,7 +835,7 @@ def test_enable_account_by_env_channel(self):
"QISKIT_IBM_URL": url,
"QISKIT_IBM_INSTANCE": "h/g/p" if channel == "ibm_quantum" else "crn:12",
}
with custom_envs(envs):
with custom_envs(envs), no_envs("QISKIT_IBM_CHANNEL"):
service = FakeRuntimeService(channel=channel)

self.assertTrue(service._account)
Expand Down Expand Up @@ -871,7 +952,7 @@ def test_enable_account_by_env_pref(self):
"QISKIT_IBM_URL": url,
"QISKIT_IBM_INSTANCE": "my_crn",
}
with custom_envs(envs):
with custom_envs(envs), no_envs("QISKIT_IBM_CHANNEL"):
service = FakeRuntimeService(**extra)

self.assertTrue(service._account)
Expand Down