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

Allow user to define a default account as an environment variable #1018

Merged
merged 41 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
67 changes: 57 additions & 10 deletions qiskit_ibm_runtime/accounts/management.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def save(
verify: Optional[bool] = None,
overwrite: Optional[bool] = False,
channel_strategy: Optional[str] = None,
set_as_default: Optional[bool] = None,
) -> None:
"""Save account on disk."""
channel = channel or os.getenv("QISKIT_IBM_CHANNEL") or _DEFAULT_CHANNEL_TYPE
Expand All @@ -69,6 +70,7 @@ def save(
)
# avoid storing invalid accounts
.validate().to_saved_format(),
set_as_default=set_as_default,
)

@staticmethod
Expand Down Expand Up @@ -137,6 +139,21 @@ def get(
filename: Full path of the file from which to get the account.
name: Account name.
channel: Channel type.
Order of precedence for selecting the account:
1. If name is specified, get account with that name
2. If the environment variables define an account, get that one
3. If the channel parameter is defined,
a. get the account of this channel type defined as "is_default_account"
b. get the account of this channel type with default name
c. get any account of this channel type
4. If the channel is defined in "QISKIT_IBM_CHANNEL"
a. get the account of this channel type defined as "is_default_account"
b. get the account of this channel type with default name
c. get any account of this channel type
5. If a default account is defined in the json file, get that account
6. Get any account that is defined in the json file with
preference for _DEFAULT_CHANNEL_TYPE.


Returns:
Account information.
Expand All @@ -157,18 +174,20 @@ def get(
if env_account is not None:
return env_account

if channel:
saved_account = read_config(
filename=filename,
name=cls._get_default_account_name(channel=channel),
)
if saved_account is None:
if os.path.isfile(_QISKITRC_CONFIG_FILE):
return cls._from_qiskitrc_file()
raise AccountNotFoundError(f"No default {channel} account saved.")
all_config = read_config(filename=filename)
# Get the default account for the given channel.
# If channel == None, get the default account, for any channel, if it exists
saved_account = cls._get_default_account(all_config, channel)

if saved_account is not None:
return Account.from_saved_format(saved_account)

all_config = read_config(filename=filename)
# Get the default account from the channel defined in the environment variable
account = cls._get_default_account(all_config, channel=channel_)
if account is not None:
return Account.from_saved_format(account)

# 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 Expand Up @@ -209,6 +228,34 @@ def _from_env_variables(cls, channel: Optional[ChannelType]) -> Optional[Account
channel=channel,
)

@classmethod
def _get_default_account(
cls, all_config: dict, channel: Optional[str] = None
) -> Optional[dict]:
default_channel_account = None
any_channel_account = None

for account_name in all_config:
account = all_config[account_name]
if channel:
if account.get("channel") == channel and account.get("is_default_account"):
return account
if account.get(
"channel"
) == channel and account_name == cls._get_default_account_name(channel):
default_channel_account = account
if account.get("channel") == channel:
any_channel_account = account
else:
if account.get("is_default_account"):
return account

if default_channel_account:
return default_channel_account
elif any_channel_account:
return any_channel_account
return None

@classmethod
def _get_default_account_name(cls, channel: ChannelType) -> str:
return (
Expand Down
22 changes: 20 additions & 2 deletions 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, set_as_default: Optional[bool] = 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 @@ -35,8 +37,24 @@ def save_config(filename: str, name: str, config: dict, overwrite: bool) -> None
f"Named account ({name}) already exists. " f"Set overwrite=True to overwrite."
)

data[name] = config

# if set_as_default, but another account is defined as default, user must specify overwrite to change
# the default account.
if set_as_default:
data[name]["is_default_account"] = True
for account_name in data:
account = data[account_name]
if account_name != name and account.get("is_default_account"):
if overwrite:
del account["is_default_account"]
else:
raise AccountAlreadyExistsError(
f"default_account ({name}) already exists. "
f"Set overwrite=True to overwrite."
)

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


Expand Down
6 changes: 6 additions & 0 deletions 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 @@ -287,6 +288,7 @@ def _discover_account(
"'channel' 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 @@ -689,6 +691,7 @@ def save_account(
verify: Optional[bool] = None,
overwrite: Optional[bool] = False,
channel_strategy: Optional[str] = None,
set_as_default: Optional[bool] = None,
) -> None:
"""Save the account to disk for future use.

Expand All @@ -709,6 +712,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_as_default: If ``True``, the account is saved in filename,
as the default account.
"""

AccountManager.save(
Expand All @@ -722,6 +727,7 @@ def save_account(
verify=verify,
overwrite=overwrite,
channel_strategy=channel_strategy,
set_as_default=set_as_default,
)

@staticmethod
Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/default_account-13d86d50f5b1d972.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
features:
- |
Added the option to define a default account in the account json file.
The select an account as default, define ``set_as_default=True`` in
``QiskitRuntimeService.save_account()``.
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[name]["is_default_account"] = True
return out
Loading
Loading