From 558749c7a10dde638fc33a0ef81ccc9d9d8aad7a Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Sun, 20 Mar 2022 19:24:45 -0400 Subject: [PATCH 01/28] Add channel parameter --- .github/workflows/ci.yml | 2 +- .github/workflows/e2e-tests.yml | 2 +- .github/workflows/integration-tests.yml | 2 +- CONTRIBUTING.md | 2 +- README.md | 10 +- docs/max_time.rst | 8 +- ...> 01_introduction_ibm_cloud_runtime.ipynb} | 6 +- ...02_introduction_ibm_quantum_runtime.ipynb} | 16 +- docs/tutorials/03_backends.ipynb | 7 +- docs/tutorials/04_account_management.ipynb | 14 +- docs/tutorials/qka.ipynb | 4 +- .../qiskit_runtime_expval_program.ipynb | 2 +- docs/tutorials/vqe.ipynb | 2 +- qiskit_ibm_runtime/__init__.py | 2 +- qiskit_ibm_runtime/accounts/__init__.py | 2 +- qiskit_ibm_runtime/accounts/account.py | 56 ++-- qiskit_ibm_runtime/accounts/management.py | 59 ++-- qiskit_ibm_runtime/api/auth.py | 6 +- qiskit_ibm_runtime/api/client_parameters.py | 15 +- qiskit_ibm_runtime/api/clients/auth.py | 6 +- qiskit_ibm_runtime/channel.py | 22 ++ qiskit_ibm_runtime/constants.py | 1 - qiskit_ibm_runtime/ibm_backend.py | 2 +- qiskit_ibm_runtime/ibm_estimator.py | 2 +- qiskit_ibm_runtime/ibm_runtime_service.py | 179 +++++++---- qiskit_ibm_runtime/ibm_sampler.py | 2 +- qiskit_ibm_runtime/runtime_options.py | 11 +- qiskit_ibm_runtime/utils/utils.py | 7 +- test/account.py | 19 +- test/decorators.py | 49 +-- test/e2e/test_tutorials.py | 21 +- test/ibm_test_case.py | 26 +- test/integration/test_account.py | 15 +- test/integration/test_auth_client.py | 17 +- test/integration/test_backend.py | 5 +- .../integration/test_backend_serialization.py | 19 +- test/integration/test_basic_server_paths.py | 3 +- test/integration/test_job.py | 9 +- test/integration/test_proxies.py | 43 +-- test/integration/test_retrieve_job.py | 13 +- ..._client.py => fake_quantum_auth_client.py} | 2 +- test/unit/mock/fake_runtime_client.py | 15 +- test/unit/mock/fake_runtime_service.py | 12 +- test/unit/test_account.py | 281 ++++++++++-------- test/unit/test_account_client.py | 6 +- test/unit/test_backend_retrieval.py | 53 ++-- test/unit/test_client_parameters.py | 23 +- test/unit/test_data_serialization.py | 9 +- test/unit/test_job_retrieval.py | 41 +-- test/unit/test_jobs.py | 61 ++-- test/unit/test_programs.py | 32 +- test/unit/test_runtime_ws.py | 3 +- test/utils.py | 3 +- 53 files changed, 720 insertions(+), 509 deletions(-) rename docs/tutorials/{01_introduction_cloud_runtime.ipynb => 01_introduction_ibm_cloud_runtime.ipynb} (99%) rename docs/tutorials/{02_introduction_legacy_runtime.ipynb => 02_introduction_ibm_quantum_runtime.ipynb} (98%) create mode 100644 qiskit_ibm_runtime/channel.py rename test/unit/mock/{fake_legacy_auth_client.py => fake_quantum_auth_client.py} (98%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c7b3c2b1..3ece17415 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,7 +111,7 @@ jobs: matrix: python-version: [ 3.9 ] os: [ "ubuntu-latest" ] - environment: [ "legacy-production", "cloud-production" ] + environment: [ "ibm-quantum-production", "ibm-cloud-production" ] environment: ${{ matrix.environment }} env: QISKIT_IBM_TOKEN: ${{ secrets.QISKIT_IBM_TOKEN }} diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 60ff0a9a0..267e0895d 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -26,7 +26,7 @@ jobs: matrix: python-version: [ 3.9 ] os: [ "ubuntu-latest" ] - environment: [ "legacy-production", "legacy-staging", "cloud-production", "cloud-staging" ] + environment: [ "ibm-quantum-production", "ibm-quantum-staging", "ibm-cloud-production", "ibm-cloud-staging" ] environment: ${{ matrix.environment }} env: QISKIT_IBM_TOKEN: ${{ secrets.QISKIT_IBM_TOKEN }} diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 24a41d7d4..9e49c97cf 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -26,7 +26,7 @@ jobs: matrix: python-version: [ 3.9 ] os: [ "ubuntu-latest" ] - environment: [ "legacy-production", "legacy-staging", "cloud-production", "cloud-staging" ] + environment: [ "ibm-quantum-production", "ibm-quantum-staging", "ibm-cloud-production", "ibm-cloud-staging" ] environment: ${{ matrix.environment }} env: QISKIT_IBM_TOKEN: ${{ secrets.QISKIT_IBM_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 645c39850..c9616d514 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -227,7 +227,7 @@ $ make e2e-test #### Configuration -Integration and E2E tests require an environment configuration and can either be run against IBM Quantum APIs ("Legacy") or IBM Cloud Quantum Service APIs. +Integration and E2E tests require an environment configuration and can either be run against IBM Quantum APIs ("ibm_quantum") or IBM Cloud ("ibm_cloud") Quantum Service APIs. Sample configuration for IBM Quantum diff --git a/README.md b/README.md index 5f682d506..dff68c7d1 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ IBM Cloud API key and Cloud Resource Name (CRN), which you will need for authent ### Qiskit Runtime on IBM Quantum Prior to becoming an IBM Cloud service, Qiskit Runtime was offered on IBM Quantum. If you have an -existing IBM Quantum account, you can continue using Qiskit Runtime on IBM Quantum, which is referred to as legacy runtime. +existing IBM Quantum account, you can continue using Qiskit Runtime on IBM Quantum. You will need your IBM Quantum API token to authenticate with the Qiskit Runtime service: @@ -65,10 +65,10 @@ them each time. The credentials are saved in the `$HOME/.qiskit/qiskit-ibm.json` from qiskit_ibm_runtime import IBMRuntimeService # Save an IBM Cloud account. -IBMRuntimeService.save_account(auth="cloud", token="MY_IBM_CLOUD_API_KEY", instance="MY_IBM_CLOUD_CRN") +IBMRuntimeService.save_account(channel="ibm_cloud", token="MY_IBM_CLOUD_API_KEY", instance="MY_IBM_CLOUD_CRN") # Save an IBM Quantum account. -IBMRuntimeService.save_account(auth="legacy", token="MY_IBM_QUANTUM_TOKEN") +IBMRuntimeService.save_account(channel="ibm_quantum", token="MY_IBM_QUANTUM_TOKEN") ``` Once the account is saved on disk, you can instantiate the service without any arguments: @@ -101,10 +101,10 @@ service with your credentials. from qiskit_ibm_runtime import IBMRuntimeService # For an IBM Cloud account. -cloud_service = IBMRuntimeService(auth="cloud", token="MY_IBM_CLOUD_API_KEY", instance="MY_IBM_CLOUD_CRN") +ibm_cloud_service = IBMRuntimeService(channel="ibm_cloud", token="MY_IBM_CLOUD_API_KEY", instance="MY_IBM_CLOUD_CRN") # For an IBM Quantum account. -legacy_service = IBMRuntimeService(auth="legacy", token="MY_IBM_QUANTUM_TOKEN") +ibm_quantum_service = IBMRuntimeService(channel="ibm_quantum", token="MY_IBM_QUANTUM_TOKEN") ``` ## Accessing Qiskit Runtime Programs diff --git a/docs/max_time.rst b/docs/max_time.rst index 479b2390d..8afbc9dd7 100644 --- a/docs/max_time.rst +++ b/docs/max_time.rst @@ -10,14 +10,14 @@ status as `Cancelled - Ran too long`. The maximum execution time is the smaller of 1) the system limit and 2) the ``max_execution_time`` defined by the program. The system limit is defined below: -Qiskit Runtime on IBM Cloud ---------------------------- +Qiskit Runtime on IBM Cloud (ibm_cloud) +--------------------------------------- The system limit on the job execution time is 3 hours for a job running on a simulator and 8 hours for a job running on a physical system. -Qiskit Runtime on IBM Quantum (legacy) --------------------------------------- +Qiskit Runtime on IBM Quantum (ibm_quantum) +------------------------------------------- The system limit on the job execution time is diff --git a/docs/tutorials/01_introduction_cloud_runtime.ipynb b/docs/tutorials/01_introduction_ibm_cloud_runtime.ipynb similarity index 99% rename from docs/tutorials/01_introduction_cloud_runtime.ipynb rename to docs/tutorials/01_introduction_ibm_cloud_runtime.ipynb index f25142f58..3f6aada99 100644 --- a/docs/tutorials/01_introduction_cloud_runtime.ipynb +++ b/docs/tutorials/01_introduction_ibm_cloud_runtime.ipynb @@ -107,7 +107,7 @@ "from qiskit_ibm_runtime import IBMRuntimeService\n", "\n", "# Save account on disk.\n", - "# IBMRuntimeService.save_account(auth=\"cloud\", token=, instance= or )\n", + "# IBMRuntimeService.save_account(channel=\"ibm_cloud\", token=, instance= or )\n", "\n", "service = IBMRuntimeService()" ] @@ -689,11 +689,11 @@ "source": [ "There are additional tutorials in this directory:\n", "\n", - "- [02_introduction_legacy_runtime.ipynb](02_introduction_legacy_runtime.ipynb) is the corresponding tutorial on using legacy Qiskit Runtime. You can skip this tutorial if you don't plan on using legacy runtime.\n", + "- [02_introduction_ibm_quantum_runtime.ipynb](02_introduction_ibm_quantum_runtime.ipynb) is the corresponding tutorial on using Qiskit Runtime on IBM Quantum. You can skip this tutorial if you don't plan on using Qiskit Runtime on IBM Quantum.\n", "- [03_backends.ipynb](03_backends.ipynb) describes how to find a target backend for the Qiskit Runtime program you want to invoke. \n", "- [04_account_management.ipynb](04_account_management.ipynb) describes how to save, load, and delete your account credentials on disk.\n", "- [qiskit_runtime_vqe_program.ipynb](sample_vqe_program/qiskit_runtime_vqe_program.ipynb) goes into more details on uploading a real-world program (VQE). \n", - "- [qka.ipynb](qka.ipynb), [vqe.ipynb](vqe.ipynb), and [qiskit_runtime_expval_program.ipynb](sample_expval_program/qiskit_runtime_expval_program.ipynb) describe how to use the public programs `qka`, `vqe`, and `sample-expval`, respectively. These programs are currently only available on legacy Qiskit Runtime." + "- [qka.ipynb](qka.ipynb), [vqe.ipynb](vqe.ipynb), and [qiskit_runtime_expval_program.ipynb](sample_expval_program/qiskit_runtime_expval_program.ipynb) describe how to use the public programs `qka`, `vqe`, and `sample-expval`, respectively. These programs are currently only available in Qiskit Runtime on IBM Quantum." ] }, { diff --git a/docs/tutorials/02_introduction_legacy_runtime.ipynb b/docs/tutorials/02_introduction_ibm_quantum_runtime.ipynb similarity index 98% rename from docs/tutorials/02_introduction_legacy_runtime.ipynb rename to docs/tutorials/02_introduction_ibm_quantum_runtime.ipynb index a8c4f51f4..a72d921b6 100644 --- a/docs/tutorials/02_introduction_legacy_runtime.ipynb +++ b/docs/tutorials/02_introduction_ibm_quantum_runtime.ipynb @@ -18,7 +18,7 @@ "id": "1de4634b", "metadata": {}, "source": [ - "# Legacy Qiskit Runtime" + "# Qiskit Runtime on IBM Quantum" ] }, { @@ -26,7 +26,7 @@ "id": "5affe56b", "metadata": {}, "source": [ - "Prior to becoming an IBM Cloud service, Qiskit Runtime was offered on IBM Quantum. If you have an existing IBM Quantum account, you can continue using Qiskit Runtime on IBM Quantum, which is referred to as _legacy runtime_. \n" + "Prior to becoming an IBM Cloud service, Qiskit Runtime was offered on IBM Quantum. If you have an existing IBM Quantum account, you can continue using Qiskit Runtime on IBM Quantum. \n" ] }, { @@ -80,7 +80,7 @@ "source": [ "Before you can start using Qiskit Runtime, you need to initialize your account. You can do this by calling `IBMRuntimeService` with your IBM Quantum API token, which can be found on the [IBM Quantum accounts page](https://quantum-computing.ibm.com/account).\n", "\n", - "You can also choose to save your credentials on disk (in the `$HOME/.qiskit/qiskit-ibm.json` file). By doing so, you only need to use `IBMRuntimeService(auth=\"legacy\")` in the future to initialize your account.\n", + "You can also choose to save your credentials on disk (in the `$HOME/.qiskit/qiskit-ibm.json` file). By doing so, you only need to use `IBMRuntimeService(channel=\"ibm_quantum\")` in the future to initialize your account.\n", "\n", "For more information about account management, such as how to delete or view an account, see [04_account_management.ipynb](04_account_management.ipynb)." ] @@ -95,10 +95,10 @@ "from qiskit_ibm_runtime import IBMRuntimeService\n", "\n", "# Save account on disk.\n", - "# IBMRuntimeService.save_account(auth=\"legacy\", token=)\n", + "# IBMRuntimeService.save_account(channel=\"ibm_quantum\", token=)\n", "\n", - "# The \"auth\" keyword is not needed if you have only 1 type (legacy or cloud) of account saved.\n", - "service = IBMRuntimeService(auth=\"legacy\")" + "# The \"channel\" keyword is not needed if you have only 1 type (ibm_quantum or ibm_cloud) of account saved.\n", + "service = IBMRuntimeService(channel=\"ibm_quantum\")" ] }, { @@ -417,7 +417,7 @@ "metadata": {}, "source": [ "
\n", - "Note: To ensure fairness, there is a maximum execution time for each Qiskit Runtime job. Refer to this documentation on what the time limit is.\n", + "Note: To ensure fairness, there is a maximum execution time for each Qiskit Runtime job. Refer to this documentation on what the time limit is.\n", "
" ] }, @@ -535,7 +535,7 @@ "- [04_account_management.ipynb](04_account_management.ipynb) describes how to save, load, and delete your account credentials on disk.\n", "- [05_uploading_program.ipynb](05_uploading_program.ipynb) is an introduction on uploading your custom Qiskit Runtime program.\n", "- [qiskit_runtime_vqe_program.ipynb](sample_vqe_program/qiskit_runtime_vqe_program.ipynb) goes into more details on uploading a real-world program (VQE). \n", - "- [qka.ipynb](qka.ipynb), [vqe.ipynb](vqe.ipynb), and [qiskit_runtime_expval_program.ipynb](sample_expval_program/qiskit_runtime_expval_program.ipynb) describe how to use the public programs `qka`, `vqe`, and `sample-expval`, respectively. These programs are currently only available on legacy Qiskit Runtime." + "- [qka.ipynb](qka.ipynb), [vqe.ipynb](vqe.ipynb), and [qiskit_runtime_expval_program.ipynb](sample_expval_program/qiskit_runtime_expval_program.ipynb) describe how to use the public programs `qka`, `vqe`, and `sample-expval`, respectively. These programs are currently only available on Qiskit Runtime on IBM Quantum." ] }, { diff --git a/docs/tutorials/03_backends.ipynb b/docs/tutorials/03_backends.ipynb index 88853c3e1..8fd5b9e97 100644 --- a/docs/tutorials/03_backends.ipynb +++ b/docs/tutorials/03_backends.ipynb @@ -227,9 +227,8 @@ } ], "source": [ - "# This program currently is only available on legacy Qiskit Runtime.\n", - "legacy_service = IBMRuntimeService(auth=\"legacy\")\n", - "program = legacy_service.program(\"hello-world\")\n", + "ibm_quantum_service = IBMRuntimeService(channel=\"ibm_quantum\")\n", + "program = ibm_quantum_service.program(\"hello-world\")\n", "print(program.backend_requirements)" ] }, @@ -269,7 +268,7 @@ } ], "source": [ - "legacy_service.backends(min_num_qubits=5)" + "ibm_quantum_service.backends(min_num_qubits=5)" ] }, { diff --git a/docs/tutorials/04_account_management.ipynb b/docs/tutorials/04_account_management.ipynb index 1fa2c6978..0641dc2d4 100644 --- a/docs/tutorials/04_account_management.ipynb +++ b/docs/tutorials/04_account_management.ipynb @@ -26,7 +26,7 @@ "id": "c22a29f4", "metadata": {}, "source": [ - "Qiskit Runtime is available on both IBM Cloud and IBM Quantum. The former requires an IBM Cloud account and the latter an IBM Quantum account. If you don't have these accounts, please refer to [01_introduction_cloud_runtime.ipynb](01_introduction_cloud_runtime.ipynb) or [02_introduction_legacy_runtime.ipynb](02_introduction_legacy_runtime.ipynb) on how to set one up.\n", + "Qiskit Runtime is available on both IBM Cloud and IBM Quantum. The former requires an IBM Cloud account and the latter an IBM Quantum account. If you don't have these accounts, please refer to [01_introduction_ibm_cloud_runtime.ipynb](01_introduction_ibm_cloud_runtime.ipynb) or [02_introduction_ibm_quantum_runtime.ipynb](02_introduction_ibm_quantum_runtime.ipynb) on how to set one up.\n", "\n" ] }, @@ -74,7 +74,7 @@ "id": "bfb828ec", "metadata": {}, "source": [ - "Below are examples of saving an IBM Cloud and an IBM Quantum accounts. The `auth` parameter indicates the authentication type of the account. If you are saving multiple account, consider using the `name` parameter to differentiate them.\n" + "Below are examples of saving an IBM Cloud and an IBM Quantum accounts. The `channel` parameter indicates the authentication type of the account. If you are saving multiple account, consider using the `name` parameter to differentiate them.\n" ] }, { @@ -87,10 +87,10 @@ "from qiskit_ibm_runtime import IBMRuntimeService\n", "\n", "# Save an IBM Cloud account on disk.\n", - "# IBMRuntimeService.save_account(auth=\"cloud\", token=, instance= or )\n", + "# IBMRuntimeService.save_account(channel=\"ibm_cloud\", token=, instance= or )\n", "\n", "# Save an IBM Quantum account on disk.\n", - "# IBMRuntimeService.save_account(auth=\"legacy\", token=)" + "# IBMRuntimeService.save_account(channel=\"ibm_quantum\", token=)" ] }, { @@ -125,7 +125,7 @@ "id": "9a4504f8", "metadata": {}, "source": [ - "If you have both an IBM Cloud and an IBM Quantum accounts saved, `IBMRuntimeService()` by default will load the IBM Cloud account. To load the IBM Quantum account, you can specify `IBMRuntimeService(auth=\"legacy\")` instead.\n", + "If you have both an IBM Cloud and an IBM Quantum accounts saved, `IBMRuntimeService()` by default will load the IBM Cloud account. To load the IBM Quantum account, you can specify `IBMRuntimeService(channel=\"ibm_quantum\")` instead.\n", "\n", "Alternatively, if you specified a `name` for your account when saving it, you can also specify the name of the account to load." ] @@ -138,7 +138,7 @@ "outputs": [], "source": [ "# Save an IBM Cloud account on disk and give it a name.\n", - "# IBMRuntimeService.save_account(auth=\"cloud\", token=, instance=, name=\"prod\")\n", + "# IBMRuntimeService.save_account(channel=\"ibm_cloud\", token=, instance=, name=\"prod\")\n", "\n", "# service = IBMRuntimeService(name=\"prod\")" ] @@ -159,7 +159,7 @@ "outputs": [], "source": [ "# Initialize an IBM Cloud account without saving it.\n", - "# service = IBMRuntimeService(auth=\"cloud\", token=, instance=)" + "# service = IBMRuntimeService(channel=\"ibm_cloud\", token=, instance=)" ] }, { diff --git a/docs/tutorials/qka.ipynb b/docs/tutorials/qka.ipynb index bc38daa1f..0d55f5180 100644 --- a/docs/tutorials/qka.ipynb +++ b/docs/tutorials/qka.ipynb @@ -85,7 +85,7 @@ "source": [ "# Load your account and get the quantum backend\n", "\n", - "We'll be using the 27-qubit device `ibmq_montreal` for this tutorial on legacy Qiskit Runtime." + "We'll be using the 27-qubit device `ibmq_montreal` for this tutorial in Qiskit Runtime on IBM Quantum." ] }, { @@ -104,7 +104,7 @@ "\n", "from qiskit_ibm_runtime import IBMRuntimeService\n", "\n", - "service = IBMRuntimeService(auth=\"legacy\")\n", + "service = IBMRuntimeService(channel=\"ibm_quantum\")\n", "backend = service.backend(\"ibmq_montreal\")" ] }, diff --git a/docs/tutorials/sample_expval_program/qiskit_runtime_expval_program.ipynb b/docs/tutorials/sample_expval_program/qiskit_runtime_expval_program.ipynb index e7bad3533..2a3cec84b 100644 --- a/docs/tutorials/sample_expval_program/qiskit_runtime_expval_program.ipynb +++ b/docs/tutorials/sample_expval_program/qiskit_runtime_expval_program.ipynb @@ -363,7 +363,7 @@ "source": [ "from qiskit_ibm_runtime import IBMRuntimeService\n", "\n", - "service = IBMRuntimeService(auth=\"legacy\")" + "service = IBMRuntimeService(channel=\"ibm_quantum\")" ] }, { diff --git a/docs/tutorials/vqe.ipynb b/docs/tutorials/vqe.ipynb index 07145a723..94071df41 100644 --- a/docs/tutorials/vqe.ipynb +++ b/docs/tutorials/vqe.ipynb @@ -275,7 +275,7 @@ "source": [ "from qiskit_ibm_runtime import IBMRuntimeService\n", "\n", - "service = IBMRuntimeService(auth=\"legacy\")" + "service = IBMRuntimeService(channel=\"ibm_quantum\")" ] }, { diff --git a/qiskit_ibm_runtime/__init__.py b/qiskit_ibm_runtime/__init__.py index 9533bdba7..00417af1d 100644 --- a/qiskit_ibm_runtime/__init__.py +++ b/qiskit_ibm_runtime/__init__.py @@ -39,7 +39,7 @@ on disk. Qiskit Runtime is available on both IBM Cloud and IBM Quantum, and you can specify -``auth="cloud"`` for IBM Cloud and ``auth="legacy"`` for IBM Quantum. The default +``channel="ibm_cloud"`` for IBM Cloud and ``channel="ibm_quantum"`` for IBM Quantum. The default is IBM Cloud. Listing runtime programs diff --git a/qiskit_ibm_runtime/accounts/__init__.py b/qiskit_ibm_runtime/accounts/__init__.py index 75a684636..4bc59cc4f 100644 --- a/qiskit_ibm_runtime/accounts/__init__.py +++ b/qiskit_ibm_runtime/accounts/__init__.py @@ -14,7 +14,7 @@ Account management functionality related to the IBM Runtime Services. """ -from .account import Account, AccountType +from .account import Account, AccountType, ChannelType from .management import AccountManager from .exceptions import ( AccountNotFoundError, diff --git a/qiskit_ibm_runtime/accounts/account.py b/qiskit_ibm_runtime/accounts/account.py index 4bbc7be23..6f4aee2b1 100644 --- a/qiskit_ibm_runtime/accounts/account.py +++ b/qiskit_ibm_runtime/accounts/account.py @@ -20,15 +20,17 @@ from typing_extensions import Literal from .exceptions import InvalidAccountError, CloudResourceNameResolutionError -from ..api.auth import LegacyAuth, CloudAuth +from ..api.auth import QuantumAuth, CloudAuth +from ..channel import Channel from ..proxies import ProxyConfiguration from ..utils.hgp import from_instance_format from ..utils import resolve_crn AccountType = Optional[Literal["cloud", "legacy"]] +ChannelType = Optional[Literal[Channel.IBM_CLOUD, Channel.IBM_QUANTUM]] -LEGACY_API_URL = "https://auth.quantum-computing.ibm.com/api" -CLOUD_API_URL = "https://cloud.ibm.com" +IBM_QUANTUM_API_URL = "https://auth.quantum-computing.ibm.com/api" +IBM_CLOUD_API_URL = "https://cloud.ibm.com" logger = logging.getLogger(__name__) @@ -37,7 +39,7 @@ class Account: def __init__( self, - auth: AccountType, + channel: ChannelType, token: str, url: Optional[str] = None, instance: Optional[str] = None, @@ -47,16 +49,18 @@ def __init__( """Account constructor. Args: - auth: Authentication type, ``cloud`` or ``legacy``. + channel: Channel type, ``ibm_cloud`` or ``ibm_quantum``. token: Account token to use. url: Authentication URL. instance: Service instance to use. proxies: Proxy configuration. verify: Whether to verify server's TLS certificate. """ - resolved_url = url or (LEGACY_API_URL if auth == "legacy" else CLOUD_API_URL) + resolved_url = url or ( + IBM_QUANTUM_API_URL if channel == Channel.IBM_QUANTUM else IBM_CLOUD_API_URL + ) - self.auth = auth + self.channel = channel self.token = token self.url = resolved_url self.instance = instance @@ -74,8 +78,10 @@ def to_saved_format(self) -> dict: def from_saved_format(cls, data: dict) -> "Account": """Creates an account instance from data saved on disk.""" proxies = data.get("proxies") + auth = data.get("auth") + channel = Channel.IBM_QUANTUM if auth == "legacy" else Channel.IBM_CLOUD return cls( - auth=data.get("auth"), + channel=data.get("channel", channel), url=data.get("url"), token=data.get("token"), instance=data.get("instance"), @@ -87,15 +93,18 @@ def resolve_crn(self) -> None: """Resolves the corresponding unique Cloud Resource Name (CRN) for the given non-unique service instance name and updates the ``instance`` attribute accordingly. - No-op if ``auth`` attribute is set to legacy. + No-op if ``channel`` attribute is set to ``ibm_quantum``. No-op if ``instance`` attribute is set to a Cloud Resource Name (CRN). Raises: CloudResourceNameResolutionError: if CRN value cannot be resolved. """ - if self.auth == "cloud": + if self.channel == Channel.IBM_CLOUD: crn = resolve_crn( - auth=self.auth, url=self.url, token=self.token, instance=self.instance + channel=self.channel, + url=self.url, + token=self.token, + instance=self.instance, ) if len(crn) == 0: raise CloudResourceNameResolutionError( @@ -115,17 +124,17 @@ def resolve_crn(self) -> None: def get_auth_handler(self) -> AuthBase: """Returns the respective authentication handler.""" - if self.auth == "cloud": + if self.channel == Channel.IBM_CLOUD: return CloudAuth(api_key=self.token, crn=self.instance) - return LegacyAuth(access_token=self.token) + return QuantumAuth(access_token=self.token) def __eq__(self, other: object) -> bool: if not isinstance(other, Account): return False return all( [ - self.auth == other.auth, + self.channel == other.channel, self.token == other.token, self.url == other.url, self.instance == other.instance, @@ -144,19 +153,20 @@ def validate(self) -> "Account": This Account instance. """ - self._assert_valid_auth(self.auth) + self._assert_valid_channel(self.channel) self._assert_valid_token(self.token) self._assert_valid_url(self.url) - self._assert_valid_instance(self.auth, self.instance) + self._assert_valid_instance(self.channel, self.instance) self._assert_valid_proxies(self.proxies) return self @staticmethod - def _assert_valid_auth(auth: AccountType) -> None: - """Assert that the auth parameter is valid.""" - if not (auth in ["cloud", "legacy"]): + def _assert_valid_channel(channel: ChannelType) -> None: + """Assert that the channel parameter is valid.""" + if not (channel in [channel.value for channel in Channel]): raise InvalidAccountError( - f"Invalid `auth` value. Expected one of ['cloud', 'legacy'], got '{auth}'." + f"Invalid `channel` value. Expected one of " + f"{[channel.value for channel in Channel]}, got '{channel}'." ) @staticmethod @@ -184,14 +194,14 @@ def _assert_valid_proxies(config: ProxyConfiguration) -> None: config.validate() @staticmethod - def _assert_valid_instance(auth: AccountType, instance: str) -> None: + def _assert_valid_instance(channel: ChannelType, instance: str) -> None: """Assert that the instance name is valid for the given account type.""" - if auth == "cloud": + if channel == Channel.IBM_CLOUD: if not (isinstance(instance, str) and len(instance) > 0): raise InvalidAccountError( f"Invalid `instance` value. Expected a non-empty string, got '{instance}'." ) - if auth == "legacy": + if channel == Channel.IBM_QUANTUM: if instance is not None: try: from_instance_format(instance) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index 08063bb6d..ca8788b04 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -15,7 +15,8 @@ import os from typing import Optional, Dict from .exceptions import AccountNotFoundError -from .account import Account, AccountType +from .account import Account, AccountType, ChannelType +from ..channel import Channel from ..proxies import ProxyConfiguration from .storage import save_config, read_config, delete_config @@ -25,8 +26,12 @@ _DEFAULT_ACCOUNT_NAME = "default" _DEFAULT_ACCOUNT_NAME_LEGACY = "default-legacy" _DEFAULT_ACCOUNT_NAME_CLOUD = "default-cloud" +_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM = "default-ibm-quantum" +_DEFAULT_ACCOUNT_NAME_IBM_CLOUD = "default-ibm-cloud" _DEFAULT_ACCOUNT_TYPE: AccountType = "cloud" +_DEFAULT_CHANNEL_TYPE: ChannelType = Channel.IBM_CLOUD _ACCOUNT_TYPES = [_DEFAULT_ACCOUNT_TYPE, "legacy"] +_CHANNEL_TYPES = [_DEFAULT_CHANNEL_TYPE, Channel.IBM_QUANTUM] class AccountManager: @@ -38,14 +43,14 @@ def save( token: Optional[str] = None, url: Optional[str] = None, instance: Optional[str] = None, - auth: Optional[AccountType] = None, + channel: Optional[ChannelType] = None, name: Optional[str] = _DEFAULT_ACCOUNT_NAME, proxies: Optional[ProxyConfiguration] = None, verify: Optional[bool] = None, overwrite: Optional[bool] = False, ) -> None: """Save account on disk.""" - config_key = name or cls._get_default_account_name(auth) + config_key = name or cls._get_default_account_name(channel) return save_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=config_key, @@ -54,7 +59,7 @@ def save( token=token, url=url, instance=instance, - auth=auth, + channel=channel, proxies=proxies, verify=verify, ) @@ -66,6 +71,7 @@ def save( def list( default: Optional[bool] = None, auth: Optional[str] = None, + channel: Optional[ChannelType] = None, name: Optional[str] = None, ) -> Dict[str, Account]: """List all accounts saved on disk.""" @@ -74,13 +80,16 @@ def _matching_name(account_name: str) -> bool: return name is None or name == account_name def _matching_auth(account: Account) -> bool: - return auth is None or account.auth == auth + return auth is None or account.channel == auth + + def _matching_channel(account: Account) -> bool: + return channel is None or account.channel == channel def _matching_default(account_name: str) -> bool: default_accounts = [ _DEFAULT_ACCOUNT_NAME, - _DEFAULT_ACCOUNT_NAME_LEGACY, - _DEFAULT_ACCOUNT_NAME_CLOUD, + _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM, + _DEFAULT_ACCOUNT_NAME_IBM_CLOUD, ] if default is None: return True @@ -99,7 +108,7 @@ def _matching_default(account_name: str) -> bool: filtered_accounts = dict( list( filter( - lambda kv: _matching_auth(kv[1]) + lambda kv: (_matching_auth(kv[1]) or _matching_channel(kv[1])) and _matching_default(kv[0]) and _matching_name(kv[0]), all_accounts, @@ -111,13 +120,13 @@ def _matching_default(account_name: str) -> bool: @classmethod def get( - cls, name: Optional[str] = None, auth: Optional[AccountType] = None + cls, name: Optional[str] = None, channel: Optional[ChannelType] = None ) -> Optional[Account]: """Read account from disk. Args: name: Account name. Takes precedence if `auth` is also specified. - auth: Account auth type. + channel: Channel type. Returns: Account information. @@ -135,18 +144,18 @@ def get( ) return Account.from_saved_format(saved_account) - auth_ = auth or _DEFAULT_ACCOUNT_TYPE - env_account = cls._from_env_variables(auth_) + channel_ = channel or _DEFAULT_CHANNEL_TYPE + env_account = cls._from_env_variables(channel_) if env_account is not None: return env_account - if auth: + if channel: saved_account = read_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, - name=cls._get_default_account_name(auth), + name=cls._get_default_account_name(channel), ) if saved_account is None: - raise AccountNotFoundError(f"No default {auth} account saved.") + raise AccountNotFoundError(f"No default {channel} account saved.") return Account.from_saved_format(saved_account) all_config = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE) @@ -161,30 +170,34 @@ def get( def delete( cls, name: Optional[str] = None, - auth: Optional[str] = None, + auth: Optional[str] = None, # pylint: disable=unused-argument + channel: Optional[ChannelType] = None, ) -> bool: """Delete account from disk.""" - config_key = name or cls._get_default_account_name(auth) + config_key = name or cls._get_default_account_name(channel) return delete_config( name=config_key, filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE ) @classmethod - def _from_env_variables(cls, auth: Optional[AccountType]) -> Optional[Account]: + def _from_env_variables(cls, channel: Optional[ChannelType]) -> Optional[Account]: """Read account from environment variable.""" token = os.getenv("QISKIT_IBM_TOKEN") url = os.getenv("QISKIT_IBM_URL") if not (token and url): return None return Account( - token=token, url=url, instance=os.getenv("QISKIT_IBM_INSTANCE"), auth=auth + token=token, + url=url, + instance=os.getenv("QISKIT_IBM_INSTANCE"), + channel=channel, ) @classmethod - def _get_default_account_name(cls, auth: AccountType) -> str: + def _get_default_account_name(cls, channel: ChannelType) -> str: return ( - _DEFAULT_ACCOUNT_NAME_LEGACY - if auth == "legacy" - else _DEFAULT_ACCOUNT_NAME_CLOUD + _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM + if channel == Channel.IBM_QUANTUM + else _DEFAULT_ACCOUNT_NAME_IBM_CLOUD ) diff --git a/qiskit_ibm_runtime/api/auth.py b/qiskit_ibm_runtime/api/auth.py index 43a5ac955..bb6fd849c 100644 --- a/qiskit_ibm_runtime/api/auth.py +++ b/qiskit_ibm_runtime/api/auth.py @@ -45,14 +45,14 @@ def get_headers(self) -> Dict: return {"Service-CRN": self.crn, "Authorization": f"apikey {self.api_key}"} -class LegacyAuth(AuthBase): - """Attaches Legacy Authentication to the given Request object.""" +class QuantumAuth(AuthBase): + """Attaches IBM Quantum Authentication to the given Request object.""" def __init__(self, access_token: str): self.access_token = access_token def __eq__(self, other: object) -> bool: - if isinstance(other, LegacyAuth): + if isinstance(other, QuantumAuth): return self.access_token == other.access_token return False diff --git a/qiskit_ibm_runtime/api/client_parameters.py b/qiskit_ibm_runtime/api/client_parameters.py index c27f7f807..4660a7799 100644 --- a/qiskit_ibm_runtime/api/client_parameters.py +++ b/qiskit_ibm_runtime/api/client_parameters.py @@ -15,8 +15,9 @@ from typing import Dict, Optional, Any, Union from ..utils import get_runtime_api_base_url -from ..api.auth import LegacyAuth, CloudAuth +from ..api.auth import QuantumAuth, CloudAuth from ..proxies import ProxyConfiguration +from ..channel import Channel TEMPLATE_IBM_HUBS = "{prefix}/Network/{hub}/Groups/{group}/Projects/{project}" """str: Template for creating an IBM Quantum URL with hub/group/project information.""" @@ -27,7 +28,7 @@ class ClientParameters: def __init__( self, - auth_type: str, + channel: str, token: str, url: str = None, instance: Optional[str] = None, @@ -37,7 +38,7 @@ def __init__( """ClientParameters constructor. Args: - auth_type: Authentication type. ``cloud`` or ``legacy``. + channel: Channel type. ``ibm_cloud`` or ``ibm_quantum``. token: IBM Quantum API token. url: IBM Quantum URL (gets replaced with a new-style URL with hub, group, project). instance: Service instance to use. @@ -46,17 +47,17 @@ def __init__( """ self.token = token self.instance = instance - self.auth_type = auth_type + self.channel = channel self.url = url self.proxies = proxies self.verify = verify - def get_auth_handler(self) -> Union[CloudAuth, LegacyAuth]: + def get_auth_handler(self) -> Union[CloudAuth, QuantumAuth]: """Returns the respective authentication handler.""" - if self.auth_type == "cloud": + if self.channel == Channel.IBM_CLOUD: return CloudAuth(api_key=self.token, crn=self.instance) - return LegacyAuth(access_token=self.token) + return QuantumAuth(access_token=self.token) def get_runtime_api_base_url(self) -> str: """Returns the Runtime API base url.""" diff --git a/qiskit_ibm_runtime/api/clients/auth.py b/qiskit_ibm_runtime/api/clients/auth.py index fd679b827..00f103b81 100644 --- a/qiskit_ibm_runtime/api/clients/auth.py +++ b/qiskit_ibm_runtime/api/clients/auth.py @@ -15,7 +15,7 @@ from typing import Dict, List, Optional, Any, Union from requests.exceptions import RequestException -from ..auth import LegacyAuth +from ..auth import QuantumAuth from ..exceptions import AuthenticationLicenseError, RequestsApiError from ..rest import Api from ..session import RetrySession @@ -55,14 +55,14 @@ def _init_service_clients(self, **request_kwargs: Any) -> Api: """ # Request an access token. self.access_token = self._request_access_token() - self.auth_api.session.auth = LegacyAuth(access_token=self.access_token) + self.auth_api.session.channel = QuantumAuth(access_token=self.access_token) self._service_urls = self.user_urls() # Create the api server client, using the access token. base_api = Api( RetrySession( self._service_urls["http"], - auth=LegacyAuth(access_token=self.access_token), + auth=QuantumAuth(access_token=self.access_token), **request_kwargs, ) ) diff --git a/qiskit_ibm_runtime/channel.py b/qiskit_ibm_runtime/channel.py new file mode 100644 index 000000000..6a6a6c1c8 --- /dev/null +++ b/qiskit_ibm_runtime/channel.py @@ -0,0 +1,22 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2022. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Channel types.""" + +from enum import Enum + + +class Channel(str, Enum): + """Channel types""" + + IBM_QUANTUM = "ibm_quantum" + IBM_CLOUD = "ibm_cloud" diff --git a/qiskit_ibm_runtime/constants.py b/qiskit_ibm_runtime/constants.py index 2031c474e..95513ab7b 100644 --- a/qiskit_ibm_runtime/constants.py +++ b/qiskit_ibm_runtime/constants.py @@ -16,7 +16,6 @@ QISKIT_IBM_RUNTIME_API_URL = "https://auth.quantum-computing.ibm.com/api" - API_TO_JOB_STATUS = { "QUEUED": JobStatus.QUEUED, "RUNNING": JobStatus.RUNNING, diff --git a/qiskit_ibm_runtime/ibm_backend.py b/qiskit_ibm_runtime/ibm_backend.py index 957445d33..e44a0d0b9 100644 --- a/qiskit_ibm_runtime/ibm_backend.py +++ b/qiskit_ibm_runtime/ibm_backend.py @@ -337,7 +337,7 @@ def properties( datetime: By specifying `datetime`, this function returns an instance of the :class:`BackendProperties` whose timestamp is closest to, but older than, the specified `datetime`. - Note that this is only supported using legacy runtime. + Note that this is only supported using ``ibm_quantum`` runtime. Returns: The backend properties or ``None`` if the backend properties are not diff --git a/qiskit_ibm_runtime/ibm_estimator.py b/qiskit_ibm_runtime/ibm_estimator.py index 23ccae44c..8c2ce7a86 100644 --- a/qiskit_ibm_runtime/ibm_estimator.py +++ b/qiskit_ibm_runtime/ibm_estimator.py @@ -67,7 +67,7 @@ class IBMEstimator(BasePrimitive): from qiskit_ibm_runtime import IBMRuntimeService, IBMEstimator - service = IBMRuntimeService(auth="cloud") + service = IBMRuntimeService(channel="ibm_cloud") estimator_factory = IBMEstimator(service=service, backend="ibmq_qasm_simulator") psi1 = RealAmplitudes(num_qubits=2, reps=2) diff --git a/qiskit_ibm_runtime/ibm_runtime_service.py b/qiskit_ibm_runtime/ibm_runtime_service.py index 007f3a209..7a1caf098 100644 --- a/qiskit_ibm_runtime/ibm_runtime_service.py +++ b/qiskit_ibm_runtime/ibm_runtime_service.py @@ -24,12 +24,13 @@ from qiskit.providers.providerutils import filter_backends from qiskit_ibm_runtime import ibm_backend -from .accounts import AccountManager, Account, AccountType +from .accounts import AccountManager, Account, AccountType, ChannelType from .proxies import ProxyConfiguration from .api.clients import AuthClient, VersionClient from .api.clients.runtime import RuntimeClient from .api.exceptions import RequestsApiError from .constants import QISKIT_IBM_RUNTIME_API_URL +from .channel import Channel from .exceptions import IBMNotAuthorizedError, IBMInputValueError, IBMAccountError from .exceptions import ( IBMRuntimeError, @@ -112,6 +113,7 @@ class IBMRuntimeService: def __init__( self, + channel: Optional[ChannelType] = None, auth: Optional[AccountType] = None, token: Optional[str] = None, url: Optional[str] = None, @@ -125,25 +127,26 @@ def __init__( An account is selected in the following order: - Account with the input `name`, if specified. - - Default account for the `auth` type, if `auth` is specified but `token` is not. - - Account defined by the input `auth` and `token`, 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 environment variables, if defined. - - Default account for the cloud account, if one is available. - - Default account for the legacy account, if one is available. + - Default account for the ``ibm_cloud`` account, if one is available. + - Default account for the ``ibm_quantum`` account, if one is available. `instance`, `proxies`, and `verify` can be used to overwrite corresponding values in the loaded account. Args: - auth: Authentication type. ``cloud`` or ``legacy``. + channel: Channel type. ``ibm_cloud`` or ``ibm_quantum``. + auth: (DEPRECATED, use `channel` instead) Authentication type. ``cloud`` or ``legacy``. token: IBM Cloud API key or IBM Quantum API token. url: The API URL. - Defaults to https://cloud.ibm.com (cloud) or - https://auth.quantum-computing.ibm.com/api (legacy). + Defaults to https://cloud.ibm.com (ibm_cloud) or + https://auth.quantum-computing.ibm.com/api (ibm_quantum). name: Name of the account to load. instance: The service instance to use. - For cloud runtime, this is the Cloud Resource Name (CRN) or the service name. - For legacy runtime, this is the hub/group/project in that format. + For ``ibm_cloud`` runtime, this is the Cloud Resource Name (CRN) or the service name. + For ``ibm_quantum`` runtime, this is the hub/group/project in that format. proxies: Proxy configuration. Supported optional keys are ``urls`` (a dictionary mapping protocol or protocol and host to the URL of the proxy, documented at https://docs.python-requests.org/en/latest/api/#requests.Session.proxies), @@ -159,10 +162,21 @@ def __init__( """ super().__init__() + if auth: + warnings.warn( + f"Use of `auth` parameter is deprecated and will " + f"be removed in a future release." + f"You can now use channel='{Channel.IBM_CLOUD}' or " + f"channel='{Channel.IBM_QUANTUM}' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._account = self._discover_account( token=token, url=url, instance=instance, + channel=channel, auth=auth, name=name, proxies=ProxyConfiguration(**proxies) if proxies else None, @@ -170,7 +184,7 @@ def __init__( ) self._client_params = ClientParameters( - auth_type=self._account.auth, + channel=self._account.channel, token=self._account.token, url=self._account.url, instance=self._account.instance, @@ -178,17 +192,17 @@ def __init__( verify=self._account.verify, ) - self._auth = self._account.auth + self._channel = self._account.channel self._programs: Dict[str, RuntimeProgram] = {} self._backends: Dict[str, "ibm_backend.IBMBackend"] = {} - if self._auth == "cloud": + if self._channel == Channel.IBM_CLOUD: self._api_client = RuntimeClient(self._client_params) # TODO: We can make the backend discovery lazy self._backends = self._discover_cloud_backends() return else: - auth_client = self._authenticate_legacy_account(self._client_params) + auth_client = self._authenticate_ibm_quantum_account(self._client_params) # Update client parameters to use authenticated values. self._client_params.url = auth_client.current_service_urls()["services"][ "runtime" @@ -210,6 +224,7 @@ def _discover_account( token: Optional[str] = None, url: Optional[str] = None, instance: Optional[str] = None, + channel: Optional[ChannelType] = None, auth: Optional[AccountType] = None, name: Optional[str] = None, proxies: Optional[ProxyConfiguration] = None, @@ -219,18 +234,23 @@ def _discover_account( account = None verify_ = verify or True if name: - if any([auth, token, url]): + if any([auth, channel, token, url]): logger.warning( - "Loading account with name %s. Any input 'auth', 'token', 'url' are ignored.", + "Loading account with name %s. Any input 'auth', " + "'channel', 'token' or 'url' are ignored.", name, ) account = AccountManager.get(name=name) elif auth: if auth not in ["legacy", "cloud"]: raise ValueError("'auth' can only be 'cloud' or 'legacy'") + if auth == "cloud": + channel = Channel.IBM_CLOUD + elif auth == "legacy": + channel = Channel.IBM_QUANTUM if token: account = Account( - auth=auth, + channel=channel, token=token, url=url, instance=instance, @@ -240,13 +260,13 @@ def _discover_account( else: if url: logger.warning( - "Loading default %s account. Input 'url' is ignored.", auth + "Loading default %s account. Input 'url' is ignored.", channel ) - account = AccountManager.get(auth=auth) + account = AccountManager.get(channel=channel) elif any([token, url]): # Let's not infer based on these attributes as they may change in the future. raise ValueError( - "'auth' is required if 'token', or 'url' is specified but 'name' is not." + "'channel' or 'auth' is required if 'token', or 'url' is specified but 'name' is not." ) if account is None: @@ -260,7 +280,7 @@ def _discover_account( account.verify = verify # resolve CRN if needed - if account.auth == "cloud": + if account.channel == Channel.IBM_CLOUD: self._resolve_crn(account) # ensure account is valid, fail early if not @@ -294,7 +314,7 @@ def _discover_cloud_backends(self) -> Dict[str, "ibm_backend.IBMBackend"]: def _resolve_crn(self, account: Account) -> None: account.resolve_crn() - def _authenticate_legacy_account( + def _authenticate_ibm_quantum_account( self, client_params: ClientParameters ) -> AuthClient: """Authenticate against IBM Quantum and populate the hub/group/projects. @@ -322,7 +342,7 @@ def _authenticate_legacy_account( service_urls = auth_client.current_service_urls() if not service_urls.get("services", {}).get(SERVICE_NAME): raise IBMNotAuthorizedError( - "This account is not authorized to use legacy runtime service." + "This account is not authorized to use ``ibm_quantum`` runtime service." ) return auth_client @@ -349,7 +369,7 @@ def _initialize_hgps( for hub_info in user_hubs: # Build credentials. hgp_params = ClientParameters( - auth_type=self._account.auth, + channel=self._account.channel, token=auth_client.current_access_token(), url=service_urls["http"], instance=to_instance_format( @@ -477,7 +497,7 @@ def backends( Args: name: Backend name to filter by. min_num_qubits: Minimum number of qubits the backend has to have. - instance: This is only supported for legacy runtime and is in the + instance: This is only supported for ``ibm_quantum`` runtime and is in the hub/group/project format. filters: More complex filters, such as lambda functions. For example:: @@ -497,7 +517,7 @@ def backends( IBMInputValueError: If an input is invalid. """ # TODO filter out input_allowed not having runtime - if self._auth == "legacy": + if self._channel == Channel.IBM_QUANTUM: if instance: backends = list(self._get_hgp(instance=instance).backends.values()) else: @@ -505,7 +525,7 @@ def backends( else: if instance: raise IBMInputValueError( - "The 'instance' keyword is only supported for legacy runtime." + "The 'instance' keyword is only supported for ``ibm_quantum`` runtime." ) backends = list(self._backends.values()) @@ -526,26 +546,41 @@ def active_account(self) -> Optional[Dict[str, str]]: return self._account.to_saved_format() @staticmethod - def delete_account(name: Optional[str] = None, auth: Optional[str] = None) -> bool: + def delete_account( + name: Optional[str] = None, + auth: Optional[str] = None, + channel: Optional[ChannelType] = None, + ) -> bool: """Delete a saved account from disk. Args: name: Name of the saved account to delete. - auth: Authentication type of the default account to delete. + auth: (DEPRECATED, use `channel` instead) Authentication type of the default + account to delete. Ignored if account name is provided. + channel: Channel type of the default account to delete. Ignored if account name is provided. Returns: True if the account was deleted. False if no account was found. """ - - return AccountManager.delete(name=name, auth=auth) + if auth: + warnings.warn( + f"Use of `auth` parameter is deprecated and will " + f"be removed in a future release." + f"You can now use channel='{Channel.IBM_CLOUD}' or " + f"channel='{Channel.IBM_QUANTUM}' instead.", + DeprecationWarning, + stacklevel=2, + ) + return AccountManager.delete(name=name, auth=auth, channel=channel) @staticmethod def save_account( token: Optional[str] = None, url: Optional[str] = None, instance: Optional[str] = None, + channel: Optional[ChannelType] = None, auth: Optional[AccountType] = None, name: Optional[str] = None, proxies: Optional[dict] = None, @@ -557,10 +592,11 @@ def save_account( Args: token: IBM Cloud API key or IBM Quantum API token. url: The API URL. - Defaults to https://cloud.ibm.com (cloud) or - https://auth.quantum-computing.ibm.com/api (legacy). - instance: The CRN (cloud) or hub/group/project (legacy). - auth: Authentication type. `cloud` or `legacy`. + Defaults to https://cloud.ibm.com (ibm_cloud) or + https://auth.quantum-computing.ibm.com/api (ibm_quantum). + instance: The CRN (ibm_cloud) or hub/group/project (ibm_quantum). + channel: Channel type. `ibm_cloud` or `ibm_quantum`. + auth: (DEPRECATED, use `channel` instead) Authentication type. `cloud` or `legacy`. name: Name of the account to save. proxies: Proxy configuration. Supported optional keys are ``urls`` (a dictionary mapping protocol or protocol and host to the URL of the proxy, @@ -570,12 +606,25 @@ def save_account( verify: Verify the server's TLS certificate. overwrite: ``True`` if the existing account is to be overwritten. """ + if auth: + warnings.warn( + f"Use of `auth` parameter is deprecated and will " + f"be removed in a future release." + f"You can now use channel='{Channel.IBM_CLOUD}' " + f"or channel={Channel.IBM_QUANTUM} instead.", + DeprecationWarning, + stacklevel=2, + ) + if auth == "cloud": + channel = Channel.IBM_CLOUD + elif auth == "legacy": + channel = Channel.IBM_QUANTUM AccountManager.save( token=token, url=url, instance=instance, - auth=auth, + channel=channel, name=name, proxies=ProxyConfiguration(**proxies) if proxies else None, verify=verify, @@ -586,13 +635,16 @@ def save_account( def saved_accounts( default: Optional[bool] = None, auth: Optional[str] = None, + channel: Optional[ChannelType] = None, name: Optional[str] = None, ) -> dict: """List the accounts saved on disk. Args: default: If set to True, only default accounts are returned. - auth: If set, only accounts with the given authentication type are returned. + auth: (DEPRECATED, use `channel` instead) If set, only accounts with the given + authentication type are returned. + channel: Channel type. `ibm_cloud` or `ibm_quantum`. name: If set, only accounts with the given name are returned. Returns: @@ -601,11 +653,21 @@ def saved_accounts( Raises: ValueError: If an invalid account is found on disk. """ - + if auth: + warnings.warn( + f"Use of `auth` parameter is deprecated and will " + f"be removed in a future release." + f"You can now use channel='{Channel.IBM_CLOUD}' " + f"or channel={Channel.IBM_QUANTUM} instead.", + DeprecationWarning, + stacklevel=2, + ) return dict( map( lambda kv: (kv[0], Account.to_saved_format(kv[1])), - AccountManager.list(default=default, auth=auth, name=name).items(), + AccountManager.list( + default=default, auth=auth, channel=channel, name=name + ).items(), ), ) @@ -618,7 +680,7 @@ def backend( Args: name: Name of the backend. - instance: This is only supported for legacy runtime and is in the + instance: This is only supported for ``ibm_quantum`` runtime and is in the hub/group/project format. Returns: @@ -795,7 +857,7 @@ def run( result_decoder: A :class:`ResultDecoder` subclass used to decode job results. ``ResultDecoder`` is used if not specified. - instance: This is only supported for legacy runtime and is in the + instance: This is only supported for ``ibm_quantum`` runtime and is in the hub/group/project format. session_id: Job ID of the first job in a runtime session. @@ -807,9 +869,9 @@ def run( RuntimeProgramNotFound: If the program cannot be found. IBMRuntimeError: An error occurred running the program. """ - if instance and self._auth != "legacy": + if instance and self._channel != Channel.IBM_QUANTUM: raise IBMInputValueError( - "The 'instance' keyword is only supported for legacy runtime. " + "The 'instance' keyword is only supported for ``ibm_quantum`` runtime. " ) # If using params object, extract as dictionary @@ -822,11 +884,11 @@ def run( if isinstance(options, dict): options = RuntimeOptions(**options) - options.validate(self.auth) + options.validate(channel=self.channel) backend = None hgp_name = None - if self._auth == "legacy": + if self._channel == Channel.IBM_QUANTUM: # Find the right hgp hgp = self._get_hgp(instance=instance, backend_name=options.backend_name) backend = hgp.backend(options.backend_name) @@ -1166,7 +1228,7 @@ def jobs( jobs are included. If ``False``, 'DONE', 'CANCELLED' and 'ERROR' jobs are included. program_id: Filter by Program ID. - instance: This is only supported for legacy runtime and is in the + instance: This is only supported for ``ibm_quantum`` runtime and is in the hub/group/project format. Returns: @@ -1177,9 +1239,9 @@ def jobs( """ hub = group = project = None if instance: - if self._auth == "cloud": + if self._channel == Channel.IBM_CLOUD: raise IBMInputValueError( - "The 'instance' keyword is only supported for legacy runtime." + "The 'instance' keyword is only supported for ``ibm_quantum`` runtime." ) hub, group, project = from_instance_format(instance) @@ -1296,7 +1358,7 @@ def least_busy( Args: min_num_qubits: Minimum number of qubits the backend has to have. - instance: This is only supported for legacy runtime and is in the + instance: This is only supported for ``ibm_quantum`` runtime and is in the hub/group/project format. filters: More complex filters, such as lambda functions. For example:: @@ -1336,7 +1398,24 @@ def auth(self) -> str: Returns: The authentication type used. """ - return self._auth + warnings.warn( + f"Use of `auth` parameter is deprecated and will " + f"be removed in a future release." + f"You can now use channel='{Channel.IBM_CLOUD}' " + f"or channel={Channel.IBM_QUANTUM} instead.", + DeprecationWarning, + stacklevel=2, + ) + return "cloud" if self._channel == Channel.IBM_CLOUD else "legacy" + + @property + def channel(self) -> str: + """Return the channel type used. + + Returns: + The channel type used. + """ + return self._channel def __repr__(self) -> str: return "<{}>".format(self.__class__.__name__) diff --git a/qiskit_ibm_runtime/ibm_sampler.py b/qiskit_ibm_runtime/ibm_sampler.py index 9c9a291aa..b344129eb 100644 --- a/qiskit_ibm_runtime/ibm_sampler.py +++ b/qiskit_ibm_runtime/ibm_sampler.py @@ -62,7 +62,7 @@ class IBMSampler(BasePrimitive): from qiskit_ibm_runtime import IBMRuntimeService, IBMSampler - service = IBMRuntimeService(auth="cloud") + service = IBMRuntimeService(channel="ibm_cloud") sampler_factory = IBMSampler(service=service, backend="ibmq_qasm_simulator") bell = QuantumCircuit(2) diff --git a/qiskit_ibm_runtime/runtime_options.py b/qiskit_ibm_runtime/runtime_options.py index 7c94dcead..e44b6f7a2 100644 --- a/qiskit_ibm_runtime/runtime_options.py +++ b/qiskit_ibm_runtime/runtime_options.py @@ -17,6 +17,7 @@ from dataclasses import dataclass from typing import Optional +from .channel import Channel from .exceptions import IBMInputValueError @@ -25,7 +26,7 @@ class RuntimeOptions: """Class for representing runtime execution options. Args: - backend_name: target backend to run on. This is required for legacy runtime. + backend_name: target backend to run on. This is required for ``ibm_quantum`` runtime. image: the runtime image used to execute the program, specified in the form of ``image_name:tag``. Not all accounts are authorized to select a different image. @@ -38,11 +39,11 @@ class RuntimeOptions: image: Optional[str] = None log_level: Optional[str] = None - def validate(self, auth: str) -> None: + def validate(self, channel: str) -> None: """Validate options. Args: - auth: authentication type. + channel: channel type. Raises: IBMInputValueError: If one or more option is invalid. @@ -53,9 +54,9 @@ def validate(self, auth: str) -> None: ): raise IBMInputValueError('"image" needs to be in form of image_name:tag') - if auth == "legacy" and not self.backend_name: + if channel == Channel.IBM_QUANTUM and not self.backend_name: raise IBMInputValueError( - '"backend_name" is required field in "options" for legacy runtime.' + '"backend_name" is required field in "options" for ``ibm_quantum`` runtime.' ) if self.log_level and not isinstance( diff --git a/qiskit_ibm_runtime/utils/utils.py b/qiskit_ibm_runtime/utils/utils.py index a06c57ed8..50622f161 100644 --- a/qiskit_ibm_runtime/utils/utils.py +++ b/qiskit_ibm_runtime/utils/utils.py @@ -24,6 +24,7 @@ import requests from ibm_cloud_sdk_core.authenticators import IAMAuthenticator from ibm_platform_services import ResourceControllerV2 +from ..channel import Channel def get_iam_api_url(cloud_url: str) -> str: @@ -38,9 +39,9 @@ def get_resource_controller_api_url(cloud_url: str) -> str: return f"{parsed_url.scheme}://resource-controller.{parsed_url.hostname}" -def resolve_crn(auth: str, url: str, instance: str, token: str) -> List[str]: +def resolve_crn(channel: str, url: str, instance: str, token: str) -> List[str]: """Resolves the Cloud Resource Name (CRN) for the given cloud account.""" - if auth != "cloud": + if channel != Channel.IBM_CLOUD: raise ValueError("CRN value can only be resolved for cloud accounts.") if is_crn(instance): @@ -85,7 +86,7 @@ def get_runtime_api_base_url(url: str, instance: str) -> str: Runtime API base URL """ - # legacy: no need to resolve runtime API URL + # ibm_quantum: no need to resolve runtime API URL api_host = url # cloud: compute runtime API URL based on crn and URL diff --git a/test/account.py b/test/account.py index a649d7ad9..8596c2b4f 100644 --- a/test/account.py +++ b/test/account.py @@ -20,7 +20,8 @@ from unittest.mock import patch from qiskit_ibm_runtime.accounts import management -from qiskit_ibm_runtime.accounts.account import CLOUD_API_URL, LEGACY_API_URL +from qiskit_ibm_runtime.accounts.account import IBM_CLOUD_API_URL, IBM_QUANTUM_API_URL +from qiskit_ibm_runtime.channel import Channel class custom_envs(ContextDecorator): @@ -127,7 +128,7 @@ def __exit__(self, *exc): def get_account_config_contents( name=None, - auth="cloud", + channel=Channel.IBM_CLOUD, token=None, url=None, instance=None, @@ -136,19 +137,21 @@ def get_account_config_contents( ): """Generate qiskitrc content""" if instance is None: - instance = "some_instance" if auth == "cloud" else "hub/group/project" + instance = ( + "some_instance" if channel == Channel.IBM_CLOUD else "hub/group/project" + ) token = token or uuid.uuid4().hex if name is None: name = ( - management._DEFAULT_ACCOUNT_NAME_CLOUD - if auth == "cloud" - else management._DEFAULT_ACCOUNT_NAME_LEGACY + management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD + if channel == Channel.IBM_CLOUD + else management._DEFAULT_ACCOUNT_NAME_IBM_QUANTUM ) if url is None: - url = CLOUD_API_URL if auth == "cloud" else LEGACY_API_URL + url = IBM_CLOUD_API_URL if channel == Channel.IBM_CLOUD else IBM_QUANTUM_API_URL out = { name: { - "auth": auth, + "channel": channel, "url": url, "token": token, "instance": instance, diff --git a/test/decorators.py b/test/decorators.py index c467f10fe..b91dbbcd7 100644 --- a/test/decorators.py +++ b/test/decorators.py @@ -19,24 +19,25 @@ from unittest import SkipTest from qiskit_ibm_runtime import IBMRuntimeService +from qiskit_ibm_runtime.channel import Channel from .unit.mock.fake_runtime_service import FakeRuntimeService -def run_legacy_and_cloud_fake(func): - """Decorator that runs a test using both legacy and cloud fake services.""" +def run_quantum_and_cloud_fake(func): + """Decorator that runs a test using both quantum and cloud fake services.""" @wraps(func) def _wrapper(self, *args, **kwargs): - legacy_service = FakeRuntimeService( - auth="legacy", token="my_token", instance="h/g/p" + ibm_quantum_service = FakeRuntimeService( + channel=Channel.IBM_QUANTUM, token="my_token", instance="h/g/p" ) cloud_service = FakeRuntimeService( - auth="cloud", + channel=Channel.IBM_CLOUD, token="my_token", instance="crn:v1:bluemix:public:quantum-computing:my-region:a/...:...::", ) - for service in [legacy_service, cloud_service]: - with self.subTest(service=service.auth): + for service in [ibm_quantum_service, cloud_service]: + with self.subTest(service=service.channel): kwargs["service"] = service func(self, *args, **kwargs) @@ -49,14 +50,18 @@ def _get_integration_test_config(): os.getenv("QISKIT_IBM_URL"), os.getenv("QISKIT_IBM_INSTANCE"), ) - auth: Any = "legacy" if url.find("quantum-computing.ibm.com") >= 0 else "cloud" - return auth, token, url, instance + channel: Any = ( + Channel.IBM_QUANTUM + if url.find("quantum-computing.ibm.com") >= 0 + else Channel.IBM_CLOUD + ) + return channel, token, url, instance def run_integration_test(func): """Decorator that injects preinitialized service and device parameters. - To be used in combinatino with the integration_test_setup decorator function.""" + To be used in combination with the integration_test_setup decorator function.""" @wraps(func) def _wrapper(self, *args, **kwargs): @@ -69,13 +74,13 @@ def _wrapper(self, *args, **kwargs): def integration_test_setup( - supported_auth: Optional[List[str]] = None, + supported_channel: Optional[List[str]] = None, init_service: Optional[bool] = True, ) -> Callable: """Returns a decorator for integration test initialization. Args: - supported_auth: a list of auth types that this test supports + supported_channel: a list of channel types that this test supports init_service: to initialize the IBMRuntimeService based on the current environment configuration and return it via the test dependencies @@ -86,26 +91,28 @@ def integration_test_setup( def _decorator(func): @wraps(func) def _wrapper(self, *args, **kwargs): - _supported_auth = ( - ["cloud", "legacy"] if supported_auth is None else supported_auth + _supported_channel = ( + [channel.value for channel in Channel] + if supported_channel is None + else supported_channel ) - auth, token, url, instance = _get_integration_test_config() - if not all([auth, token, url]): + channel, token, url, instance = _get_integration_test_config() + if not all([channel, token, url]): raise Exception("Configuration Issue") - if auth not in _supported_auth: + if channel not in _supported_channel: raise SkipTest( - f"Skipping integration test. Test does not support auth type {auth}" + f"Skipping integration test. Test does not support channel type {channel}" ) service = None if init_service: service = IBMRuntimeService( - auth=auth, token=token, url=url, instance=instance + channel=channel, token=token, url=url, instance=instance ) dependencies = IntegrationTestDependencies( - auth=auth, + channel=channel, token=token, url=url, instance=instance, @@ -126,5 +133,5 @@ class IntegrationTestDependencies: service: IBMRuntimeService instance: Optional[str] token: str - auth: str + channel: str url: str diff --git a/test/e2e/test_tutorials.py b/test/e2e/test_tutorials.py index e84306e76..89b427351 100644 --- a/test/e2e/test_tutorials.py +++ b/test/e2e/test_tutorials.py @@ -20,26 +20,29 @@ from nbconvert.preprocessors import ExecutePreprocessor from qiskit_ibm_runtime.utils.utils import to_python_identifier +from qiskit_ibm_runtime.channel import Channel from ..ibm_test_case import IBMIntegrationTestCase TUTORIAL_PATH = "docs/tutorials/**/*.ipynb" SUPPORTED_TUTORIALS = ["04_account_management"] -SUPPORTED_TUTORIALS_CLOUD = [ - "01_introduction_cloud_runtime.ipynb", +SUPPORTED_TUTORIALS_IBM_CLOUD = [ + "01_introduction_ibm_cloud_runtime.ipynb", *SUPPORTED_TUTORIALS, ] -SUPPORTED_TUTORIALS_LEGACY = [ - "02_introduction_legacy_runtime.ipynb", +SUPPORTED_TUTORIALS_IBM_QUANTUM = [ + "02_introduction_ibm_quantum_runtime.ipynb", *SUPPORTED_TUTORIALS, ] -def _is_supported(auth: str, tutorial_filename: str) -> bool: - """Not all tutorials work for all auth types. Check if the given tutorial is supported by the +def _is_supported(channel: str, tutorial_filename: str) -> bool: + """Not all tutorials work for all channel types. Check if the given tutorial is supported by the targeted environment.""" allowlist = ( - SUPPORTED_TUTORIALS_LEGACY if auth == "legacy" else SUPPORTED_TUTORIALS_CLOUD + SUPPORTED_TUTORIALS_IBM_QUANTUM + if channel == Channel.IBM_QUANTUM + else SUPPORTED_TUTORIALS_IBM_CLOUD ) return any(tutorial_filename.endswith(filename) for filename in allowlist) @@ -70,9 +73,9 @@ class TestTutorials(IBMIntegrationTestCase, metaclass=TutorialsTestCaseMeta): """Tests for tutorials.""" def _run_notebook(self, filename): - if not _is_supported(self.dependencies.auth, filename): + if not _is_supported(self.dependencies.channel, filename): self.skipTest( - f"Tutorial {filename} not supported on {self.dependencies.auth}" + f"Tutorial {filename} not supported on {self.dependencies.channel}" ) # Create the preprocessor. diff --git a/test/ibm_test_case.py b/test/ibm_test_case.py index 398a86d98..3a57561fc 100644 --- a/test/ibm_test_case.py +++ b/test/ibm_test_case.py @@ -93,12 +93,12 @@ def tearDown(self) -> None: super().tearDown() # Delete programs service = self.service - for prog in self.to_delete[service.auth]: + for prog in self.to_delete[service.channel]: with suppress(Exception): service.delete_program(prog) # Cancel and delete jobs. - for job in self.to_cancel[service.auth]: + for job in self.to_cancel[service.channel]: with suppress(Exception): job.cancel() with suppress(Exception): @@ -120,7 +120,7 @@ def _upload_program( metadata["max_execution_time"] = max_execution_time metadata["is_public"] = is_public program_id = service.upload_program(data=data, metadata=metadata) - self.to_delete[service.auth].append(program_id) + self.to_delete[service.channel].append(program_id) return program_id @@ -143,11 +143,11 @@ def tearDownClass(cls) -> None: # Delete default program. with suppress(Exception): service = cls.service - service.delete_program(cls.program_ids[service.auth]) + service.delete_program(cls.program_ids[service.channel]) cls.log.debug( "Deleted %s program %s", - service.auth, - cls.program_ids[service.auth], + service.channel, + cls.program_ids[service.channel], ) @classmethod @@ -160,15 +160,15 @@ def _create_default_program(cls): service = cls.service try: prog_id = service.upload_program(data=RUNTIME_PROGRAM, metadata=metadata) - cls.log.debug("Uploaded %s program %s", service.auth, prog_id) - cls.program_ids[service.auth] = prog_id + cls.log.debug("Uploaded %s program %s", service.channel, prog_id) + cls.program_ids[service.channel] = prog_id except IBMNotAuthorizedError: raise unittest.SkipTest("No upload access.") @classmethod def _find_sim_backends(cls): """Find a simulator backend for each service.""" - cls.sim_backends[cls.service.auth] = cls.service.backends(simulator=True)[ + cls.sim_backends[cls.service.channel] = cls.service.backends(simulator=True)[ 0 ].name @@ -185,7 +185,7 @@ def _run_program( log_level=None, ): """Run a program.""" - self.log.debug("Running program on %s", service.auth) + self.log.debug("Running program on %s", service.channel) inputs = ( inputs if inputs is not None @@ -195,14 +195,14 @@ def _run_program( "final_result": final_result or {}, } ) - pid = program_id or self.program_ids[service.auth] + pid = program_id or self.program_ids[service.channel] backend_name = ( - backend if backend is not None else self.sim_backends[service.auth] + backend if backend is not None else self.sim_backends[service.channel] ) options = {"backend_name": backend_name, "log_level": log_level} job = service.run( program_id=pid, inputs=inputs, options=options, callback=callback ) self.log.info("Runtime job %s submitted.", job.job_id) - self.to_cancel[service.auth].append(job) + self.to_cancel[service.channel].append(job) return job diff --git a/test/integration/test_account.py b/test/integration/test_account.py index 50b2fe3c5..0287fcdd8 100644 --- a/test/integration/test_account.py +++ b/test/integration/test_account.py @@ -18,6 +18,7 @@ from qiskit_ibm_runtime import IBMRuntimeService from qiskit_ibm_runtime.accounts import CloudResourceNameResolutionError +from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.utils.utils import ( get_resource_controller_api_url, get_iam_api_url, @@ -49,18 +50,18 @@ def _get_service_instance_name_for_crn( class TestIntegrationAccount(IBMIntegrationTestCase): """Integration tests for account management.""" - def _skip_on_legacy(self): - if self.dependencies.auth == "legacy": - self.skipTest("Not supported on legacy") + def _skip_on_ibm_quantum(self): + if self.dependencies.channel == Channel.IBM_QUANTUM: + self.skipTest(f"Not supported on {Channel.IBM_QUANTUM}") def test_resolve_crn_for_valid_service_instance_name(self): """Verify if CRN is transparently resolved based for an existing service instance name.""" - self._skip_on_legacy() + self._skip_on_ibm_quantum() service_instance_name = _get_service_instance_name_for_crn(self.dependencies) with self.subTest(instance=service_instance_name): service = IBMRuntimeService( - auth="cloud", + channel=Channel.IBM_CLOUD, url=self.dependencies.url, token=self.dependencies.token, instance=service_instance_name, @@ -72,14 +73,14 @@ def test_resolve_crn_for_valid_service_instance_name(self): def test_resolve_crn_for_invalid_service_instance_name(self): """Verify if CRN resolution fails for non-existing service instance name.""" - self._skip_on_legacy() + self._skip_on_ibm_quantum() service_instance_name = "-non-existing-service-name-" with self.subTest(instance="-non-existing-service-name-"), self.assertRaises( CloudResourceNameResolutionError ): IBMRuntimeService( - auth="cloud", + channel=Channel.IBM_CLOUD, url=self.dependencies.url, token=self.dependencies.token, instance=service_instance_name, diff --git a/test/integration/test_auth_client.py b/test/integration/test_auth_client.py index 6178f4c25..0028b2bb6 100644 --- a/test/integration/test_auth_client.py +++ b/test/integration/test_auth_client.py @@ -14,6 +14,7 @@ import re +from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.api.client_parameters import ClientParameters from qiskit_ibm_runtime.api.clients import AuthClient from qiskit_ibm_runtime.api.exceptions import ApiError @@ -24,41 +25,41 @@ class TestAuthClient(IBMTestCase): """Tests for the AuthClient.""" - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_valid_login(self, dependencies: IntegrationTestDependencies) -> None: """Test valid authentication.""" client = self._init_auth_client(dependencies.token, dependencies.url) self.assertTrue(client.access_token) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_url_404(self, dependencies: IntegrationTestDependencies) -> None: """Test login against a 404 URL""" url_404 = re.sub(r"/api.*$", "/api/TEST_404", dependencies.url) with self.assertRaises(ApiError): _ = self._init_auth_client(dependencies.token, url_404) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_invalid_token(self, dependencies: IntegrationTestDependencies) -> None: """Test login using invalid token.""" qe_token = "INVALID_TOKEN" with self.assertRaises(ApiError): _ = self._init_auth_client(qe_token, dependencies.url) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_url_unreachable(self, dependencies: IntegrationTestDependencies) -> None: """Test login against an invalid (malformed) URL.""" qe_url = "INVALID_URL" with self.assertRaises(ApiError): _ = self._init_auth_client(dependencies.token, qe_url) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_api_version(self, dependencies: IntegrationTestDependencies) -> None: """Check the version of the QX API.""" client = self._init_auth_client(dependencies.token, dependencies.url) version = client.api_version() self.assertIsNotNone(version) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_user_urls(self, dependencies: IntegrationTestDependencies) -> None: """Check the user urls of the QX API.""" client = self._init_auth_client(dependencies.token, dependencies.url) @@ -66,7 +67,7 @@ def test_user_urls(self, dependencies: IntegrationTestDependencies) -> None: self.assertIsNotNone(user_urls) self.assertTrue("http" in user_urls and "ws" in user_urls) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_user_hubs(self, dependencies: IntegrationTestDependencies) -> None: """Check the user hubs of the QX API.""" client = self._init_auth_client(dependencies.token, dependencies.url) @@ -80,5 +81,5 @@ def test_user_hubs(self, dependencies: IntegrationTestDependencies) -> None: def _init_auth_client(self, token, url): """Return an AuthClient.""" - params = ClientParameters(auth_type="legacy", token=token, url=url) + params = ClientParameters(channel=Channel.IBM_QUANTUM, token=token, url=url) return AuthClient(params) diff --git a/test/integration/test_backend.py b/test/integration/test_backend.py index 86d039272..c6929187f 100644 --- a/test/integration/test_backend.py +++ b/test/integration/test_backend.py @@ -15,6 +15,7 @@ from unittest import SkipTest from qiskit.transpiler.target import Target +from qiskit_ibm_runtime.channel import Channel from ..ibm_test_case import IBMIntegrationTestCase from ..decorators import run_integration_test @@ -51,10 +52,10 @@ def setUpClass(cls): # pylint: disable=arguments-differ # pylint: disable=no-value-for-parameter super().setUpClass() - if cls.dependencies.auth == "cloud": + if cls.dependencies.channel == Channel.IBM_CLOUD: # TODO use real device when cloud supports it cls.backend = cls.dependencies.service.least_busy(min_num_qubits=5) - if cls.dependencies.auth == "legacy": + if cls.dependencies.channel == Channel.IBM_QUANTUM: cls.backend = cls.dependencies.service.least_busy( simulator=False, min_num_qubits=5, instance=cls.dependencies.instance ) diff --git a/test/integration/test_backend_serialization.py b/test/integration/test_backend_serialization.py index 3efd6af58..e6bdb8090 100644 --- a/test/integration/test_backend_serialization.py +++ b/test/integration/test_backend_serialization.py @@ -16,6 +16,7 @@ import dateutil.parser +from qiskit_ibm_runtime.channel import Channel from ..ibm_test_case import IBMIntegrationTestCase from ..decorators import ( run_integration_test, @@ -28,7 +29,11 @@ class TestSerialization(IBMIntegrationTestCase): @run_integration_test def test_backend_configuration(self, service): """Test deserializing backend configuration.""" - instance = self.dependencies.instance if service.auth == "legacy" else None + instance = ( + self.dependencies.instance + if service.channel == Channel.IBM_QUANTUM + else None + ) backends = service.backends( operational=True, simulator=False, instance=instance ) @@ -56,7 +61,11 @@ def test_backend_configuration(self, service): @run_integration_test def test_pulse_defaults(self, service): """Test deserializing backend configuration.""" - instance = self.dependencies.instance if service.auth == "legacy" else None + instance = ( + self.dependencies.instance + if service.channel == Channel.IBM_QUANTUM + else None + ) backends = service.backends( operational=True, open_pulse=True, instance=instance ) @@ -73,7 +82,11 @@ def test_pulse_defaults(self, service): @run_integration_test def test_backend_properties(self, service): """Test deserializing backend properties.""" - instance = self.dependencies.instance if service.auth == "legacy" else None + instance = ( + self.dependencies.instance + if service.channel == Channel.IBM_QUANTUM + else None + ) backends = service.backends( operational=True, simulator=False, instance=instance ) diff --git a/test/integration/test_basic_server_paths.py b/test/integration/test_basic_server_paths.py index aa8f705a3..4e1098c88 100644 --- a/test/integration/test_basic_server_paths.py +++ b/test/integration/test_basic_server_paths.py @@ -12,6 +12,7 @@ """Tests that hit all the basic server endpoints using both a public and premium h/g/p.""" +from qiskit_ibm_runtime.channel import Channel from ..ibm_test_case import IBMTestCase from ..decorators import integration_test_setup, IntegrationTestDependencies @@ -20,7 +21,7 @@ class TestBasicServerPaths(IBMTestCase): """Test the basic server endpoints using both a public and premium provider.""" @classmethod - @integration_test_setup(supported_auth=["legacy"]) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM]) def setUpClass(cls, dependencies: IntegrationTestDependencies) -> None: # pylint: disable=arguments-differ super().setUpClass() diff --git a/test/integration/test_job.py b/test/integration/test_job.py index 715dc5dd4..c8a11f4a3 100644 --- a/test/integration/test_job.py +++ b/test/integration/test_job.py @@ -18,6 +18,7 @@ from qiskit.providers.jobstatus import JOB_FINAL_STATES, JobStatus from qiskit.test.decorators import slow_test +from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.constants import API_TO_JOB_ERROR_MESSAGE from qiskit_ibm_runtime.exceptions import ( RuntimeJobFailureError, @@ -59,8 +60,8 @@ def test_run_program_real_device(self, service): def test_run_program_cloud_no_backend(self, service): """Test running a cloud program with no backend.""" - if self.dependencies.auth == "legacy": - self.skipTest("Not supported on legacy") + if self.dependencies.channel == Channel.IBM_QUANTUM: + self.skipTest(f"Not supported on {Channel.IBM_QUANTUM}") job = self._run_program(service, backend="") self.assertTrue(job.backend, f"Job {job.job_id} has no backend.") @@ -205,13 +206,13 @@ def test_job_inputs(self, service): def test_job_backend(self, service): """Test job backend.""" job = self._run_program(service) - self.assertEqual(self.sim_backends[service.auth], job.backend.name) + self.assertEqual(self.sim_backends[service.channel], job.backend.name) @run_integration_test def test_job_program_id(self, service): """Test job program ID.""" job = self._run_program(service) - self.assertEqual(self.program_ids[service.auth], job.program_id) + self.assertEqual(self.program_ids[service.channel], job.program_id) @run_integration_test def test_wait_for_final_state(self, service): diff --git a/test/integration/test_proxies.py b/test/integration/test_proxies.py index 1021a3c49..f06a53322 100644 --- a/test/integration/test_proxies.py +++ b/test/integration/test_proxies.py @@ -21,6 +21,7 @@ from qiskit_ibm_runtime.api.client_parameters import ClientParameters from qiskit_ibm_runtime.api.clients import AuthClient, VersionClient from qiskit_ibm_runtime.api.clients.runtime import RuntimeClient +from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.api.exceptions import RequestsApiError from qiskit_ibm_runtime.proxies import ProxyConfiguration from ..ibm_test_case import IBMTestCase @@ -55,7 +56,7 @@ def tearDown(self): # wait for the process to terminate self.proxy_process.wait() - @integration_test_setup(supported_auth=["cloud"]) + @integration_test_setup(supported_channel=["cloud"]) def test_proxies_cloud_runtime_client( self, dependencies: IntegrationTestDependencies ) -> None: @@ -70,13 +71,13 @@ def test_proxies_cloud_runtime_client( proxy_output = self.proxy_process.stdout.read().decode("utf-8") self.assertIn(api_line, proxy_output) - @integration_test_setup(supported_auth=["legacy"], init_service=False) - def test_proxies_legacy_runtime_client( + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + def test_proxies_ibm_quantum_runtime_client( self, dependencies: IntegrationTestDependencies ) -> None: """Should reach the proxy using RuntimeClient.""" service = IBMRuntimeService( - auth="legacy", + channel=Channel.IBM_QUANTUM, token=dependencies.token, url=dependencies.url, proxies={"urls": VALID_PROXIES}, @@ -94,13 +95,13 @@ def test_proxies_legacy_runtime_client( # Check if the API call (querying providers list) went through proxy. self.assertIn(api_line, proxy_output) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_proxies_account_client( self, dependencies: IntegrationTestDependencies ) -> None: """Should reach the proxy using AccountClient.""" service = IBMRuntimeService( - auth="legacy", + channel=Channel.IBM_QUANTUM, token=dependencies.token, url=dependencies.url, proxies={"urls": VALID_PROXIES}, @@ -118,7 +119,7 @@ def test_proxies_account_client( # Check if the API call (querying providers list) went through proxy. self.assertIn(api_line, proxy_output) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_proxies_authclient( self, dependencies: IntegrationTestDependencies ) -> None: @@ -127,7 +128,7 @@ def test_proxies_authclient( dependencies.url ) params = ClientParameters( - auth_type="legacy", + channel=Channel.IBM_QUANTUM, token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls=VALID_PROXIES), @@ -141,7 +142,7 @@ def test_proxies_authclient( self.proxy_process.stdout.read().decode("utf-8"), ) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_proxies_versionclient( self, dependencies: IntegrationTestDependencies ) -> None: @@ -159,13 +160,13 @@ def test_proxies_versionclient( self.proxy_process.stdout.read().decode("utf-8"), ) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_invalid_proxy_port_runtime_client( self, dependencies: IntegrationTestDependencies ) -> None: """Should raise RequestApiError with ProxyError using RuntimeClient.""" params = ClientParameters( - auth_type="legacy", + channel=Channel.IBM_QUANTUM, token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls=INVALID_PORT_PROXIES), @@ -175,13 +176,13 @@ def test_invalid_proxy_port_runtime_client( client.list_programs(limit=1) self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_invalid_proxy_port_authclient( self, dependencies: IntegrationTestDependencies ) -> None: """Should raise RequestApiError with ProxyError using AuthClient.""" params = ClientParameters( - auth_type="legacy", + channel=Channel.IBM_QUANTUM, token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls=INVALID_PORT_PROXIES), @@ -191,7 +192,7 @@ def test_invalid_proxy_port_authclient( self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_invalid_proxy_port_versionclient( self, dependencies: IntegrationTestDependencies ) -> None: @@ -204,13 +205,13 @@ def test_invalid_proxy_port_versionclient( self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_invalid_proxy_address_runtime_client( self, dependencies: IntegrationTestDependencies ) -> None: """Should raise RequestApiError with ProxyError using RuntimeClient.""" params = ClientParameters( - auth_type="legacy", + channel=Channel.IBM_QUANTUM, token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls=INVALID_ADDRESS_PROXIES), @@ -221,13 +222,13 @@ def test_invalid_proxy_address_runtime_client( self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_invalid_proxy_address_authclient( self, dependencies: IntegrationTestDependencies ) -> None: """Should raise RequestApiError with ProxyError using AuthClient.""" params = ClientParameters( - auth_type="legacy", + channel=Channel.IBM_QUANTUM, token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls=INVALID_ADDRESS_PROXIES), @@ -237,7 +238,7 @@ def test_invalid_proxy_address_authclient( self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_invalid_proxy_address_versionclient( self, dependencies: IntegrationTestDependencies ) -> None: @@ -250,7 +251,7 @@ def test_invalid_proxy_address_versionclient( self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_auth=["legacy"], init_service=False) + @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) def test_proxy_urls(self, dependencies: IntegrationTestDependencies) -> None: """Test different forms of the proxy urls.""" test_urls = [ @@ -261,7 +262,7 @@ def test_proxy_urls(self, dependencies: IntegrationTestDependencies) -> None: for proxy_url in test_urls: with self.subTest(proxy_url=proxy_url): params = ClientParameters( - auth_type="legacy", + channel=Channel.IBM_QUANTUM, token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls={"https": proxy_url}), diff --git a/test/integration/test_retrieve_job.py b/test/integration/test_retrieve_job.py index b2b4a1a91..f892d4b29 100644 --- a/test/integration/test_retrieve_job.py +++ b/test/integration/test_retrieve_job.py @@ -16,6 +16,7 @@ from qiskit.providers.jobstatus import JobStatus +from qiskit_ibm_runtime.channel import Channel from ..ibm_test_case import IBMIntegrationJobTestCase from ..decorators import run_integration_test from ..utils import wait_for_status, get_real_device @@ -33,7 +34,7 @@ def test_retrieve_job_queued(self, service): wait_for_status(job, JobStatus.QUEUED) rjob = service.job(job.job_id) self.assertEqual(job.job_id, rjob.job_id) - self.assertEqual(self.program_ids[service.auth], rjob.program_id) + self.assertEqual(self.program_ids[service.channel], rjob.program_id) @run_integration_test def test_retrieve_job_running(self, service): @@ -42,7 +43,7 @@ def test_retrieve_job_running(self, service): wait_for_status(job, JobStatus.RUNNING) rjob = service.job(job.job_id) self.assertEqual(job.job_id, rjob.job_id) - self.assertEqual(self.program_ids[service.auth], rjob.program_id) + self.assertEqual(self.program_ids[service.channel], rjob.program_id) @run_integration_test def test_retrieve_job_done(self, service): @@ -51,7 +52,7 @@ def test_retrieve_job_done(self, service): job.wait_for_final_state() rjob = service.job(job.job_id) self.assertEqual(job.job_id, rjob.job_id) - self.assertEqual(self.program_ids[service.auth], rjob.program_id) + self.assertEqual(self.program_ids[service.channel], rjob.program_id) @run_integration_test def test_retrieve_all_jobs(self, service): @@ -74,7 +75,7 @@ def test_retrieve_jobs_limit(self, service): for _ in range(3): jobs.append(self._run_program(service)) - rjobs = service.jobs(limit=2, program_id=self.program_ids[service.auth]) + rjobs = service.jobs(limit=2, program_id=self.program_ids[service.channel]) self.assertEqual(len(rjobs), 2, f"Retrieved jobs: {[j.job_id for j in rjobs]}") job_ids = {job.job_id for job in jobs} rjob_ids = {rjob.job_id for rjob in rjobs} @@ -130,8 +131,8 @@ def test_retrieve_jobs_by_program_id(self, service): @run_integration_test def test_jobs_filter_by_hgp(self, service): """Test retrieving jobs by hgp.""" - if self.dependencies.auth == "cloud": - self.skipTest("Not supported on cloud") + if self.dependencies.channel == Channel.IBM_CLOUD: + self.skipTest(f"Not supported on {Channel.IBM_CLOUD}") default_hgp = list(service._hgps.keys())[0] program_id = self._upload_program(service) diff --git a/test/unit/mock/fake_legacy_auth_client.py b/test/unit/mock/fake_quantum_auth_client.py similarity index 98% rename from test/unit/mock/fake_legacy_auth_client.py rename to test/unit/mock/fake_quantum_auth_client.py index 05865857a..dd65a1919 100644 --- a/test/unit/mock/fake_legacy_auth_client.py +++ b/test/unit/mock/fake_quantum_auth_client.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Fake legacy AuthClient.""" +"""Fake ibm_quantum AuthClient.""" from typing import Dict, List, Union, Optional diff --git a/test/unit/mock/fake_runtime_client.py b/test/unit/mock/fake_runtime_client.py index bb4775d0e..e9eb5e819 100644 --- a/test/unit/mock/fake_runtime_client.py +++ b/test/unit/mock/fake_runtime_client.py @@ -21,18 +21,21 @@ from typing import Optional, Dict, Any from qiskit_ibm_runtime.api.exceptions import RequestsApiError +from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.utils import RuntimeEncoder from qiskit_ibm_runtime.utils.hgp import from_instance_format from .fake_account_client import BaseFakeAccountClient def cloud_only(func): - """Decorator that runs a test using both legacy and cloud services.""" + """Decorator that runs a test using only ibm_cloud services.""" @wraps(func) def _wrapper(self, *args, **kwargs): - if self._auth_type != "cloud": - raise ValueError(f"Method {func} called by a legacy client!") + if self._channel != Channel.IBM_CLOUD: + raise ValueError( + f"Method {func} called by an {Channel.IBM_QUANTUM} client!" + ) return func(self, *args, **kwargs) return _wrapper @@ -264,7 +267,7 @@ def __init__(self, *args, **kwargs): self._backend_client = test_options.get( "backend_client", BaseFakeAccountClient() ) - self._auth_type = test_options.get("auth_type", "legacy") + self._channel = test_options.get("channel", Channel.IBM_QUANTUM) def set_job_classes(self, classes): """Set job classes to use.""" @@ -514,5 +517,5 @@ def backend_pulse_defaults(self, backend_name: str) -> Dict[str, Any]: @cloud_only def _check_cloud_only(self): - if self._auth_type != "cloud": - raise ValueError("A backend method is called by a legacy client!") + if self._channel != Channel.IBM_CLOUD: + raise ValueError("A backend method is called by an ibm_quantum client!") diff --git a/test/unit/mock/fake_runtime_service.py b/test/unit/mock/fake_runtime_service.py index a766fd5ec..692ee0c44 100644 --- a/test/unit/mock/fake_runtime_service.py +++ b/test/unit/mock/fake_runtime_service.py @@ -17,6 +17,7 @@ from unittest import mock from qiskit_ibm_runtime.accounts import Account +from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.api.client_parameters import ClientParameters from qiskit_ibm_runtime.api.clients import AuthClient from qiskit_ibm_runtime.hub_group_project import HubGroupProject @@ -52,7 +53,7 @@ def __init__(self, *args, **kwargs): ): super().__init__(*args, **kwargs) - def _authenticate_legacy_account( + def _authenticate_ibm_quantum_account( self, client_params: ClientParameters ) -> "FakeAuthClient": """Mock authentication.""" @@ -73,7 +74,7 @@ def _initialize_hgps( hgp_name = self.DEFAULT_HGPS[idx] hgp_params = ClientParameters( - auth_type="legacy", + channel=Channel.IBM_QUANTUM, token="some_token", url="some_url", instance=hgp_name, @@ -113,7 +114,7 @@ def _discover_cloud_backends(self): test_options = { "backend_client": self._fake_account_client, - "auth_type": "cloud", + "channel": Channel.IBM_CLOUD, } self._api_client = BaseFakeRuntimeClient(test_options=test_options) return super()._discover_cloud_backends() @@ -128,7 +129,10 @@ def __init__(self): # pylint: disable=super-init-not-called def current_service_urls(self): """Return service urls.""" - return {"http": "legacy_api_url", "services": {"runtime": "legacy_runtime_url"}} + return { + "http": "IBM_QUANTUM_API_URL", + "services": {"runtime": "ibm_quantum_runtime_url"}, + } def current_access_token(self): """Return access token.""" diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 659aeb255..21e87edcb 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -27,7 +27,8 @@ AccountNotFoundError, InvalidAccountError, ) -from qiskit_ibm_runtime.accounts.account import CLOUD_API_URL, LEGACY_API_URL +from qiskit_ibm_runtime.channel import Channel +from qiskit_ibm_runtime.accounts.account import IBM_CLOUD_API_URL, IBM_QUANTUM_API_URL from qiskit_ibm_runtime.proxies import ProxyConfiguration from .mock.fake_runtime_service import FakeRuntimeService from ..ibm_test_case import IBMTestCase @@ -38,15 +39,15 @@ custom_envs, ) -_TEST_LEGACY_ACCOUNT = Account( - auth="legacy", +_TEST_IBM_QUANTUM_ACCOUNT = Account( + channel=Channel.IBM_QUANTUM, token="token-x", url="https://auth.quantum-computing.ibm.com/api", instance="ibm-q/open/main", ) -_TEST_CLOUD_ACCOUNT = Account( - auth="cloud", +_TEST_IBM_CLOUD_ACCOUNT = Account( + channel=Channel.IBM_CLOUD, token="token-y", url="https://cloud.ibm.com", instance="crn:v1:bluemix:public:quantum-computing:us-east:a/...::", @@ -60,24 +61,26 @@ class TestAccount(IBMTestCase): """Tests for Account class.""" dummy_token = "123" - dummy_cloud_url = "https://us-east.quantum-computing.cloud.ibm.com" - dummy_legacy_url = "https://auth.quantum-computing.ibm.com/api" + dummy_ibm_cloud_url = "https://us-east.quantum-computing.cloud.ibm.com" + dummy_ibm_quantum_url = "https://auth.quantum-computing.ibm.com/api" def test_skip_crn_resolution_for_crn(self): """Test that CRN resolution is skipped if the instance value is already a CRN.""" - account = copy.deepcopy(_TEST_CLOUD_ACCOUNT) + account = copy.deepcopy(_TEST_IBM_CLOUD_ACCOUNT) account.resolve_crn() - self.assertEqual(account.instance, _TEST_CLOUD_ACCOUNT.instance) + self.assertEqual(account.instance, _TEST_IBM_CLOUD_ACCOUNT.instance) - def test_invalid_auth(self): - """Test invalid values for auth parameter.""" + def test_invalid_channel(self): + """Test invalid values for channel parameter.""" with self.assertRaises(InvalidAccountError) as err: - invalid_auth: Any = "phantom" + invalid_channel: Any = "phantom" Account( - auth=invalid_auth, token=self.dummy_token, url=self.dummy_cloud_url + channel=invalid_channel, + token=self.dummy_token, + url=self.dummy_ibm_cloud_url, ).validate() - self.assertIn("Invalid `auth` value.", str(err.exception)) + self.assertIn("Invalid `channel` value.", str(err.exception)) def test_invalid_token(self): """Test invalid values for token parameter.""" @@ -87,7 +90,9 @@ def test_invalid_token(self): with self.subTest(token=token): with self.assertRaises(InvalidAccountError) as err: Account( - auth="cloud", token=token, url=self.dummy_cloud_url + channel=Channel.IBM_CLOUD, + token=token, + url=self.dummy_ibm_cloud_url, ).validate() self.assertIn("Invalid `token` value.", str(err.exception)) @@ -95,7 +100,7 @@ def test_invalid_url(self): """Test invalid values for url parameter.""" subtests = [ - {"auth": "cloud", "url": 123}, + {"channel": Channel.IBM_CLOUD, "url": 123}, ] for params in subtests: with self.subTest(params=params): @@ -107,15 +112,15 @@ def test_invalid_instance(self): """Test invalid values for instance parameter.""" subtests = [ - {"auth": "cloud", "instance": ""}, - {"auth": "cloud"}, - {"auth": "legacy", "instance": "no-hgp-format"}, + {"channel": Channel.IBM_CLOUD, "instance": ""}, + {"channel": Channel.IBM_CLOUD}, + {"channel": Channel.IBM_QUANTUM, "instance": "no-hgp-format"}, ] for params in subtests: with self.subTest(params=params): with self.assertRaises(InvalidAccountError) as err: Account( - **params, token=self.dummy_token, url=self.dummy_cloud_url + **params, token=self.dummy_token, url=self.dummy_ibm_cloud_url ).validate() self.assertIn("Invalid `instance` value.", str(err.exception)) @@ -138,9 +143,9 @@ def test_invalid_proxy_config(self): with self.assertRaises(ValueError) as err: Account( **params, - auth="legacy", + channel=Channel.IBM_QUANTUM, token=self.dummy_token, - url=self.dummy_cloud_url, + url=self.dummy_ibm_cloud_url, ).validate() self.assertIn("Invalid proxy configuration", str(err.exception)) @@ -151,22 +156,22 @@ class TestAccountManager(IBMTestCase): """Tests for AccountManager class.""" @temporary_account_config_file( - contents={"conflict": _TEST_CLOUD_ACCOUNT.to_saved_format()} + contents={"conflict": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format()} ) def test_save_without_override(self): """Test to override an existing account without setting overwrite=True.""" with self.assertRaises(AccountAlreadyExistsError): AccountManager.save( name="conflict", - token=_TEST_CLOUD_ACCOUNT.token, - url=_TEST_CLOUD_ACCOUNT.url, - instance=_TEST_CLOUD_ACCOUNT.instance, - auth="cloud", + token=_TEST_IBM_CLOUD_ACCOUNT.token, + url=_TEST_IBM_CLOUD_ACCOUNT.url, + instance=_TEST_IBM_CLOUD_ACCOUNT.instance, + channel=Channel.IBM_CLOUD, overwrite=False, ) @temporary_account_config_file( - contents={"conflict": _TEST_CLOUD_ACCOUNT.to_saved_format()} + contents={"conflict": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format()} ) def test_get_none(self): """Test to get an account with an invalid name.""" @@ -184,27 +189,31 @@ def test_save_get(self): # - the name passed to AccountManager.get sub_tests = [ # verify accounts can be saved and retrieved via custom names - (_TEST_LEGACY_ACCOUNT, "acct-1", "acct-1"), - (_TEST_CLOUD_ACCOUNT, "acct-2", "acct-2"), - # verify default account name handling for cloud accounts - (_TEST_CLOUD_ACCOUNT, None, management._DEFAULT_ACCOUNT_NAME_CLOUD), - (_TEST_CLOUD_ACCOUNT, None, None), - # verify default account name handling for legacy accounts - (_TEST_LEGACY_ACCOUNT, None, management._DEFAULT_ACCOUNT_NAME_LEGACY), + (_TEST_IBM_QUANTUM_ACCOUNT, "acct-1", "acct-1"), + (_TEST_IBM_CLOUD_ACCOUNT, "acct-2", "acct-2"), + # verify default account name handling for ibm_cloud accounts + (_TEST_IBM_CLOUD_ACCOUNT, None, management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD), + (_TEST_IBM_CLOUD_ACCOUNT, None, None), + # verify default account name handling for ibm_quantum accounts + ( + _TEST_IBM_QUANTUM_ACCOUNT, + None, + management._DEFAULT_ACCOUNT_NAME_IBM_QUANTUM, + ), # verify account override - (_TEST_LEGACY_ACCOUNT, "acct", "acct"), - (_TEST_CLOUD_ACCOUNT, "acct", "acct"), + (_TEST_IBM_QUANTUM_ACCOUNT, "acct", "acct"), + (_TEST_IBM_CLOUD_ACCOUNT, "acct", "acct"), ] for account, name_save, name_get in sub_tests: with self.subTest( - f"for account type '{account.auth}' " + f"for account type '{account.channel}' " f"using `save(name={name_save})` and `get(name={name_get})`" ): AccountManager.save( token=account.token, url=account.url, instance=account.instance, - auth=account.auth, + channel=account.channel, proxies=account.proxies, verify=account.verify, name=name_save, @@ -215,8 +224,8 @@ def test_save_get(self): @temporary_account_config_file( contents=json.dumps( { - "cloud": _TEST_CLOUD_ACCOUNT.to_saved_format(), - "legacy": _TEST_LEGACY_ACCOUNT.to_saved_format(), + "ibm_cloud": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format(), + "ibm_quantum": _TEST_IBM_QUANTUM_ACCOUNT.to_saved_format(), } ) ) @@ -225,15 +234,15 @@ def test_list(self): with temporary_account_config_file( contents={ - "key1": _TEST_CLOUD_ACCOUNT.to_saved_format(), - "key2": _TEST_LEGACY_ACCOUNT.to_saved_format(), + "key1": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format(), + "key2": _TEST_IBM_QUANTUM_ACCOUNT.to_saved_format(), } ), self.subTest("non-empty list of accounts"): accounts = AccountManager.list() self.assertEqual(len(accounts), 2) - self.assertEqual(accounts["key1"], _TEST_CLOUD_ACCOUNT) - self.assertTrue(accounts["key2"], _TEST_LEGACY_ACCOUNT) + self.assertEqual(accounts["key1"], _TEST_IBM_CLOUD_ACCOUNT) + self.assertTrue(accounts["key2"], _TEST_IBM_QUANTUM_ACCOUNT) with temporary_account_config_file(contents={}), self.subTest( "empty list of accounts" @@ -242,33 +251,37 @@ def test_list(self): with temporary_account_config_file( contents={ - "key1": _TEST_CLOUD_ACCOUNT.to_saved_format(), - "key2": _TEST_LEGACY_ACCOUNT.to_saved_format(), - management._DEFAULT_ACCOUNT_NAME_CLOUD: Account( - "cloud", "token-cloud", instance="crn:123" + "key1": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format(), + "key2": _TEST_IBM_QUANTUM_ACCOUNT.to_saved_format(), + management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD: Account( + Channel.IBM_CLOUD, "token-ibm-cloud", instance="crn:123" ).to_saved_format(), - management._DEFAULT_ACCOUNT_NAME_LEGACY: Account( - "legacy", "token-legacy" + management._DEFAULT_ACCOUNT_NAME_IBM_QUANTUM: Account( + Channel.IBM_QUANTUM, "token-ibm-quantum" ).to_saved_format(), } ), self.subTest("filtered list of accounts"): - accounts = list(AccountManager.list(auth="cloud").keys()) + accounts = list(AccountManager.list(channel=Channel.IBM_CLOUD).keys()) self.assertEqual(len(accounts), 2) self.assertListEqual( - accounts, ["key1", management._DEFAULT_ACCOUNT_NAME_CLOUD] + accounts, ["key1", management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD] ) - accounts = list(AccountManager.list(auth="legacy").keys()) + accounts = list(AccountManager.list(channel=Channel.IBM_QUANTUM).keys()) self.assertEqual(len(accounts), 2) self.assertListEqual( - accounts, ["key2", management._DEFAULT_ACCOUNT_NAME_LEGACY] + accounts, ["key2", management._DEFAULT_ACCOUNT_NAME_IBM_QUANTUM] ) - accounts = list(AccountManager.list(auth="cloud", default=True).keys()) + accounts = list( + AccountManager.list(channel=Channel.IBM_CLOUD, default=True).keys() + ) self.assertEqual(len(accounts), 1) - self.assertListEqual(accounts, [management._DEFAULT_ACCOUNT_NAME_CLOUD]) + self.assertListEqual(accounts, [management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD]) - accounts = list(AccountManager.list(auth="cloud", default=False).keys()) + accounts = list( + AccountManager.list(channel=Channel.IBM_CLOUD, default=False).keys() + ) self.assertEqual(len(accounts), 1) self.assertListEqual(accounts, ["key1"]) @@ -278,9 +291,9 @@ def test_list(self): @temporary_account_config_file( contents={ - "key1": _TEST_CLOUD_ACCOUNT.to_saved_format(), - management._DEFAULT_ACCOUNT_NAME_LEGACY: _TEST_LEGACY_ACCOUNT.to_saved_format(), - management._DEFAULT_ACCOUNT_NAME_CLOUD: _TEST_CLOUD_ACCOUNT.to_saved_format(), + "key1": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format(), + management._DEFAULT_ACCOUNT_NAME_IBM_QUANTUM: _TEST_IBM_QUANTUM_ACCOUNT.to_saved_format(), + management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD: _TEST_IBM_CLOUD_ACCOUNT.to_saved_format(), } ) def test_delete(self): @@ -290,10 +303,10 @@ def test_delete(self): self.assertTrue(AccountManager.delete(name="key1")) self.assertFalse(AccountManager.delete(name="key1")) - with self.subTest("delete default legacy account"): - self.assertTrue(AccountManager.delete(auth="legacy")) + with self.subTest("delete default ibm_quantum account"): + self.assertTrue(AccountManager.delete(channel=Channel.IBM_QUANTUM)) - with self.subTest("delete default cloud account"): + with self.subTest("delete default ibm_cloud account"): self.assertTrue(AccountManager.delete()) self.assertTrue(len(AccountManager.list()) == 0) @@ -317,13 +330,13 @@ def test_enable_account_by_name(self): self.assertTrue(service._account) self.assertEqual(service._account.token, token) - def test_enable_account_by_auth(self): - """Test initializing account by auth.""" - for auth in ["cloud", "legacy"]: - with self.subTest(auth=auth), no_envs(["QISKIT_IBM_TOKEN"]): + def test_enable_account_by_channel(self): + """Test initializing account by channel.""" + for channel in [channel.value for channel in Channel]: + with self.subTest(channel=channel), no_envs(["QISKIT_IBM_TOKEN"]): token = uuid.uuid4().hex - with temporary_account_config_file(auth=auth, token=token): - service = FakeRuntimeService(auth=auth) + with temporary_account_config_file(channel=channel, token=token): + service = FakeRuntimeService(channel=channel) self.assertTrue(service._account) self.assertEqual(service._account.token, token) @@ -343,10 +356,10 @@ def test_enable_account_by_token_url(self): def test_enable_account_by_name_and_other(self): """Test initializing account by name and other.""" subtests = [ - {"auth": "cloud"}, + {"channel": Channel.IBM_CLOUD}, {"token": "some_token"}, {"url": "some_url"}, - {"auth": "cloud", "token": "some_token", "url": "some_url"}, + {"channel": Channel.IBM_CLOUD, "token": "some_token", "url": "some_url"}, ] name = "foo" @@ -362,70 +375,84 @@ def test_enable_account_by_name_and_other(self): self.assertEqual(service._account.token, token) self.assertIn("are ignored", logged.output[0]) - def test_enable_cloud_account_by_auth_token_url(self): - """Test initializing cloud account by auth, token, url.""" + def test_enable_cloud_account_by_channel_token_url(self): + """Test initializing cloud account by channel, token, url.""" # Enable account will fail due to missing CRN. urls = [None, "some_url"] for url in urls: with self.subTest(url=url), no_envs(["QISKIT_IBM_TOKEN"]): token = uuid.uuid4().hex with self.assertRaises(InvalidAccountError) as err: - _ = FakeRuntimeService(auth="cloud", token=token, url=url) + _ = FakeRuntimeService( + channel=Channel.IBM_CLOUD, token=token, url=url + ) self.assertIn("instance", str(err.exception)) - def test_enable_legacy_account_by_auth_token_url(self): - """Test initializing legacy account by auth, token, url.""" - urls = [(None, LEGACY_API_URL), ("some_url", "some_url")] + def test_enable_ibm_quantum_account_by_channel_token_url(self): + """Test initializing ibm_quantum account by channel, token, url.""" + urls = [(None, IBM_QUANTUM_API_URL), ("some_url", "some_url")] for url, expected in urls: with self.subTest(url=url), no_envs(["QISKIT_IBM_TOKEN"]): token = uuid.uuid4().hex - service = FakeRuntimeService(auth="legacy", token=token, url=url) + service = FakeRuntimeService( + channel=Channel.IBM_QUANTUM, token=token, url=url + ) self.assertTrue(service._account) self.assertEqual(service._account.token, token) self.assertEqual(service._account.url, expected) - def test_enable_account_by_auth_url(self): - """Test initializing legacy account by auth, token, url.""" - subtests = ["legacy", "cloud"] - for auth in subtests: - with self.subTest(auth=auth): + def test_enable_account_by_channel_url(self): + """Test initializing ibm_quantum account by channel, token, url.""" + subtests = [channel.value for channel in Channel] + for channel in subtests: + with self.subTest(channel=channel): token = uuid.uuid4().hex - with temporary_account_config_file(auth=auth, token=token), no_envs( - ["QISKIT_IBM_TOKEN"] - ): + with temporary_account_config_file( + channel=channel, token=token + ), no_envs(["QISKIT_IBM_TOKEN"]): with self.assertLogs( "qiskit_ibm_runtime", logging.WARNING ) as logged: - service = FakeRuntimeService(auth=auth, url="some_url") + service = FakeRuntimeService(channel=channel, url="some_url") self.assertTrue(service._account) self.assertEqual(service._account.token, token) - expected = CLOUD_API_URL if auth == "cloud" else LEGACY_API_URL + expected = ( + IBM_CLOUD_API_URL + if channel == Channel.IBM_CLOUD + else IBM_QUANTUM_API_URL + ) self.assertEqual(service._account.url, expected) self.assertIn("url", logged.output[0]) def test_enable_account_by_only_auth(self): """Test initializing account with single saved account.""" - subtests = ["legacy", "cloud"] - for auth in subtests: - with self.subTest(auth=auth): + subtests = [channel.value for channel in Channel] + for channel in subtests: + with self.subTest(channel=channel): token = uuid.uuid4().hex - with temporary_account_config_file(auth=auth, token=token), no_envs( - ["QISKIT_IBM_TOKEN"] - ): + with temporary_account_config_file( + channel=channel, token=token + ), no_envs(["QISKIT_IBM_TOKEN"]): service = FakeRuntimeService() self.assertTrue(service._account) self.assertEqual(service._account.token, token) - expected = CLOUD_API_URL if auth == "cloud" else LEGACY_API_URL + expected = ( + IBM_CLOUD_API_URL + if channel == Channel.IBM_CLOUD + else IBM_QUANTUM_API_URL + ) self.assertEqual(service._account.url, expected) - self.assertEqual(service._account.auth, auth) + self.assertEqual(service._account.channel, channel) def test_enable_account_both_auth(self): """Test initializing account with both saved types.""" token = uuid.uuid4().hex - contents = get_account_config_contents(auth="cloud", token=token) + contents = get_account_config_contents(channel=Channel.IBM_CLOUD, token=token) contents.update( - get_account_config_contents(auth="legacy", token=uuid.uuid4().hex) + get_account_config_contents( + channel=Channel.IBM_QUANTUM, token=uuid.uuid4().hex + ) ) with temporary_account_config_file(contents=contents), no_envs( ["QISKIT_IBM_TOKEN"] @@ -433,29 +460,31 @@ def test_enable_account_both_auth(self): service = FakeRuntimeService() self.assertTrue(service._account) self.assertEqual(service._account.token, token) - self.assertEqual(service._account.url, CLOUD_API_URL) - self.assertEqual(service._account.auth, "cloud") - - def test_enable_account_by_env_auth(self): - """Test initializing account by environment variable and auth.""" - subtests = ["legacy", "cloud", None] - for auth in subtests: - with self.subTest(auth=auth): + self.assertEqual(service._account.url, IBM_CLOUD_API_URL) + self.assertEqual(service._account.channel, Channel.IBM_CLOUD) + + def test_enable_account_by_env_channel(self): + """Test initializing account by environment variable and channel.""" + subtests = [Channel.IBM_QUANTUM, Channel.IBM_CLOUD, None] + for channel in subtests: + with self.subTest(channel=channel): token = uuid.uuid4().hex url = uuid.uuid4().hex envs = { "QISKIT_IBM_TOKEN": token, "QISKIT_IBM_URL": url, - "QISKIT_IBM_INSTANCE": "h/g/p" if auth == "legacy" else "crn:123", + "QISKIT_IBM_INSTANCE": "h/g/p" + if channel == Channel.IBM_QUANTUM + else "crn:123", } with custom_envs(envs): - service = FakeRuntimeService(auth=auth) + service = FakeRuntimeService(channel=channel) self.assertTrue(service._account) self.assertEqual(service._account.token, token) self.assertEqual(service._account.url, url) - auth = auth or "cloud" - self.assertEqual(service._account.auth, auth) + channel = channel or Channel.IBM_CLOUD + self.assertEqual(service._account.channel, channel) def test_enable_account_by_env_token_url(self): """Test initializing account by environment variable and extra.""" @@ -482,12 +511,12 @@ def test_enable_account_bad_name(self): _ = FakeRuntimeService(name=name) self.assertIn(name, str(err.exception)) - def test_enable_account_bad_auth(self): + def test_enable_account_bad_channel(self): """Test initializing account by bad name.""" - auth = "phantom" + channel = "phantom" with temporary_account_config_file() as _, self.assertRaises(ValueError) as err: - _ = FakeRuntimeService(auth=auth) - self.assertIn("auth", str(err.exception)) + _ = FakeRuntimeService(channel=channel) + self.assertIn("channel", str(err.exception)) def test_enable_account_by_name_pref(self): """Test initializing account by name and preferences.""" @@ -507,24 +536,24 @@ def test_enable_account_by_name_pref(self): self.assertTrue(service._account) self._verify_prefs(extra, service._account) - def test_enable_account_by_auth_pref(self): - """Test initializing account by auth and preferences.""" + def test_enable_account_by_channel_pref(self): + """Test initializing account by channel and preferences.""" subtests = [ {"proxies": MOCK_PROXY_CONFIG_DICT}, {"verify": False}, {"instance": "h/g/p"}, {"proxies": MOCK_PROXY_CONFIG_DICT, "verify": False, "instance": "h/g/p"}, ] - for auth in ["cloud", "legacy"]: + for channel in [channel.value for channel in Channel]: for extra in subtests: with self.subTest( - auth=auth, extra=extra + channel=channel, extra=extra ), temporary_account_config_file( - auth=auth, verify=True, proxies="some proxies" + channel=channel, verify=True, proxies="some proxies" ), no_envs( ["QISKIT_IBM_TOKEN"] ): - service = FakeRuntimeService(auth=auth, **extra) + service = FakeRuntimeService(channel=channel, **extra) self.assertTrue(service._account) self._verify_prefs(extra, service._account) @@ -560,11 +589,11 @@ def test_enable_account_by_name_input_instance(self): self.assertTrue(service._account) self.assertEqual(service._account.instance, instance) - def test_enable_account_by_auth_input_instance(self): - """Test initializing account by auth and input instance.""" + def test_enable_account_by_channel_input_instance(self): + """Test initializing account by channel and input instance.""" instance = uuid.uuid4().hex - with temporary_account_config_file(auth="cloud", instance="bla"): - service = FakeRuntimeService(auth="cloud", instance=instance) + with temporary_account_config_file(channel=Channel.IBM_CLOUD, instance="bla"): + service = FakeRuntimeService(channel=Channel.IBM_CLOUD, instance=instance) self.assertTrue(service._account) self.assertEqual(service._account.instance, instance) @@ -577,7 +606,7 @@ def test_enable_account_by_env_input_instance(self): "QISKIT_IBM_INSTANCE": "some_instance", } with custom_envs(envs): - service = FakeRuntimeService(auth="cloud", instance=instance) + service = FakeRuntimeService(channel=Channel.IBM_CLOUD, instance=instance) self.assertTrue(service._account) self.assertEqual(service._account.instance, instance) diff --git a/test/unit/test_account_client.py b/test/unit/test_account_client.py index 0942e62fc..c0e065012 100644 --- a/test/unit/test_account_client.py +++ b/test/unit/test_account_client.py @@ -15,6 +15,7 @@ from qiskit_ibm_runtime.api.client_parameters import ClientParameters from qiskit_ibm_runtime.api.clients import AccountClient from qiskit_ibm_runtime.api.exceptions import RequestsApiError +from qiskit_ibm_runtime.channel import Channel from .mock.http_server import SimpleServer, ClientErrorHandler from ..ibm_test_case import IBMTestCase from ..account import custom_envs, no_envs @@ -38,7 +39,10 @@ def _get_client(self): """Helper for instantiating an AccountClient.""" # pylint: disable=no-value-for-parameter params = ClientParameters( - auth_type="legacy", url=SimpleServer.URL, token="foo", instance="h/g/p" + channel=Channel.IBM_QUANTUM, + url=SimpleServer.URL, + token="foo", + instance="h/g/p", ) return AccountClient(params) diff --git a/test/unit/test_backend_retrieval.py b/test/unit/test_backend_retrieval.py index 628be6a56..4f13993b6 100644 --- a/test/unit/test_backend_retrieval.py +++ b/test/unit/test_backend_retrieval.py @@ -15,23 +15,24 @@ from qiskit.providers.exceptions import QiskitBackendNotFoundError from qiskit.test.mock.backends import FakeLima +from qiskit_ibm_runtime.channel import Channel from .mock.fake_account_client import BaseFakeAccountClient from .mock.fake_runtime_service import FakeRuntimeService from ..ibm_test_case import IBMTestCase -from ..decorators import run_legacy_and_cloud_fake +from ..decorators import run_quantum_and_cloud_fake class TestBackendFilters(IBMTestCase): """Qiskit Backend Filtering Tests.""" - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_no_filter(self, service): """Test no filtering.""" # FakeRuntimeService by default creates 3 backends. backend_name = [back.name for back in service.backends()] self.assertEqual(len(backend_name), 3) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_filter_by_name(self, service): """Test filtering by name.""" for name in [ @@ -42,9 +43,9 @@ def test_filter_by_name(self, service): backend_name = [back.name for back in service.backends(name=name)] self.assertEqual(len(backend_name), 1) - def test_filter_by_instance_legacy(self): - """Test filtering by instance.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + def test_filter_by_instance_ibm_quantum(self): + """Test filtering by instance (works only on ibm_quantum).""" + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") for hgp in FakeRuntimeService.DEFAULT_HGPS: with self.subTest(hgp=hgp): backends = service.backends(instance=hgp) @@ -64,7 +65,7 @@ def test_filter_config_properties(self): services = self._get_services(fake_backends) for service in services: - with self.subTest(service=service.auth): + with self.subTest(service=service.channel): filtered_backends = service.backends(n_qubits=n_qubits, local=False) self.assertTrue(len(filtered_backends), 1) self.assertEqual( @@ -83,7 +84,7 @@ def test_filter_status_dict(self): services = self._get_services(fake_backends) for service in services: - with self.subTest(service=service.auth): + with self.subTest(service=service.channel): filtered_backends = service.backends( operational=True, # from status simulator=True, # from configuration @@ -104,7 +105,7 @@ def test_filter_config_callable(self): services = self._get_services(fake_backends) for service in services: - with self.subTest(service=service.auth): + with self.subTest(service=service.channel): filtered_backends = service.backends( filters=lambda x: (x.configuration().n_qubits >= 5) ) @@ -126,7 +127,7 @@ def test_filter_least_busy(self): services = self._get_services(fake_backends) for service in services: - with self.subTest(service=service.auth): + with self.subTest(service=service.channel): backend = service.least_busy() self.assertEqual(backend.name, "bingo") @@ -141,7 +142,7 @@ def test_filter_min_num_qubits(self): services = self._get_services(fake_backends) for service in services: - with self.subTest(service=service.auth): + with self.subTest(service=service.channel): filtered_backends = service.backends(min_num_qubits=n_qubits) self.assertTrue(len(filtered_backends), 2) for backend in filtered_backends: @@ -154,13 +155,13 @@ def test_filter_by_hgp(self): "account_client": BaseFakeAccountClient(num_backends=num_backends), "num_hgps": 2, } - legacy_service = FakeRuntimeService( - auth="legacy", + ibm_quantum_service = FakeRuntimeService( + channel=Channel.IBM_QUANTUM, token="my_token", instance="h/g/p", test_options=test_options, ) - backends = legacy_service.backends(instance="hub0/group0/project0") + backends = ibm_quantum_service.backends(instance="hub0/group0/project0") self.assertEqual(len(backends), num_backends) def _get_specs(self, **kwargs): @@ -177,49 +178,49 @@ def _get_specs(self, **kwargs): return specs def _get_services(self, fake_backends): - """Get both cloud and legacy services initialized with fake backends.""" + """Get both ibm_cloud and ibm_quantum services initialized with fake backends.""" test_options = {"account_client": BaseFakeAccountClient(specs=fake_backends)} - legacy_service = FakeRuntimeService( - auth="legacy", + ibm_quantum_service = FakeRuntimeService( + channel=Channel.IBM_QUANTUM, token="my_token", instance="h/g/p", test_options=test_options, ) cloud_service = FakeRuntimeService( - auth="cloud", + channel=Channel.IBM_CLOUD, token="my_token", instance="my_instance", test_options=test_options, ) - return [legacy_service, cloud_service] + return [ibm_quantum_service, cloud_service] class TestGetBackend(IBMTestCase): - """Test getting a backend via legacy api.""" + """Test getting a backend via ibm_quantum api.""" def test_get_common_backend(self): """Test getting a backend that is in default and non-default hgp.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") backend = service.backend(FakeRuntimeService.DEFAULT_COMMON_BACKEND) self.assertEqual(backend._api_client.hgp, list(service._hgps.keys())[0]) def test_get_unique_backend_default_hgp(self): """Test getting a backend in the default hgp.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") backend_name = FakeRuntimeService.DEFAULT_UNIQUE_BACKEND_PREFIX + "0" backend = service.backend(backend_name) self.assertEqual(backend._api_client.hgp, list(service._hgps.keys())[0]) def test_get_unique_backend_non_default_hgp(self): """Test getting a backend in the non default hgp.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") backend_name = FakeRuntimeService.DEFAULT_UNIQUE_BACKEND_PREFIX + "1" backend = service.backend(backend_name) self.assertEqual(backend._api_client.hgp, list(service._hgps.keys())[1]) def test_get_phantom_backend(self): """Test getting a phantom backend.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") with self.assertRaises(QiskitBackendNotFoundError): service.backend("phantom") @@ -227,7 +228,7 @@ def test_get_backend_by_hgp(self): """Test getting a backend by hgp.""" hgp = FakeRuntimeService.DEFAULT_HGPS[1] backend_name = FakeRuntimeService.DEFAULT_COMMON_BACKEND - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") backend = service.backend(backend_name, instance=hgp) self.assertEqual(backend._api_client.hgp, hgp) @@ -235,6 +236,6 @@ def test_get_backend_by_bad_hgp(self): """Test getting a backend not in hgp.""" hgp = FakeRuntimeService.DEFAULT_HGPS[1] backend_name = FakeRuntimeService.DEFAULT_UNIQUE_BACKEND_PREFIX + "0" - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") with self.assertRaises(QiskitBackendNotFoundError): _ = service.backend(backend_name, instance=hgp) diff --git a/test/unit/test_client_parameters.py b/test/unit/test_client_parameters.py index 8757a511d..0859f3447 100644 --- a/test/unit/test_client_parameters.py +++ b/test/unit/test_client_parameters.py @@ -15,9 +15,10 @@ import uuid from requests_ntlm import HttpNtlmAuth +from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.proxies import ProxyConfiguration from qiskit_ibm_runtime.api.client_parameters import ClientParameters -from qiskit_ibm_runtime.api.auth import CloudAuth, LegacyAuth +from qiskit_ibm_runtime.api.auth import CloudAuth, QuantumAuth from ..ibm_test_case import IBMTestCase @@ -65,17 +66,17 @@ def test_get_runtime_api_base_url(self) -> None: "https://my-region.quantum-computing.cloud.ibm.com", ), ( - "legacy", + Channel.IBM_QUANTUM, "h/g/p", "https://auth.quantum-computing.ibm.com/api", "https://auth.quantum-computing.ibm.com/api", ), ] for spec in test_specs: - auth, instance, url, expected = spec + channel, instance, url, expected = spec with self.subTest(instance=instance, url=url): params = self._get_client_params( - auth_type=auth, instance=instance, url=url + channel=channel, instance=instance, url=url ) self.assertEqual(params.get_runtime_api_base_url(), expected) @@ -122,12 +123,12 @@ def test_malformed_ntlm_params(self) -> None: with self.assertRaises(AttributeError): _ = malformed_ntlm_credentials.connection_parameters() - def test_auth_handler_legacy(self): - """Test getting legacy auth handler.""" + def test_auth_handler_quantum(self): + """Test getting quantum auth handler.""" token = uuid.uuid4().hex - params = self._get_client_params(auth_type="legacy", token=token) + params = self._get_client_params(channel=Channel.IBM_QUANTUM, token=token) handler = params.get_auth_handler() - self.assertIsInstance(handler, LegacyAuth) + self.assertIsInstance(handler, QuantumAuth) self.assertIn(token, handler.get_headers().values()) def test_auth_handler_cloud(self): @@ -135,7 +136,7 @@ def test_auth_handler_cloud(self): token = uuid.uuid4().hex instance = uuid.uuid4().hex params = self._get_client_params( - auth_type="cloud", token=token, instance=instance + channel=Channel.IBM_CLOUD, token=token, instance=instance ) handler = params.get_auth_handler() self.assertIsInstance(handler, CloudAuth) @@ -144,7 +145,7 @@ def test_auth_handler_cloud(self): def _get_client_params( self, - auth_type="legacy", + channel=Channel.IBM_QUANTUM, token="dummy_token", url="https://dummy_url", instance=None, @@ -155,7 +156,7 @@ def _get_client_params( if verify is None: verify = True return ClientParameters( - auth_type=auth_type, + channel=channel, token=token, url=url, instance=instance, diff --git a/test/unit/test_data_serialization.py b/test/unit/test_data_serialization.py index 2aa1921fd..a0f980e8b 100644 --- a/test/unit/test_data_serialization.py +++ b/test/unit/test_data_serialization.py @@ -61,6 +61,7 @@ from qiskit.quantum_info import SparsePauliOp, Pauli, PauliTable, Statevector from qiskit.result import Result +from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.utils import RuntimeEncoder, RuntimeDecoder from .mock.fake_runtime_client import CustomResultRuntimeJob from .mock.fake_runtime_service import FakeRuntimeService @@ -287,13 +288,17 @@ def test_result_decoder(self): custom_result = get_complex_types() job_cls = CustomResultRuntimeJob job_cls.custom_result = custom_result - legacy_service = FakeRuntimeService(auth="legacy", token="some_token") + ibm_quantum_service = FakeRuntimeService( + channel=Channel.IBM_QUANTUM, token="some_token" + ) sub_tests = [(SerializableClassDecoder, None), (None, SerializableClassDecoder)] for result_decoder, decoder in sub_tests: with self.subTest(decoder=decoder): job = run_program( - service=legacy_service, job_classes=job_cls, decoder=result_decoder + service=ibm_quantum_service, + job_classes=job_cls, + decoder=result_decoder, ) result = job.result(decoder=decoder) self.assertIsInstance(result["serializable_class"], SerializableClass) diff --git a/test/unit/test_job_retrieval.py b/test/unit/test_job_retrieval.py index 937a63bb8..d9e69ebf5 100644 --- a/test/unit/test_job_retrieval.py +++ b/test/unit/test_job_retrieval.py @@ -12,10 +12,11 @@ """Tests for runtime job retrieval.""" +from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.exceptions import IBMInputValueError from .mock.fake_runtime_service import FakeRuntimeService from ..ibm_test_case import IBMTestCase -from ..decorators import run_legacy_and_cloud_fake +from ..decorators import run_quantum_and_cloud_fake from ..program import run_program, upload_program from ..utils import mock_wait_for_final_state @@ -26,9 +27,11 @@ class TestRetrieveJobs(IBMTestCase): def setUp(self): """Initial test setup.""" super().setUp() - self._legacy_service = FakeRuntimeService(auth="legacy", token="my_token") + self._ibm_quantum_service = FakeRuntimeService( + channel=Channel.IBM_QUANTUM, token="my_token" + ) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_retrieve_job(self, service): """Test retrieving a job.""" program_id = upload_program(service) @@ -38,7 +41,7 @@ def test_retrieve_job(self, service): self.assertEqual(job.job_id, rjob.job_id) self.assertEqual(program_id, rjob.program_id) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_jobs_no_limit(self, service): """Test retrieving jobs without limit.""" program_id = upload_program(service) @@ -49,7 +52,7 @@ def test_jobs_no_limit(self, service): rjobs = service.jobs(limit=None) self.assertEqual(25, len(rjobs)) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_jobs_limit(self, service): """Test retrieving jobs with limit.""" program_id = upload_program(service) @@ -65,7 +68,7 @@ def test_jobs_limit(self, service): rjobs = service.jobs(limit=limit) self.assertEqual(min(limit, job_count), len(rjobs)) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_jobs_skip(self, service): """Test retrieving jobs with skip.""" program_id = upload_program(service) @@ -78,7 +81,7 @@ def test_jobs_skip(self, service): def test_jobs_skip_limit(self): """Test retrieving jobs with skip and limit.""" - service = self._legacy_service + service = self._ibm_quantum_service program_id = upload_program(service) jobs = [] @@ -87,7 +90,7 @@ def test_jobs_skip_limit(self): rjobs = service.jobs(skip=4, limit=2) self.assertEqual(2, len(rjobs)) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_jobs_pending(self, service): """Test retrieving pending jobs (QUEUED, RUNNING).""" program_id = upload_program(service) @@ -100,7 +103,7 @@ def test_jobs_pending(self, service): def test_jobs_limit_pending(self): """Test retrieving pending jobs (QUEUED, RUNNING) with limit.""" - service = self._legacy_service + service = self._ibm_quantum_service program_id = upload_program(service) self._populate_jobs_with_all_statuses(service, program_id=program_id) @@ -110,7 +113,7 @@ def test_jobs_limit_pending(self): def test_jobs_skip_pending(self): """Test retrieving pending jobs (QUEUED, RUNNING) with skip.""" - service = self._legacy_service + service = self._ibm_quantum_service program_id = upload_program(service) _, pending_jobs_count, _ = self._populate_jobs_with_all_statuses( @@ -122,7 +125,7 @@ def test_jobs_skip_pending(self): def test_jobs_limit_skip_pending(self): """Test retrieving pending jobs (QUEUED, RUNNING) with limit and skip.""" - service = self._legacy_service + service = self._ibm_quantum_service program_id = upload_program(service) self._populate_jobs_with_all_statuses(service, program_id=program_id) @@ -133,7 +136,7 @@ def test_jobs_limit_skip_pending(self): def test_jobs_returned(self): """Test retrieving returned jobs (COMPLETED, FAILED, CANCELLED).""" - service = self._legacy_service + service = self._ibm_quantum_service program_id = upload_program(service) _, _, returned_jobs_count = self._populate_jobs_with_all_statuses( @@ -144,7 +147,7 @@ def test_jobs_returned(self): def test_jobs_limit_returned(self): """Test retrieving returned jobs (COMPLETED, FAILED, CANCELLED) with limit.""" - service = self._legacy_service + service = self._ibm_quantum_service program_id = upload_program(service) self._populate_jobs_with_all_statuses(service, program_id=program_id) @@ -154,7 +157,7 @@ def test_jobs_limit_returned(self): def test_jobs_skip_returned(self): """Test retrieving returned jobs (COMPLETED, FAILED, CANCELLED) with skip.""" - service = self._legacy_service + service = self._ibm_quantum_service program_id = upload_program(service) _, _, returned_jobs_count = self._populate_jobs_with_all_statuses( @@ -166,7 +169,7 @@ def test_jobs_skip_returned(self): def test_jobs_limit_skip_returned(self): """Test retrieving returned jobs (COMPLETED, FAILED, CANCELLED) with limit and skip.""" - service = self._legacy_service + service = self._ibm_quantum_service program_id = upload_program(service) self._populate_jobs_with_all_statuses(service, program_id=program_id) @@ -175,7 +178,7 @@ def test_jobs_limit_skip_returned(self): rjobs = service.jobs(limit=limit, skip=skip, pending=False) self.assertEqual(limit, len(rjobs)) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_jobs_filter_by_program_id(self, service): """Test retrieving jobs by Program ID.""" program_id = upload_program(service) @@ -192,7 +195,7 @@ def test_jobs_filter_by_program_id(self, service): def test_jobs_filter_by_instance(self): """Test retrieving jobs by instance.""" - service = self._legacy_service + service = self._ibm_quantum_service program_id = upload_program(service) instance = FakeRuntimeService.DEFAULT_HGPS[1] @@ -210,7 +213,7 @@ def test_jobs_filter_by_instance(self): def test_jobs_bad_instance(self): """Test retrieving jobs with bad instance values.""" - service = self._legacy_service + service = self._ibm_quantum_service with self.assertRaises(IBMInputValueError): _ = service.jobs(instance="foo") @@ -218,7 +221,7 @@ def test_different_hgps(self): """Test retrieving job submitted with different hgp.""" # Initialize with hgp0 service = FakeRuntimeService( - auth="legacy", + channel=Channel.IBM_QUANTUM, token="some_token", instance=FakeRuntimeService.DEFAULT_HGPS[0], ) diff --git a/test/unit/test_jobs.py b/test/unit/test_jobs.py index aeb3002bc..9345791e5 100644 --- a/test/unit/test_jobs.py +++ b/test/unit/test_jobs.py @@ -19,6 +19,7 @@ from qiskit.providers.jobstatus import JobStatus from qiskit_ibm_runtime import RuntimeJob +from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.constants import API_TO_JOB_ERROR_MESSAGE from qiskit_ibm_runtime.exceptions import ( RuntimeJobFailureError, @@ -34,7 +35,7 @@ ) from .mock.fake_runtime_service import FakeRuntimeService from ..ibm_test_case import IBMTestCase -from ..decorators import run_legacy_and_cloud_fake +from ..decorators import run_quantum_and_cloud_fake from ..program import run_program, upload_program from ..serialization import get_complex_types from ..utils import mock_wait_for_final_state @@ -43,7 +44,7 @@ class TestRuntimeJob(IBMTestCase): """Class for testing runtime jobs.""" - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_run_program(self, service): """Test running program.""" params = {"param1": "foo"} @@ -57,28 +58,28 @@ def test_run_program(self, service): self.assertEqual(job.status(), JobStatus.DONE) self.assertTrue(job.result()) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_run_phantom_program(self, service): """Test running a phantom program.""" with self.assertRaises(RuntimeProgramNotFound): _ = run_program(service=service, program_id="phantom_program") - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_run_program_phantom_backend(self, service): """Test running on a phantom backend.""" with self.assertRaises(QiskitBackendNotFoundError): _ = run_program(service=service, backend_name="phantom_backend") - def test_run_program_missing_backend_legacy(self): - """Test running a legacy program with no backend.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + def test_run_program_missing_backend_ibm_quantum(self): + """Test running an ibm_quantum program with no backend.""" + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") with self.assertRaises(IBMInputValueError): _ = run_program(service=service, backend_name="") - def test_run_program_missing_backend_cloud(self): - """Test running a cloud program with no backend.""" + def test_run_program_missing_backend_ibm_cloud(self): + """Test running an ibm_cloud program with no backend.""" service = FakeRuntimeService( - auth="cloud", + channel=Channel.IBM_CLOUD, token="my_token", instance="crn:v1:bluemix:public:quantum-computing:my-region:a/...:...::", ) @@ -87,7 +88,7 @@ def test_run_program_missing_backend_cloud(self): def test_run_program_default_hgp_backend(self): """Test running a program with a backend in default hgp.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") backend = FakeRuntimeService.DEFAULT_COMMON_BACKEND default_hgp = list(service._hgps.values())[0] self.assertIn(backend, default_hgp.backends.keys()) @@ -99,7 +100,7 @@ def test_run_program_default_hgp_backend(self): def test_run_program_non_default_hgp_backend(self): """Test running a program with a backend in non-default hgp.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") backend = FakeRuntimeService.DEFAULT_UNIQUE_BACKEND_PREFIX + "1" default_hgp = list(service._hgps.values())[0] self.assertNotIn(backend, default_hgp.backends.keys()) @@ -108,7 +109,7 @@ def test_run_program_non_default_hgp_backend(self): def test_run_program_by_hgp_backend(self): """Test running a program with both backend and hgp.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") backend = FakeRuntimeService.DEFAULT_COMMON_BACKEND non_default_hgp = list(service._hgps.keys())[1] job = run_program( @@ -119,7 +120,7 @@ def test_run_program_by_hgp_backend(self): def test_run_program_by_hgp_bad_backend(self): """Test running a program with backend not in hgp.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") backend = FakeRuntimeService.DEFAULT_UNIQUE_BACKEND_PREFIX + "1" default_hgp = list(service._hgps.values())[0] self.assertNotIn(backend, default_hgp.backends.keys()) @@ -130,17 +131,17 @@ def test_run_program_by_hgp_bad_backend(self): def test_run_program_by_phantom_hgp(self): """Test running a program with a phantom hgp.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") with self.assertRaises(IBMInputValueError): _ = run_program(service=service, instance="h/g/p") def test_run_program_by_bad_hgp(self): """Test running a program with a bad hgp.""" - service = FakeRuntimeService(auth="legacy", token="my_token") + service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") with self.assertRaises(IBMInputValueError): _ = run_program(service=service, instance="foo") - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_run_program_with_custom_runtime_image(self, service): """Test running program with a custom image.""" params = {"param1": "foo"} @@ -156,14 +157,14 @@ def test_run_program_with_custom_runtime_image(self, service): self.assertEqual(job.status(), JobStatus.DONE) self.assertEqual(job.image, image) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_run_program_with_custom_log_level(self, service): """Test running program with a custom image.""" job = run_program(service=service, log_level="DEBUG") job_raw = service._api_client._get_job(job.job_id) self.assertEqual(job_raw.log_level, "DEBUG") - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_run_program_failed(self, service): """Test a failed program execution.""" job = run_program(service=service, job_classes=FailedRuntimeJob) @@ -178,7 +179,7 @@ def test_run_program_failed(self, service): with self.assertRaises(RuntimeJobFailureError): job.result() - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_run_program_failed_ran_too_long(self, service): """Test a program that failed since it ran longer than maximum execution time.""" job = run_program(service=service, job_classes=FailedRanTooLongRuntimeJob) @@ -195,7 +196,7 @@ def test_run_program_failed_ran_too_long(self, service): with self.assertRaises(RuntimeJobFailureError): job.result() - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_program_params_namespace(self, service): """Test running a program using parameter namespace.""" program_id = upload_program(service) @@ -203,7 +204,7 @@ def test_program_params_namespace(self, service): params.param1 = "Hello World" run_program(service, program_id, inputs=params) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_cancel_job(self, service): """Test canceling a job.""" job = run_program(service, job_classes=CancelableRuntimeJob) @@ -213,7 +214,7 @@ def test_cancel_job(self, service): rjob = service.job(job.job_id) self.assertEqual(rjob.status(), JobStatus.CANCELLED) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_final_result(self, service): """Test getting final result.""" job = run_program(service) @@ -221,7 +222,7 @@ def test_final_result(self, service): result = job.result() self.assertTrue(result) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_interim_results(self, service): """Test getting interim results.""" job = run_program(service) @@ -229,28 +230,28 @@ def test_interim_results(self, service): interim_results = job.interim_results() self.assertTrue(interim_results) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_job_status(self, service): """Test job status.""" job = run_program(service) time.sleep(random.randint(1, 5)) self.assertTrue(job.status()) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_job_inputs(self, service): """Test job inputs.""" inputs = {"param1": "foo", "param2": "bar"} job = run_program(service, inputs=inputs) self.assertEqual(inputs, job.inputs) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_job_program_id(self, service): """Test job program ID.""" program_id = upload_program(service) job = run_program(service, program_id=program_id) self.assertEqual(program_id, job.program_id) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_wait_for_final_state(self, service): """Test wait for final state.""" job = run_program(service) @@ -258,7 +259,7 @@ def test_wait_for_final_state(self, service): job.wait_for_final_state() self.assertEqual(JobStatus.DONE, job.status()) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_get_result_twice(self, service): """Test getting results multiple times.""" custom_result = get_complex_types() @@ -270,7 +271,7 @@ def test_get_result_twice(self, service): _ = job.result() _ = job.result() - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_delete_job(self, service): """Test deleting a job.""" params = {"param1": "foo"} diff --git a/test/unit/test_programs.py b/test/unit/test_programs.py index 16e041bef..77c3f56b2 100644 --- a/test/unit/test_programs.py +++ b/test/unit/test_programs.py @@ -24,14 +24,14 @@ from qiskit_ibm_runtime.exceptions import RuntimeProgramNotFound from qiskit_ibm_runtime.runtime_program import ParameterNamespace from ..ibm_test_case import IBMTestCase -from ..decorators import run_legacy_and_cloud_fake +from ..decorators import run_quantum_and_cloud_fake from ..program import upload_program, DEFAULT_DATA, DEFAULT_METADATA class TestPrograms(IBMTestCase): """Class for testing runtime modules.""" - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_list_programs(self, service): """Test listing programs.""" program_id = upload_program(service) @@ -39,7 +39,7 @@ def test_list_programs(self, service): all_ids = [prog.program_id for prog in programs] self.assertIn(program_id, all_ids) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_list_programs_with_limit_skip(self, service): """Test listing programs with limit and skip.""" program_ids = [] @@ -54,14 +54,14 @@ def test_list_programs_with_limit_skip(self, service): all_ids = [prog.program_id for prog in programs] self.assertIn(program_ids[0], all_ids) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_list_program(self, service): """Test listing a single program.""" program_id = upload_program(service) program = service.program(program_id) self.assertEqual(program_id, program.program_id) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_print_programs(self, service): """Test printing programs.""" ids = [] @@ -85,7 +85,7 @@ def test_print_programs(self, service): self.assertIn(str(prog.max_execution_time), stdout_detailed) self.assertIn("Backend requirements", stdout_detailed) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_upload_program(self, service): """Test uploading a program.""" max_execution_time = 3000 @@ -99,7 +99,7 @@ def test_upload_program(self, service): self.assertEqual(max_execution_time, program.max_execution_time) self.assertEqual(program.is_public, is_public) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_update_program(self, service): """Test updating program.""" new_data = "def main() {foo=bar}" @@ -144,7 +144,7 @@ def test_update_program(self, service): raw_program = service._api_client.program_get(program_id) self.assertEqual(new_spec, raw_program["spec"]) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_update_program_no_new_fields(self, service): """Test updating a program without any new data.""" program_id = upload_program(service) @@ -152,13 +152,13 @@ def test_update_program_no_new_fields(self, service): service.update_program(program_id=program_id) self.assertEqual(len(warn_cm), 1) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_update_phantom_program(self, service): """Test updating a phantom program.""" with self.assertRaises(RuntimeProgramNotFound): service.update_program("phantom_program", name="foo") - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_delete_program(self, service): """Test deleting program.""" program_id = upload_program(service) @@ -166,7 +166,7 @@ def test_delete_program(self, service): with self.assertRaises(RuntimeProgramNotFound): service.program(program_id, refresh=True) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_double_delete_program(self, service): """Test deleting a deleted program.""" program_id = upload_program(service) @@ -174,7 +174,7 @@ def test_double_delete_program(self, service): with self.assertRaises(RuntimeProgramNotFound): service.delete_program(program_id) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_retrieve_program_data(self, service): """Test retrieving program data""" program_id = upload_program(service, name="qiskit-test") @@ -183,7 +183,7 @@ def test_retrieve_program_data(self, service): self.assertEqual(program.data, DEFAULT_DATA) self._validate_program(program) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_program_params_validation(self, service): """Test program parameters validation process""" program_id = upload_program(service) @@ -201,7 +201,7 @@ def test_program_params_validation(self, service): params.validate() params.param1 = "foo" - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_program_metadata(self, service): """Test program metadata.""" temp_fp = tempfile.NamedTemporaryFile(mode="w+", delete=False) @@ -221,7 +221,7 @@ def test_program_metadata(self, service): finally: os.remove(temp_fp.name) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_set_program_visibility(self, service): """Test setting program visibility.""" program_id = upload_program(service, is_public=False) @@ -229,7 +229,7 @@ def test_set_program_visibility(self, service): program = service.program(program_id) self.assertTrue(program.is_public) - @run_legacy_and_cloud_fake + @run_quantum_and_cloud_fake def test_set_program_visibility_phantom_program(self, service): """Test setting program visibility for a phantom program.""" with self.assertRaises(RuntimeProgramNotFound): diff --git a/test/unit/test_runtime_ws.py b/test/unit/test_runtime_ws.py index 44be86a86..fcdfc2b4e 100644 --- a/test/unit/test_runtime_ws.py +++ b/test/unit/test_runtime_ws.py @@ -18,6 +18,7 @@ from qiskit_ibm_runtime import RuntimeJob from qiskit_ibm_runtime.api.client_parameters import ClientParameters +from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.exceptions import RuntimeInvalidStateError from .mock.fake_runtime_client import BaseFakeRuntimeClient from .mock.ws_handler import ( @@ -187,7 +188,7 @@ def result_callback(job_id, interim_result): def _get_job(self, callback=None, job_id=JOB_ID_PROGRESS_DONE): """Get a runtime job.""" params = ClientParameters( - auth_type="legacy", token="my_token", url=MockWsServer.VALID_WS_URL + channel=Channel.IBM_QUANTUM, token="my_token", url=MockWsServer.VALID_WS_URL ) job = RuntimeJob( backend=FakeQasmSimulator(), diff --git a/test/utils.py b/test/utils.py index 9564aa513..0a045da46 100644 --- a/test/utils.py +++ b/test/utils.py @@ -26,6 +26,7 @@ from qiskit_ibm_runtime.ibm_backend import IBMBackend from qiskit_ibm_runtime.runtime_job import RuntimeJob from qiskit_ibm_runtime.exceptions import RuntimeInvalidStateError +from qiskit_ibm_runtime.channel import Channel def setup_test_logging(logger: logging.Logger, filename: str) -> None: @@ -87,7 +88,7 @@ def get_hgp(qe_token: str, qe_url: str, default: bool = True) -> HubGroupProject A HubGroupProject, as specified by `default`. """ service = IBMRuntimeService( - auth="legacy", token=qe_token, url=qe_url + channel=Channel.IBM_QUANTUM, token=qe_token, url=qe_url ) # Default hub/group/project. open_hgp = service._get_hgp() # Open access hgp hgp_to_return = open_hgp From 7c8380cb66e4def6f8720aad0ed372e0b0f44eaf Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Mon, 21 Mar 2022 01:14:00 -0400 Subject: [PATCH 02/28] Fix unit tests --- qiskit_ibm_runtime/accounts/management.py | 6 +++--- qiskit_ibm_runtime/ibm_runtime_service.py | 9 +++++++-- test/unit/test_account.py | 6 +++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index ca8788b04..314dc8543 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -108,7 +108,7 @@ def _matching_default(account_name: str) -> bool: filtered_accounts = dict( list( filter( - lambda kv: (_matching_auth(kv[1]) or _matching_channel(kv[1])) + lambda kv: _matching_channel(kv[1]) and _matching_default(kv[0]) and _matching_name(kv[0]), all_accounts, @@ -159,8 +159,8 @@ def get( return Account.from_saved_format(saved_account) all_config = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE) - for account_type in _ACCOUNT_TYPES: - account_name = cls._get_default_account_name(account_type) + for channel_type in _CHANNEL_TYPES: + account_name = cls._get_default_account_name(channel_type) if account_name in all_config: return Account.from_saved_format(all_config[account_name]) diff --git a/qiskit_ibm_runtime/ibm_runtime_service.py b/qiskit_ibm_runtime/ibm_runtime_service.py index 7a1caf098..22da48470 100644 --- a/qiskit_ibm_runtime/ibm_runtime_service.py +++ b/qiskit_ibm_runtime/ibm_runtime_service.py @@ -241,9 +241,14 @@ def _discover_account( name, ) account = AccountManager.get(name=name) - elif auth: - if auth not in ["legacy", "cloud"]: + elif auth or channel: + if auth and auth not in ["legacy", "cloud"]: raise ValueError("'auth' can only be 'cloud' or 'legacy'") + if channel and channel not in [channel.value for channel in Channel]: + raise ValueError( + f"'channel' can only be '{Channel.IBM_CLOUD}' " + f"or '{Channel.IBM_QUANTUM}'" + ) if auth == "cloud": channel = Channel.IBM_CLOUD elif auth == "legacy": diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 21e87edcb..a7fb630a4 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -425,7 +425,7 @@ def test_enable_account_by_channel_url(self): self.assertEqual(service._account.url, expected) self.assertIn("url", logged.output[0]) - def test_enable_account_by_only_auth(self): + def test_enable_account_by_only_channel(self): """Test initializing account with single saved account.""" subtests = [channel.value for channel in Channel] for channel in subtests: @@ -445,7 +445,7 @@ def test_enable_account_by_only_auth(self): self.assertEqual(service._account.url, expected) self.assertEqual(service._account.channel, channel) - def test_enable_account_both_auth(self): + def test_enable_account_both_channel(self): """Test initializing account with both saved types.""" token = uuid.uuid4().hex contents = get_account_config_contents(channel=Channel.IBM_CLOUD, token=token) @@ -475,7 +475,7 @@ def test_enable_account_by_env_channel(self): "QISKIT_IBM_URL": url, "QISKIT_IBM_INSTANCE": "h/g/p" if channel == Channel.IBM_QUANTUM - else "crn:123", + else "crn:12", } with custom_envs(envs): service = FakeRuntimeService(channel=channel) From fadb457d3e0769d032fc3a0b6b86c34b45ded429 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Mon, 21 Mar 2022 23:42:24 -0400 Subject: [PATCH 03/28] Add mising space --- qiskit_ibm_runtime/ibm_runtime_service.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qiskit_ibm_runtime/ibm_runtime_service.py b/qiskit_ibm_runtime/ibm_runtime_service.py index 22da48470..d016af355 100644 --- a/qiskit_ibm_runtime/ibm_runtime_service.py +++ b/qiskit_ibm_runtime/ibm_runtime_service.py @@ -165,7 +165,7 @@ def __init__( if auth: warnings.warn( f"Use of `auth` parameter is deprecated and will " - f"be removed in a future release." + f"be removed in a future release. " f"You can now use channel='{Channel.IBM_CLOUD}' or " f"channel='{Channel.IBM_QUANTUM}' instead.", DeprecationWarning, @@ -572,7 +572,7 @@ def delete_account( if auth: warnings.warn( f"Use of `auth` parameter is deprecated and will " - f"be removed in a future release." + f"be removed in a future release. " f"You can now use channel='{Channel.IBM_CLOUD}' or " f"channel='{Channel.IBM_QUANTUM}' instead.", DeprecationWarning, @@ -614,7 +614,7 @@ def save_account( if auth: warnings.warn( f"Use of `auth` parameter is deprecated and will " - f"be removed in a future release." + f"be removed in a future release. " f"You can now use channel='{Channel.IBM_CLOUD}' " f"or channel={Channel.IBM_QUANTUM} instead.", DeprecationWarning, @@ -661,7 +661,7 @@ def saved_accounts( if auth: warnings.warn( f"Use of `auth` parameter is deprecated and will " - f"be removed in a future release." + f"be removed in a future release. " f"You can now use channel='{Channel.IBM_CLOUD}' " f"or channel={Channel.IBM_QUANTUM} instead.", DeprecationWarning, @@ -1405,7 +1405,7 @@ def auth(self) -> str: """ warnings.warn( f"Use of `auth` parameter is deprecated and will " - f"be removed in a future release." + f"be removed in a future release. " f"You can now use channel='{Channel.IBM_CLOUD}' " f"or channel={Channel.IBM_QUANTUM} instead.", DeprecationWarning, From b6d1f807eec7dc3f293a96ff8cd80effff0a1e39 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Tue, 22 Mar 2022 00:05:06 -0400 Subject: [PATCH 04/28] Save account with new channel parameter even when auth is passed --- qiskit_ibm_runtime/accounts/management.py | 14 ++++++++++++-- qiskit_ibm_runtime/accounts/storage.py | 8 ++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index 314dc8543..a3dfe1b5a 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -50,10 +50,12 @@ def save( overwrite: Optional[bool] = False, ) -> None: """Save account on disk.""" - config_key = name or cls._get_default_account_name(channel) + name = name or cls._get_default_account_name(channel) + old_name = cls._get_old_default_account_name(channel) return save_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, - name=config_key, + name=name, + old_name=old_name, overwrite=overwrite, config=Account( token=token, @@ -201,3 +203,11 @@ def _get_default_account_name(cls, channel: ChannelType) -> str: if channel == Channel.IBM_QUANTUM else _DEFAULT_ACCOUNT_NAME_IBM_CLOUD ) + + @classmethod + def _get_old_default_account_name(cls, channel: ChannelType) -> str: + return ( + _DEFAULT_ACCOUNT_NAME_LEGACY + if channel == Channel.IBM_QUANTUM + else _DEFAULT_ACCOUNT_NAME_CLOUD + ) diff --git a/qiskit_ibm_runtime/accounts/storage.py b/qiskit_ibm_runtime/accounts/storage.py index a3948b61f..a23003669 100644 --- a/qiskit_ibm_runtime/accounts/storage.py +++ b/qiskit_ibm_runtime/accounts/storage.py @@ -21,7 +21,9 @@ logger = logging.getLogger(__name__) -def save_config(filename: str, name: str, config: dict, overwrite: bool) -> None: +def save_config( + filename: str, name: str, old_name: str, config: dict, overwrite: bool +) -> 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) @@ -29,7 +31,7 @@ def save_config(filename: str, name: str, config: dict, overwrite: bool) -> None with open(filename, mode="r", encoding="utf-8") as json_in: data = json.load(json_in) - if data.get(name) and not overwrite: + if (data.get(name) or data.get(old_name)) and not overwrite: raise AccountAlreadyExistsError( f"Named account ({name}) already exists. " f"Set overwrite=True to overwrite." @@ -37,6 +39,8 @@ def save_config(filename: str, name: str, config: dict, overwrite: bool) -> None with open(filename, mode="w", encoding="utf-8") as json_out: data[name] = config + if old_name in data: + del data[old_name] json.dump(data, json_out, sort_keys=True, indent=4) From c503154a09fbf9c99c9480c8fadf9551c079a056 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Wed, 23 Mar 2022 14:03:39 -0400 Subject: [PATCH 05/28] Fix delete_account for auth parameter --- qiskit_ibm_runtime/accounts/management.py | 6 +++--- qiskit_ibm_runtime/accounts/storage.py | 8 ++++++-- qiskit_ibm_runtime/ibm_runtime_service.py | 12 ++++++++---- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index a3dfe1b5a..f649a88ca 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -172,14 +172,14 @@ def get( def delete( cls, name: Optional[str] = None, - auth: Optional[str] = None, # pylint: disable=unused-argument channel: Optional[ChannelType] = None, ) -> bool: """Delete account from disk.""" - config_key = name or cls._get_default_account_name(channel) + name = name or cls._get_default_account_name(channel) + old_name = cls._get_old_default_account_name(channel) return delete_config( - name=config_key, filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE + filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=name, old_name=old_name, ) @classmethod diff --git a/qiskit_ibm_runtime/accounts/storage.py b/qiskit_ibm_runtime/accounts/storage.py index a23003669..c27ce9e29 100644 --- a/qiskit_ibm_runtime/accounts/storage.py +++ b/qiskit_ibm_runtime/accounts/storage.py @@ -65,6 +65,7 @@ def read_config( def delete_config( filename: str, name: str, + old_name: str, ) -> bool: """Delete configuration data from a JSON file.""" @@ -74,9 +75,12 @@ def delete_config( with open(filename, mode="r", encoding="utf-8") as json_in: data = json.load(json_in) - if name in data: + if name in data or old_name in data: with open(filename, mode="w", encoding="utf-8") as json_out: - del data[name] + if name in data: + del data[name] + if old_name in data: + del data[old_name] json.dump(data, json_out, sort_keys=True, indent=4) return True diff --git a/qiskit_ibm_runtime/ibm_runtime_service.py b/qiskit_ibm_runtime/ibm_runtime_service.py index d016af355..d3d0dd79e 100644 --- a/qiskit_ibm_runtime/ibm_runtime_service.py +++ b/qiskit_ibm_runtime/ibm_runtime_service.py @@ -578,7 +578,11 @@ def delete_account( DeprecationWarning, stacklevel=2, ) - return AccountManager.delete(name=name, auth=auth, channel=channel) + if auth == "cloud": + channel = Channel.IBM_CLOUD + elif auth == "legacy": + channel = Channel.IBM_QUANTUM + return AccountManager.delete(name=name, channel=channel) @staticmethod def save_account( @@ -616,7 +620,7 @@ def save_account( f"Use of `auth` parameter is deprecated and will " f"be removed in a future release. " f"You can now use channel='{Channel.IBM_CLOUD}' " - f"or channel={Channel.IBM_QUANTUM} instead.", + f"or channel='{Channel.IBM_QUANTUM}' instead.", DeprecationWarning, stacklevel=2, ) @@ -663,7 +667,7 @@ def saved_accounts( f"Use of `auth` parameter is deprecated and will " f"be removed in a future release. " f"You can now use channel='{Channel.IBM_CLOUD}' " - f"or channel={Channel.IBM_QUANTUM} instead.", + f"or channel='{Channel.IBM_QUANTUM}' instead.", DeprecationWarning, stacklevel=2, ) @@ -1407,7 +1411,7 @@ def auth(self) -> str: f"Use of `auth` parameter is deprecated and will " f"be removed in a future release. " f"You can now use channel='{Channel.IBM_CLOUD}' " - f"or channel={Channel.IBM_QUANTUM} instead.", + f"or channel='{Channel.IBM_QUANTUM}' instead.", DeprecationWarning, stacklevel=2, ) From 0ada0c99a392383feaff74c9ffe6e62365990fd3 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Wed, 23 Mar 2022 14:13:47 -0400 Subject: [PATCH 06/28] Fix style --- qiskit_ibm_runtime/accounts/management.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index f649a88ca..4aefc0803 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -179,7 +179,9 @@ def delete( name = name or cls._get_default_account_name(channel) old_name = cls._get_old_default_account_name(channel) return delete_config( - filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=name, old_name=old_name, + filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, + name=name, + old_name=old_name, ) @classmethod From 4417689a849e3db8d1ffa1db718d611403a79c35 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Wed, 23 Mar 2022 15:28:14 -0400 Subject: [PATCH 07/28] Make auth parameter work with saved_accounts --- qiskit_ibm_runtime/accounts/account.py | 4 +++- qiskit_ibm_runtime/accounts/management.py | 16 +++++++++++----- qiskit_ibm_runtime/ibm_runtime_service.py | 6 +++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/qiskit_ibm_runtime/accounts/account.py b/qiskit_ibm_runtime/accounts/account.py index 6f4aee2b1..3bd7db5cc 100644 --- a/qiskit_ibm_runtime/accounts/account.py +++ b/qiskit_ibm_runtime/accounts/account.py @@ -79,7 +79,9 @@ def from_saved_format(cls, data: dict) -> "Account": """Creates an account instance from data saved on disk.""" proxies = data.get("proxies") auth = data.get("auth") - channel = Channel.IBM_QUANTUM if auth == "legacy" else Channel.IBM_CLOUD + channel = ( + Channel.IBM_QUANTUM.value if auth == "legacy" else Channel.IBM_CLOUD.value + ) return cls( channel=data.get("channel", channel), url=data.get("url"), diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index 4aefc0803..1bcd61d38 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -72,7 +72,6 @@ def save( @staticmethod def list( default: Optional[bool] = None, - auth: Optional[str] = None, channel: Optional[ChannelType] = None, name: Optional[str] = None, ) -> Dict[str, Account]: @@ -81,9 +80,6 @@ def list( def _matching_name(account_name: str) -> bool: return name is None or name == account_name - def _matching_auth(account: Account) -> bool: - return auth is None or account.channel == auth - def _matching_channel(account: Account) -> bool: return channel is None or account.channel == channel @@ -102,7 +98,17 @@ def _matching_default(account_name: str) -> bool: # load all accounts all_accounts = map( - lambda kv: (kv[0], Account.from_saved_format(kv[1])), + lambda kv: ( + kv[0] + if kv[0] + not in [_DEFAULT_ACCOUNT_NAME_CLOUD, _DEFAULT_ACCOUNT_NAME_LEGACY] + else ( + _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM + if kv[0] == _DEFAULT_ACCOUNT_NAME_LEGACY + else _DEFAULT_ACCOUNT_NAME_IBM_CLOUD + ), + Account.from_saved_format(kv[1]), + ), read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE).items(), ) diff --git a/qiskit_ibm_runtime/ibm_runtime_service.py b/qiskit_ibm_runtime/ibm_runtime_service.py index d3d0dd79e..3eea3220f 100644 --- a/qiskit_ibm_runtime/ibm_runtime_service.py +++ b/qiskit_ibm_runtime/ibm_runtime_service.py @@ -671,11 +671,15 @@ def saved_accounts( DeprecationWarning, stacklevel=2, ) + if auth == "cloud": + channel = Channel.IBM_CLOUD + elif auth == "legacy": + channel = Channel.IBM_QUANTUM return dict( map( lambda kv: (kv[0], Account.to_saved_format(kv[1])), AccountManager.list( - default=default, auth=auth, channel=channel, name=name + default=default, channel=channel, name=name ).items(), ), ) From 8edfdc9729f936ec8c74bd9eb19bf78af687f575 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Wed, 23 Mar 2022 15:55:39 -0400 Subject: [PATCH 08/28] Support auth parameter when enabling account --- qiskit_ibm_runtime/accounts/management.py | 12 +++++++----- qiskit_ibm_runtime/accounts/storage.py | 3 +++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index 1bcd61d38..448529a8a 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -15,7 +15,7 @@ import os from typing import Optional, Dict from .exceptions import AccountNotFoundError -from .account import Account, AccountType, ChannelType +from .account import Account, ChannelType from ..channel import Channel from ..proxies import ProxyConfiguration from .storage import save_config, read_config, delete_config @@ -28,9 +28,7 @@ _DEFAULT_ACCOUNT_NAME_CLOUD = "default-cloud" _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM = "default-ibm-quantum" _DEFAULT_ACCOUNT_NAME_IBM_CLOUD = "default-ibm-cloud" -_DEFAULT_ACCOUNT_TYPE: AccountType = "cloud" _DEFAULT_CHANNEL_TYPE: ChannelType = Channel.IBM_CLOUD -_ACCOUNT_TYPES = [_DEFAULT_ACCOUNT_TYPE, "legacy"] _CHANNEL_TYPES = [_DEFAULT_CHANNEL_TYPE, Channel.IBM_QUANTUM] @@ -160,7 +158,8 @@ def get( if channel: saved_account = read_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, - name=cls._get_default_account_name(channel), + name=cls._get_default_account_name(channel=channel), + old_name=cls._get_old_default_account_name(channel=channel), ) if saved_account is None: raise AccountNotFoundError(f"No default {channel} account saved.") @@ -168,9 +167,12 @@ def get( all_config = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE) for channel_type in _CHANNEL_TYPES: - account_name = cls._get_default_account_name(channel_type) + account_name = cls._get_default_account_name(channel=channel_type) + old_account_name = cls._get_old_default_account_name(channel=channel_type) if account_name in all_config: return Account.from_saved_format(all_config[account_name]) + if old_account_name in all_config: + return Account.from_saved_format(all_config[old_account_name]) raise AccountNotFoundError("Unable to find account.") diff --git a/qiskit_ibm_runtime/accounts/storage.py b/qiskit_ibm_runtime/accounts/storage.py index c27ce9e29..03d1f666f 100644 --- a/qiskit_ibm_runtime/accounts/storage.py +++ b/qiskit_ibm_runtime/accounts/storage.py @@ -47,6 +47,7 @@ def save_config( def read_config( filename: str, name: Optional[str] = None, + old_name: Optional[str] = None, ) -> Optional[Dict]: """Read configuration data from a JSON file.""" logger.debug("Read configuration data for '%s' from '%s'", name, filename) @@ -58,6 +59,8 @@ def read_config( return data if name in data: return data[name] + if old_name in data: + return data[old_name] return None From b9c16c7938e7bbba74b2f52bf0d9ab52a7fb600b Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Fri, 25 Mar 2022 09:34:54 -0400 Subject: [PATCH 09/28] Revert temporarily to fix doc builds on fork --- docs/release_notes.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/release_notes.rst b/docs/release_notes.rst index d02fe5fb6..ec77acc34 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -1,2 +1 @@ .. release-notes:: Release Notes - :version: 0.2.0, 0.1.0, 0.1.0rc2, 0.1.0rc1 From 99073f778bbe7fd8265d65f7bf7e4ce9370f8748 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Fri, 25 Mar 2022 11:47:38 -0400 Subject: [PATCH 10/28] Add some tests for overwriting saved auth accounts with channel accounts --- test/unit/test_account.py | 93 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/test/unit/test_account.py b/test/unit/test_account.py index a7fb630a4..08beebf32 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -29,6 +29,10 @@ ) from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.accounts.account import IBM_CLOUD_API_URL, IBM_QUANTUM_API_URL +from qiskit_ibm_runtime.accounts.management import ( + _DEFAULT_ACCOUNT_NAME_LEGACY, + _DEFAULT_ACCOUNT_NAME_CLOUD, +) from qiskit_ibm_runtime.proxies import ProxyConfiguration from .mock.fake_runtime_service import FakeRuntimeService from ..ibm_test_case import IBMTestCase @@ -56,6 +60,20 @@ ), ) +_TEST_LEGACY_ACCOUNT = { + "auth": "legacy", + "token": "token-x", + "url": "https://auth.quantum-computing.ibm.com/api", + "instance": "ibm-q/open/main", +} + +_TEST_CLOUD_ACCOUNT = { + "auth": "cloud", + "token": "token-y", + "url": "https://cloud.ibm.com", + "instance": "crn:v1:bluemix:public:quantum-computing:us-east:a/...::", +} + class TestAccount(IBMTestCase): """Tests for Account class.""" @@ -158,8 +176,8 @@ class TestAccountManager(IBMTestCase): @temporary_account_config_file( contents={"conflict": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format()} ) - def test_save_without_override(self): - """Test to override an existing account without setting overwrite=True.""" + def test_save_without_overwrite(self): + """Test to overwrite an existing account without setting overwrite=True.""" with self.assertRaises(AccountAlreadyExistsError): AccountManager.save( name="conflict", @@ -170,6 +188,77 @@ def test_save_without_override(self): overwrite=False, ) + # TODO remove test when removing auth parameter + @temporary_account_config_file( + contents={_DEFAULT_ACCOUNT_NAME_CLOUD: _TEST_CLOUD_ACCOUNT} + ) + def test_save_channel_ibm_cloud_over_auth_cloud_without_overwrite(self): + """Test to overwrite an existing auth "cloud" account with channel "ibm_cloud" + and without setting overwrite=True.""" + with self.assertRaises(AccountAlreadyExistsError): + AccountManager.save( + token=_TEST_IBM_CLOUD_ACCOUNT.token, + url=_TEST_IBM_CLOUD_ACCOUNT.url, + instance=_TEST_IBM_CLOUD_ACCOUNT.instance, + channel=Channel.IBM_CLOUD, + overwrite=False, + ) + + # TODO remove test when removing auth parameter + @temporary_account_config_file( + contents={_DEFAULT_ACCOUNT_NAME_LEGACY: _TEST_LEGACY_ACCOUNT} + ) + def test_save_channel_ibm_quantum_over_auth_legacy_without_overwrite(self): + """Test to overwrite an existing auth "legacy" account with channel "ibm_quantum" + and without setting overwrite=True.""" + with self.assertRaises(AccountAlreadyExistsError): + AccountManager.save( + token=_TEST_IBM_QUANTUM_ACCOUNT.token, + url=_TEST_IBM_QUANTUM_ACCOUNT.url, + instance=_TEST_IBM_QUANTUM_ACCOUNT.instance, + channel=Channel.IBM_QUANTUM, + overwrite=False, + ) + + # TODO remove test when removing auth parameter + @temporary_account_config_file( + contents={_DEFAULT_ACCOUNT_NAME_LEGACY: _TEST_LEGACY_ACCOUNT} + ) + def test_save_channel_ibm_quantum_over_auth_legacy_with_overwrite(self): + """Test to overwrite an existing auth "legacy" account with channel "ibm_quantum" + and with setting overwrite=True.""" + AccountManager.save( + token=_TEST_IBM_QUANTUM_ACCOUNT.token, + url=_TEST_IBM_QUANTUM_ACCOUNT.url, + instance=_TEST_IBM_QUANTUM_ACCOUNT.instance, + channel=Channel.IBM_QUANTUM.value, + name=None, + overwrite=True, + ) + self.assertEqual( + _TEST_IBM_QUANTUM_ACCOUNT, AccountManager.get(channel=Channel.IBM_QUANTUM) + ) + + # TODO remove test when removing auth parameter + @temporary_account_config_file( + contents={_DEFAULT_ACCOUNT_NAME_CLOUD: _TEST_CLOUD_ACCOUNT} + ) + def test_save_channel_ibm_cloud_over_auth_cloud_with_overwrite(self): + """Test to overwrite an existing auth "cloud" account with channel "ibm_cloud" + and with setting overwrite=True.""" + AccountManager.save( + token=_TEST_IBM_CLOUD_ACCOUNT.token, + url=_TEST_IBM_CLOUD_ACCOUNT.url, + instance=_TEST_IBM_CLOUD_ACCOUNT.instance, + channel=Channel.IBM_CLOUD.value, + proxies=_TEST_IBM_CLOUD_ACCOUNT.proxies, + name=None, + overwrite=True, + ) + self.assertEqual( + _TEST_IBM_CLOUD_ACCOUNT, AccountManager.get(channel=Channel.IBM_CLOUD) + ) + @temporary_account_config_file( contents={"conflict": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format()} ) From bc79b259fce471d2cfeba531d23af4f3a7c7bc31 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Fri, 25 Mar 2022 12:13:05 -0400 Subject: [PATCH 11/28] Add couple more scenarios for testing overwriting named auth accounts with named channel accounts --- test/unit/test_account.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 08beebf32..13a2a131d 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -259,6 +259,44 @@ def test_save_channel_ibm_cloud_over_auth_cloud_with_overwrite(self): _TEST_IBM_CLOUD_ACCOUNT, AccountManager.get(channel=Channel.IBM_CLOUD) ) + # TODO remove test when removing auth parameter + @temporary_account_config_file( + contents={"personal-account": _TEST_CLOUD_ACCOUNT} + ) + def test_save_channel_ibm_cloud_with_name_over_auth_cloud_with_overwrite(self): + """Test to overwrite an existing named auth "cloud" account with channel "ibm_cloud" + and with setting overwrite=True.""" + AccountManager.save( + token=_TEST_IBM_CLOUD_ACCOUNT.token, + url=_TEST_IBM_CLOUD_ACCOUNT.url, + instance=_TEST_IBM_CLOUD_ACCOUNT.instance, + channel=Channel.IBM_CLOUD.value, + proxies=_TEST_IBM_CLOUD_ACCOUNT.proxies, + name="personal-account", + overwrite=True, + ) + self.assertEqual( + _TEST_IBM_CLOUD_ACCOUNT, AccountManager.get(name="personal-account") + ) + + # TODO remove test when removing auth parameter + @temporary_account_config_file( + contents={"personal-account": _TEST_CLOUD_ACCOUNT} + ) + def test_save_channel_ibm_cloud_with_name_over_auth_cloud_without_overwrite(self): + """Test to overwrite an existing named auth "cloud" account with channel "ibm_cloud" + and without setting overwrite=True.""" + with self.assertRaises(AccountAlreadyExistsError): + AccountManager.save( + token=_TEST_IBM_CLOUD_ACCOUNT.token, + url=_TEST_IBM_CLOUD_ACCOUNT.url, + instance=_TEST_IBM_CLOUD_ACCOUNT.instance, + channel=Channel.IBM_CLOUD.value, + proxies=_TEST_IBM_CLOUD_ACCOUNT.proxies, + name="personal-account", + overwrite=False, + ) + @temporary_account_config_file( contents={"conflict": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format()} ) From 0922e282f482dcc6815045c026c3f75866598513 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Fri, 25 Mar 2022 12:25:48 -0400 Subject: [PATCH 12/28] Add couple more tests for saving named legacy accounts --- test/unit/test_account.py | 46 +++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 13a2a131d..32163e11c 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -259,10 +259,8 @@ def test_save_channel_ibm_cloud_over_auth_cloud_with_overwrite(self): _TEST_IBM_CLOUD_ACCOUNT, AccountManager.get(channel=Channel.IBM_CLOUD) ) - # TODO remove test when removing auth parameter - @temporary_account_config_file( - contents={"personal-account": _TEST_CLOUD_ACCOUNT} - ) + # TODO remove test when removing auth parameter + @temporary_account_config_file(contents={"personal-account": _TEST_CLOUD_ACCOUNT}) def test_save_channel_ibm_cloud_with_name_over_auth_cloud_with_overwrite(self): """Test to overwrite an existing named auth "cloud" account with channel "ibm_cloud" and with setting overwrite=True.""" @@ -280,9 +278,7 @@ def test_save_channel_ibm_cloud_with_name_over_auth_cloud_with_overwrite(self): ) # TODO remove test when removing auth parameter - @temporary_account_config_file( - contents={"personal-account": _TEST_CLOUD_ACCOUNT} - ) + @temporary_account_config_file(contents={"personal-account": _TEST_CLOUD_ACCOUNT}) def test_save_channel_ibm_cloud_with_name_over_auth_cloud_without_overwrite(self): """Test to overwrite an existing named auth "cloud" account with channel "ibm_cloud" and without setting overwrite=True.""" @@ -297,6 +293,42 @@ def test_save_channel_ibm_cloud_with_name_over_auth_cloud_without_overwrite(self overwrite=False, ) + # TODO remove test when removing auth parameter + @temporary_account_config_file(contents={"personal-account": _TEST_LEGACY_ACCOUNT}) + def test_save_channel_ibm_quantum_with_name_over_auth_legacy_with_overwrite(self): + """Test to overwrite an existing named auth "legacy" account with channel "ibm_quantum" + and with setting overwrite=True.""" + AccountManager.save( + token=_TEST_IBM_QUANTUM_ACCOUNT.token, + url=_TEST_IBM_QUANTUM_ACCOUNT.url, + instance=_TEST_IBM_QUANTUM_ACCOUNT.instance, + channel=Channel.IBM_QUANTUM.value, + proxies=_TEST_IBM_QUANTUM_ACCOUNT.proxies, + name="personal-account", + overwrite=True, + ) + self.assertEqual( + _TEST_IBM_QUANTUM_ACCOUNT, AccountManager.get(name="personal-account") + ) + + # TODO remove test when removing auth parameter + @temporary_account_config_file(contents={"personal-account": _TEST_LEGACY_ACCOUNT}) + def test_save_channel_ibm_quantum_with_name_over_auth_legacy_without_overwrite( + self, + ): + """Test to overwrite an existing named auth "legacy" account with channel "ibm_quantum" + and without setting overwrite=True.""" + with self.assertRaises(AccountAlreadyExistsError): + AccountManager.save( + token=_TEST_IBM_QUANTUM_ACCOUNT.token, + url=_TEST_IBM_QUANTUM_ACCOUNT.url, + instance=_TEST_IBM_QUANTUM_ACCOUNT.instance, + channel=Channel.IBM_QUANTUM.value, + proxies=_TEST_IBM_QUANTUM_ACCOUNT.proxies, + name="personal-account", + overwrite=False, + ) + @temporary_account_config_file( contents={"conflict": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format()} ) From 20a27a77a4eb31b7b5a86366c94356875ba81958 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Fri, 25 Mar 2022 14:03:39 -0400 Subject: [PATCH 13/28] Test to list existing auth accounts in channel format --- test/unit/test_account.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 32163e11c..d6e3a467a 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -32,6 +32,8 @@ from qiskit_ibm_runtime.accounts.management import ( _DEFAULT_ACCOUNT_NAME_LEGACY, _DEFAULT_ACCOUNT_NAME_CLOUD, + _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM, + _DEFAULT_ACCOUNT_NAME_IBM_CLOUD, ) from qiskit_ibm_runtime.proxies import ProxyConfiguration from .mock.fake_runtime_service import FakeRuntimeService @@ -72,6 +74,9 @@ "token": "token-y", "url": "https://cloud.ibm.com", "instance": "crn:v1:bluemix:public:quantum-computing:us-east:a/...::", + "proxies": { + "username_ntlm": "bla", "password_ntlm": "blub", "urls":{"https": "127.0.0.1"} + }, } @@ -403,6 +408,18 @@ def test_list(self): self.assertEqual(accounts["key1"], _TEST_IBM_CLOUD_ACCOUNT) self.assertTrue(accounts["key2"], _TEST_IBM_QUANTUM_ACCOUNT) + with temporary_account_config_file( + contents={ + _DEFAULT_ACCOUNT_NAME_CLOUD: _TEST_CLOUD_ACCOUNT, + _DEFAULT_ACCOUNT_NAME_LEGACY: _TEST_CLOUD_ACCOUNT, + } + ), self.subTest("non-empty list of auth accounts"): + accounts = AccountManager.list() + + self.assertEqual(len(accounts), 2) + self.assertEqual(accounts[_DEFAULT_ACCOUNT_NAME_IBM_CLOUD], _TEST_IBM_CLOUD_ACCOUNT) + self.assertTrue(accounts[_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM], _TEST_IBM_QUANTUM_ACCOUNT) + with temporary_account_config_file(contents={}), self.subTest( "empty list of accounts" ): From 1db9e997334dfc0625dd6c0a316e0d3110fe8737 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Fri, 25 Mar 2022 14:05:23 -0400 Subject: [PATCH 14/28] Fix lint --- test/unit/test_account.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/unit/test_account.py b/test/unit/test_account.py index d6e3a467a..3086f9cc1 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -75,7 +75,9 @@ "url": "https://cloud.ibm.com", "instance": "crn:v1:bluemix:public:quantum-computing:us-east:a/...::", "proxies": { - "username_ntlm": "bla", "password_ntlm": "blub", "urls":{"https": "127.0.0.1"} + "username_ntlm": "bla", + "password_ntlm": "blub", + "urls": {"https": "127.0.0.1"}, }, } @@ -417,8 +419,12 @@ def test_list(self): accounts = AccountManager.list() self.assertEqual(len(accounts), 2) - self.assertEqual(accounts[_DEFAULT_ACCOUNT_NAME_IBM_CLOUD], _TEST_IBM_CLOUD_ACCOUNT) - self.assertTrue(accounts[_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM], _TEST_IBM_QUANTUM_ACCOUNT) + self.assertEqual( + accounts[_DEFAULT_ACCOUNT_NAME_IBM_CLOUD], _TEST_IBM_CLOUD_ACCOUNT + ) + self.assertTrue( + accounts[_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM], _TEST_IBM_QUANTUM_ACCOUNT + ) with temporary_account_config_file(contents={}), self.subTest( "empty list of accounts" From 44a21e8d30934f9e10b9a2071f9d8c5684c73b4f Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Fri, 25 Mar 2022 15:03:22 -0400 Subject: [PATCH 15/28] Add test for filtering auth accounts --- test/unit/test_account.py | 56 ++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 3086f9cc1..02f5b7746 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -22,7 +22,6 @@ from qiskit_ibm_runtime.accounts import ( AccountManager, Account, - management, AccountAlreadyExistsError, AccountNotFoundError, InvalidAccountError, @@ -358,13 +357,13 @@ def test_save_get(self): (_TEST_IBM_QUANTUM_ACCOUNT, "acct-1", "acct-1"), (_TEST_IBM_CLOUD_ACCOUNT, "acct-2", "acct-2"), # verify default account name handling for ibm_cloud accounts - (_TEST_IBM_CLOUD_ACCOUNT, None, management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD), + (_TEST_IBM_CLOUD_ACCOUNT, None, _DEFAULT_ACCOUNT_NAME_IBM_CLOUD), (_TEST_IBM_CLOUD_ACCOUNT, None, None), # verify default account name handling for ibm_quantum accounts ( _TEST_IBM_QUANTUM_ACCOUNT, None, - management._DEFAULT_ACCOUNT_NAME_IBM_QUANTUM, + _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM, ), # verify account override (_TEST_IBM_QUANTUM_ACCOUNT, "acct", "acct"), @@ -435,10 +434,10 @@ def test_list(self): contents={ "key1": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format(), "key2": _TEST_IBM_QUANTUM_ACCOUNT.to_saved_format(), - management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD: Account( + _DEFAULT_ACCOUNT_NAME_IBM_CLOUD: Account( Channel.IBM_CLOUD, "token-ibm-cloud", instance="crn:123" ).to_saved_format(), - management._DEFAULT_ACCOUNT_NAME_IBM_QUANTUM: Account( + _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM: Account( Channel.IBM_QUANTUM, "token-ibm-quantum" ).to_saved_format(), } @@ -446,20 +445,57 @@ def test_list(self): accounts = list(AccountManager.list(channel=Channel.IBM_CLOUD).keys()) self.assertEqual(len(accounts), 2) self.assertListEqual( - accounts, ["key1", management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD] + accounts, ["key1", _DEFAULT_ACCOUNT_NAME_IBM_CLOUD] ) accounts = list(AccountManager.list(channel=Channel.IBM_QUANTUM).keys()) self.assertEqual(len(accounts), 2) self.assertListEqual( - accounts, ["key2", management._DEFAULT_ACCOUNT_NAME_IBM_QUANTUM] + accounts, ["key2", _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM] ) accounts = list( AccountManager.list(channel=Channel.IBM_CLOUD, default=True).keys() ) self.assertEqual(len(accounts), 1) - self.assertListEqual(accounts, [management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD]) + self.assertListEqual(accounts, [_DEFAULT_ACCOUNT_NAME_IBM_CLOUD]) + + accounts = list( + AccountManager.list(channel=Channel.IBM_CLOUD, default=False).keys() + ) + self.assertEqual(len(accounts), 1) + self.assertListEqual(accounts, ["key1"]) + + accounts = list(AccountManager.list(name="key1").keys()) + self.assertEqual(len(accounts), 1) + self.assertListEqual(accounts, ["key1"]) + + # TODO remove test when removing auth parameter + with temporary_account_config_file( + contents={ + "key1": _TEST_CLOUD_ACCOUNT, + "key2": _TEST_LEGACY_ACCOUNT, + _DEFAULT_ACCOUNT_NAME_CLOUD: _TEST_CLOUD_ACCOUNT, + _DEFAULT_ACCOUNT_NAME_LEGACY: _TEST_LEGACY_ACCOUNT, + } + ), self.subTest("filtered list of auth accounts"): + accounts = list(AccountManager.list(channel=Channel.IBM_CLOUD).keys()) + self.assertEqual(len(accounts), 2) + self.assertListEqual( + accounts, ["key1", _DEFAULT_ACCOUNT_NAME_IBM_CLOUD] + ) + + accounts = list(AccountManager.list(channel=Channel.IBM_QUANTUM).keys()) + self.assertEqual(len(accounts), 2) + self.assertListEqual( + accounts, ["key2", _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM] + ) + + accounts = list( + AccountManager.list(channel=Channel.IBM_CLOUD, default=True).keys() + ) + self.assertEqual(len(accounts), 1) + self.assertListEqual(accounts, [_DEFAULT_ACCOUNT_NAME_IBM_CLOUD]) accounts = list( AccountManager.list(channel=Channel.IBM_CLOUD, default=False).keys() @@ -474,8 +510,8 @@ def test_list(self): @temporary_account_config_file( contents={ "key1": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format(), - management._DEFAULT_ACCOUNT_NAME_IBM_QUANTUM: _TEST_IBM_QUANTUM_ACCOUNT.to_saved_format(), - management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD: _TEST_IBM_CLOUD_ACCOUNT.to_saved_format(), + _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM: _TEST_IBM_QUANTUM_ACCOUNT.to_saved_format(), + _DEFAULT_ACCOUNT_NAME_IBM_CLOUD: _TEST_IBM_CLOUD_ACCOUNT.to_saved_format(), } ) def test_delete(self): From 4513d559b366f33f48fbf0b0de9723845074e4b0 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Fri, 25 Mar 2022 15:04:18 -0400 Subject: [PATCH 16/28] Fix lint --- test/unit/test_account.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 02f5b7746..2a6ce3f3b 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -444,15 +444,11 @@ def test_list(self): ), self.subTest("filtered list of accounts"): accounts = list(AccountManager.list(channel=Channel.IBM_CLOUD).keys()) self.assertEqual(len(accounts), 2) - self.assertListEqual( - accounts, ["key1", _DEFAULT_ACCOUNT_NAME_IBM_CLOUD] - ) + self.assertListEqual(accounts, ["key1", _DEFAULT_ACCOUNT_NAME_IBM_CLOUD]) accounts = list(AccountManager.list(channel=Channel.IBM_QUANTUM).keys()) self.assertEqual(len(accounts), 2) - self.assertListEqual( - accounts, ["key2", _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM] - ) + self.assertListEqual(accounts, ["key2", _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM]) accounts = list( AccountManager.list(channel=Channel.IBM_CLOUD, default=True).keys() @@ -481,15 +477,11 @@ def test_list(self): ), self.subTest("filtered list of auth accounts"): accounts = list(AccountManager.list(channel=Channel.IBM_CLOUD).keys()) self.assertEqual(len(accounts), 2) - self.assertListEqual( - accounts, ["key1", _DEFAULT_ACCOUNT_NAME_IBM_CLOUD] - ) + self.assertListEqual(accounts, ["key1", _DEFAULT_ACCOUNT_NAME_IBM_CLOUD]) accounts = list(AccountManager.list(channel=Channel.IBM_QUANTUM).keys()) self.assertEqual(len(accounts), 2) - self.assertListEqual( - accounts, ["key2", _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM] - ) + self.assertListEqual(accounts, ["key2", _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM]) accounts = list( AccountManager.list(channel=Channel.IBM_CLOUD, default=True).keys() From 648647bf95561b0b47244ecfce148997a77df4f6 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Fri, 25 Mar 2022 17:13:04 -0400 Subject: [PATCH 17/28] Fix failing integration tests --- qiskit_ibm_runtime/api/clients/auth.py | 2 +- test/decorators.py | 8 ++++---- test/integration/test_proxies.py | 2 +- test/unit/test_client_parameters.py | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/qiskit_ibm_runtime/api/clients/auth.py b/qiskit_ibm_runtime/api/clients/auth.py index 00f103b81..fabe0840c 100644 --- a/qiskit_ibm_runtime/api/clients/auth.py +++ b/qiskit_ibm_runtime/api/clients/auth.py @@ -55,7 +55,7 @@ def _init_service_clients(self, **request_kwargs: Any) -> Api: """ # Request an access token. self.access_token = self._request_access_token() - self.auth_api.session.channel = QuantumAuth(access_token=self.access_token) + self.auth_api.session.auth = QuantumAuth(access_token=self.access_token) self._service_urls = self.user_urls() # Create the api server client, using the access token. diff --git a/test/decorators.py b/test/decorators.py index b91dbbcd7..88ce32a0c 100644 --- a/test/decorators.py +++ b/test/decorators.py @@ -29,10 +29,10 @@ def run_quantum_and_cloud_fake(func): @wraps(func) def _wrapper(self, *args, **kwargs): ibm_quantum_service = FakeRuntimeService( - channel=Channel.IBM_QUANTUM, token="my_token", instance="h/g/p" + channel=Channel.IBM_QUANTUM.value, token="my_token", instance="h/g/p" ) cloud_service = FakeRuntimeService( - channel=Channel.IBM_CLOUD, + channel=Channel.IBM_CLOUD.value, token="my_token", instance="crn:v1:bluemix:public:quantum-computing:my-region:a/...:...::", ) @@ -51,9 +51,9 @@ def _get_integration_test_config(): os.getenv("QISKIT_IBM_INSTANCE"), ) channel: Any = ( - Channel.IBM_QUANTUM + Channel.IBM_QUANTUM.value if url.find("quantum-computing.ibm.com") >= 0 - else Channel.IBM_CLOUD + else Channel.IBM_CLOUD.value ) return channel, token, url, instance diff --git a/test/integration/test_proxies.py b/test/integration/test_proxies.py index f06a53322..36f1d5e62 100644 --- a/test/integration/test_proxies.py +++ b/test/integration/test_proxies.py @@ -56,7 +56,7 @@ def tearDown(self): # wait for the process to terminate self.proxy_process.wait() - @integration_test_setup(supported_channel=["cloud"]) + @integration_test_setup(supported_channel=[Channel.IBM_CLOUD]) def test_proxies_cloud_runtime_client( self, dependencies: IntegrationTestDependencies ) -> None: diff --git a/test/unit/test_client_parameters.py b/test/unit/test_client_parameters.py index 0859f3447..5c144c8cf 100644 --- a/test/unit/test_client_parameters.py +++ b/test/unit/test_client_parameters.py @@ -54,19 +54,19 @@ def test_get_runtime_api_base_url(self) -> None: """Test resolution of runtime API base URL.""" test_specs = [ ( - "cloud", + Channel.IBM_CLOUD.value, "crn:v1:bluemix:public:quantum-computing:us-east:a/...:...::", "https://cloud.ibm.com", "https://us-east.quantum-computing.cloud.ibm.com", ), ( - "cloud", + Channel.IBM_CLOUD.value, "crn:v1:bluemix:public:quantum-computing:my-region:a/...:...::", "https://cloud.ibm.com", "https://my-region.quantum-computing.cloud.ibm.com", ), ( - Channel.IBM_QUANTUM, + Channel.IBM_QUANTUM.value, "h/g/p", "https://auth.quantum-computing.ibm.com/api", "https://auth.quantum-computing.ibm.com/api", From bfbd3dad54ad206016bd5ebb6b18311daebb9cb8 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Fri, 25 Mar 2022 18:09:49 -0400 Subject: [PATCH 18/28] Add tests for deleting existing auth accounts using channel --- qiskit_ibm_runtime/accounts/management.py | 8 +++++-- qiskit_ibm_runtime/accounts/storage.py | 6 ++--- test/unit/test_account.py | 28 +++++++++++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index 448529a8a..2d029f0a8 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -49,7 +49,9 @@ def save( ) -> None: """Save account on disk.""" name = name or cls._get_default_account_name(channel) - old_name = cls._get_old_default_account_name(channel) + old_name = None + if name in [_DEFAULT_ACCOUNT_NAME_IBM_CLOUD, _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM]: + old_name = cls._get_old_default_account_name(channel) return save_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=name, @@ -185,7 +187,9 @@ def delete( """Delete account from disk.""" name = name or cls._get_default_account_name(channel) - old_name = cls._get_old_default_account_name(channel) + old_name = None + if name in [_DEFAULT_ACCOUNT_NAME_IBM_CLOUD, _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM]: + old_name = cls._get_old_default_account_name(channel) return delete_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=name, diff --git a/qiskit_ibm_runtime/accounts/storage.py b/qiskit_ibm_runtime/accounts/storage.py index 03d1f666f..5e32feeec 100644 --- a/qiskit_ibm_runtime/accounts/storage.py +++ b/qiskit_ibm_runtime/accounts/storage.py @@ -31,7 +31,7 @@ def save_config( with open(filename, mode="r", encoding="utf-8") as json_in: data = json.load(json_in) - if (data.get(name) or data.get(old_name)) and not overwrite: + if (data.get(name) or (old_name and data.get(old_name))) and not overwrite: raise AccountAlreadyExistsError( f"Named account ({name}) already exists. " f"Set overwrite=True to overwrite." @@ -39,7 +39,7 @@ def save_config( with open(filename, mode="w", encoding="utf-8") as json_out: data[name] = config - if old_name in data: + if old_name and old_name in data: del data[old_name] json.dump(data, json_out, sort_keys=True, indent=4) @@ -82,7 +82,7 @@ def delete_config( with open(filename, mode="w", encoding="utf-8") as json_out: if name in data: del data[name] - if old_name in data: + elif old_name in data: del data[old_name] json.dump(data, json_out, sort_keys=True, indent=4) return True diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 2a6ce3f3b..ec42fdd78 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -198,6 +198,7 @@ def test_save_without_overwrite(self): @temporary_account_config_file( contents={_DEFAULT_ACCOUNT_NAME_CLOUD: _TEST_CLOUD_ACCOUNT} ) + @no_envs(["QISKIT_IBM_TOKEN"]) def test_save_channel_ibm_cloud_over_auth_cloud_without_overwrite(self): """Test to overwrite an existing auth "cloud" account with channel "ibm_cloud" and without setting overwrite=True.""" @@ -207,6 +208,7 @@ def test_save_channel_ibm_cloud_over_auth_cloud_without_overwrite(self): url=_TEST_IBM_CLOUD_ACCOUNT.url, instance=_TEST_IBM_CLOUD_ACCOUNT.instance, channel=Channel.IBM_CLOUD, + name=None, overwrite=False, ) @@ -214,6 +216,7 @@ def test_save_channel_ibm_cloud_over_auth_cloud_without_overwrite(self): @temporary_account_config_file( contents={_DEFAULT_ACCOUNT_NAME_LEGACY: _TEST_LEGACY_ACCOUNT} ) + @no_envs(["QISKIT_IBM_TOKEN"]) def test_save_channel_ibm_quantum_over_auth_legacy_without_overwrite(self): """Test to overwrite an existing auth "legacy" account with channel "ibm_quantum" and without setting overwrite=True.""" @@ -223,6 +226,7 @@ def test_save_channel_ibm_quantum_over_auth_legacy_without_overwrite(self): url=_TEST_IBM_QUANTUM_ACCOUNT.url, instance=_TEST_IBM_QUANTUM_ACCOUNT.instance, channel=Channel.IBM_QUANTUM, + name=None, overwrite=False, ) @@ -230,6 +234,7 @@ def test_save_channel_ibm_quantum_over_auth_legacy_without_overwrite(self): @temporary_account_config_file( contents={_DEFAULT_ACCOUNT_NAME_LEGACY: _TEST_LEGACY_ACCOUNT} ) + @no_envs(["QISKIT_IBM_TOKEN"]) def test_save_channel_ibm_quantum_over_auth_legacy_with_overwrite(self): """Test to overwrite an existing auth "legacy" account with channel "ibm_quantum" and with setting overwrite=True.""" @@ -249,6 +254,7 @@ def test_save_channel_ibm_quantum_over_auth_legacy_with_overwrite(self): @temporary_account_config_file( contents={_DEFAULT_ACCOUNT_NAME_CLOUD: _TEST_CLOUD_ACCOUNT} ) + @no_envs(["QISKIT_IBM_TOKEN"]) def test_save_channel_ibm_cloud_over_auth_cloud_with_overwrite(self): """Test to overwrite an existing auth "cloud" account with channel "ibm_cloud" and with setting overwrite=True.""" @@ -521,6 +527,28 @@ def test_delete(self): self.assertTrue(len(AccountManager.list()) == 0) + @temporary_account_config_file( + contents={ + "key1": _TEST_CLOUD_ACCOUNT, + _DEFAULT_ACCOUNT_NAME_LEGACY: _TEST_LEGACY_ACCOUNT, + _DEFAULT_ACCOUNT_NAME_CLOUD: _TEST_CLOUD_ACCOUNT, + } + ) + def test_delete_auth(self): + """Test delete accounts already saved using auth.""" + + with self.subTest("delete named account"): + self.assertTrue(AccountManager.delete(name="key1")) + self.assertFalse(AccountManager.delete(name="key1")) + + with self.subTest("delete default auth='legacy' account using channel"): + self.assertTrue(AccountManager.delete(channel=Channel.IBM_QUANTUM)) + + with self.subTest("delete default auth='cloud' account using channel"): + self.assertTrue(AccountManager.delete()) + + self.assertTrue(len(AccountManager.list()) == 0) + MOCK_PROXY_CONFIG_DICT = { "urls": {"https": "127.0.0.1", "username_ntlm": "", "password_ntlm": ""} From 544b39a0d219ec1441e9c3fab18244c3bd0506b0 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Mon, 28 Mar 2022 10:08:44 -0400 Subject: [PATCH 19/28] Remove channel IDs to address confusion --- docs/max_time.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/max_time.rst b/docs/max_time.rst index 8afbc9dd7..cdfe5c515 100644 --- a/docs/max_time.rst +++ b/docs/max_time.rst @@ -10,14 +10,14 @@ status as `Cancelled - Ran too long`. The maximum execution time is the smaller of 1) the system limit and 2) the ``max_execution_time`` defined by the program. The system limit is defined below: -Qiskit Runtime on IBM Cloud (ibm_cloud) ---------------------------------------- +Qiskit Runtime on IBM Cloud +--------------------------- The system limit on the job execution time is 3 hours for a job running on a simulator and 8 hours for a job running on a physical system. -Qiskit Runtime on IBM Quantum (ibm_quantum) -------------------------------------------- +Qiskit Runtime on IBM Quantum +----------------------------- The system limit on the job execution time is From 593bb82dfb9314d44d1bb3bf63d77a5939e42d3a Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Mon, 28 Mar 2022 10:10:25 -0400 Subject: [PATCH 20/28] Update docs/tutorials/04_account_management.ipynb Co-authored-by: Daniel Kaulen --- docs/tutorials/04_account_management.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/04_account_management.ipynb b/docs/tutorials/04_account_management.ipynb index 0641dc2d4..2bc8935dc 100644 --- a/docs/tutorials/04_account_management.ipynb +++ b/docs/tutorials/04_account_management.ipynb @@ -74,7 +74,7 @@ "id": "bfb828ec", "metadata": {}, "source": [ - "Below are examples of saving an IBM Cloud and an IBM Quantum accounts. The `channel` parameter indicates the authentication type of the account. If you are saving multiple account, consider using the `name` parameter to differentiate them.\n" + "Below are examples of saving an IBM Cloud and an IBM Quantum accounts. The `channel` parameter allows to distinguish between different account types. If you are saving multiple accounts per channel, consider using the `name` parameter to differentiate them.\n" ] }, { From 489409b4cde35ac2fc0ddabd0e2d9a896b27b5f7 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Mon, 28 Mar 2022 10:36:19 -0400 Subject: [PATCH 21/28] Refactor and add functions for duplicated logic --- qiskit_ibm_runtime/ibm_runtime_service.py | 79 ++++++++--------------- 1 file changed, 27 insertions(+), 52 deletions(-) diff --git a/qiskit_ibm_runtime/ibm_runtime_service.py b/qiskit_ibm_runtime/ibm_runtime_service.py index 3eea3220f..89b72917b 100644 --- a/qiskit_ibm_runtime/ibm_runtime_service.py +++ b/qiskit_ibm_runtime/ibm_runtime_service.py @@ -163,14 +163,7 @@ def __init__( super().__init__() if auth: - warnings.warn( - f"Use of `auth` parameter is deprecated and will " - f"be removed in a future release. " - f"You can now use channel='{Channel.IBM_CLOUD}' or " - f"channel='{Channel.IBM_QUANTUM}' instead.", - DeprecationWarning, - stacklevel=2, - ) + self._auth_warning() self._account = self._discover_account( token=token, @@ -219,6 +212,17 @@ def __init__( # just seems wrong since backends are not runtime service instances. # self._discover_backends() + @staticmethod + def _auth_warning() -> None: + warnings.warn( + f"Use of `auth` parameter is deprecated and will " + f"be removed in a future release. " + f"You can now use channel='{Channel.IBM_CLOUD}' or " + f"channel='{Channel.IBM_QUANTUM}' instead.", + DeprecationWarning, + stacklevel=3, + ) + def _discover_account( self, token: Optional[str] = None, @@ -570,20 +574,18 @@ def delete_account( False if no account was found. """ if auth: - warnings.warn( - f"Use of `auth` parameter is deprecated and will " - f"be removed in a future release. " - f"You can now use channel='{Channel.IBM_CLOUD}' or " - f"channel='{Channel.IBM_QUANTUM}' instead.", - DeprecationWarning, - stacklevel=2, - ) - if auth == "cloud": - channel = Channel.IBM_CLOUD - elif auth == "legacy": - channel = Channel.IBM_QUANTUM + IBMRuntimeService._auth_warning() + channel = channel or IBMRuntimeService._get_channel_for_auth(auth) + return AccountManager.delete(name=name, channel=channel) + @staticmethod + def _get_channel_for_auth(auth: str) -> str: + """Returns channel type based on auth""" + if auth == "legacy": + return Channel.IBM_QUANTUM + return Channel.IBM_CLOUD + @staticmethod def save_account( token: Optional[str] = None, @@ -616,18 +618,8 @@ def save_account( overwrite: ``True`` if the existing account is to be overwritten. """ if auth: - warnings.warn( - f"Use of `auth` parameter is deprecated and will " - f"be removed in a future release. " - f"You can now use channel='{Channel.IBM_CLOUD}' " - f"or channel='{Channel.IBM_QUANTUM}' instead.", - DeprecationWarning, - stacklevel=2, - ) - if auth == "cloud": - channel = Channel.IBM_CLOUD - elif auth == "legacy": - channel = Channel.IBM_QUANTUM + IBMRuntimeService._auth_warning() + channel = channel or IBMRuntimeService._get_channel_for_auth(auth) AccountManager.save( token=token, @@ -663,18 +655,8 @@ def saved_accounts( ValueError: If an invalid account is found on disk. """ if auth: - warnings.warn( - f"Use of `auth` parameter is deprecated and will " - f"be removed in a future release. " - f"You can now use channel='{Channel.IBM_CLOUD}' " - f"or channel='{Channel.IBM_QUANTUM}' instead.", - DeprecationWarning, - stacklevel=2, - ) - if auth == "cloud": - channel = Channel.IBM_CLOUD - elif auth == "legacy": - channel = Channel.IBM_QUANTUM + IBMRuntimeService._auth_warning() + channel = channel or IBMRuntimeService._get_channel_for_auth(auth) return dict( map( lambda kv: (kv[0], Account.to_saved_format(kv[1])), @@ -1411,14 +1393,7 @@ def auth(self) -> str: Returns: The authentication type used. """ - warnings.warn( - f"Use of `auth` parameter is deprecated and will " - f"be removed in a future release. " - f"You can now use channel='{Channel.IBM_CLOUD}' " - f"or channel='{Channel.IBM_QUANTUM}' instead.", - DeprecationWarning, - stacklevel=2, - ) + self._auth_warning() return "cloud" if self._channel == Channel.IBM_CLOUD else "legacy" @property From dc1288c9df42551a2743dd995c607274fbc484e0 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Mon, 28 Mar 2022 14:02:33 -0400 Subject: [PATCH 22/28] Use strings for now since enums don't play well with literals --- qiskit_ibm_runtime/accounts/account.py | 21 ++-- qiskit_ibm_runtime/accounts/management.py | 9 +- qiskit_ibm_runtime/api/client_parameters.py | 3 +- qiskit_ibm_runtime/channel.py | 22 ---- qiskit_ibm_runtime/ibm_runtime_service.py | 41 +++---- qiskit_ibm_runtime/runtime_options.py | 3 +- qiskit_ibm_runtime/utils/utils.py | 3 +- test/account.py | 11 +- test/decorators.py | 12 +- test/e2e/test_tutorials.py | 4 +- test/integration/test_account.py | 9 +- test/integration/test_auth_client.py | 17 ++- test/integration/test_backend.py | 6 +- .../integration/test_backend_serialization.py | 13 +-- test/integration/test_basic_server_paths.py | 3 +- test/integration/test_job.py | 5 +- test/integration/test_proxies.py | 41 ++++--- test/integration/test_retrieve_job.py | 5 +- test/unit/mock/fake_runtime_client.py | 11 +- test/unit/mock/fake_runtime_service.py | 5 +- test/unit/test_account.py | 109 ++++++++---------- test/unit/test_account_client.py | 4 +- test/unit/test_backend_retrieval.py | 21 ++-- test/unit/test_client_parameters.py | 14 +-- test/unit/test_data_serialization.py | 3 +- test/unit/test_job_retrieval.py | 5 +- test/unit/test_jobs.py | 17 ++- test/unit/test_runtime_ws.py | 3 +- test/utils.py | 3 +- 29 files changed, 175 insertions(+), 248 deletions(-) delete mode 100644 qiskit_ibm_runtime/channel.py diff --git a/qiskit_ibm_runtime/accounts/account.py b/qiskit_ibm_runtime/accounts/account.py index 3bd7db5cc..eff4c7db2 100644 --- a/qiskit_ibm_runtime/accounts/account.py +++ b/qiskit_ibm_runtime/accounts/account.py @@ -21,13 +21,12 @@ from .exceptions import InvalidAccountError, CloudResourceNameResolutionError from ..api.auth import QuantumAuth, CloudAuth -from ..channel import Channel from ..proxies import ProxyConfiguration from ..utils.hgp import from_instance_format from ..utils import resolve_crn AccountType = Optional[Literal["cloud", "legacy"]] -ChannelType = Optional[Literal[Channel.IBM_CLOUD, Channel.IBM_QUANTUM]] +ChannelType = Optional[Literal["ibm_cloud", "ibm_quantum"]] IBM_QUANTUM_API_URL = "https://auth.quantum-computing.ibm.com/api" IBM_CLOUD_API_URL = "https://cloud.ibm.com" @@ -57,7 +56,7 @@ def __init__( verify: Whether to verify server's TLS certificate. """ resolved_url = url or ( - IBM_QUANTUM_API_URL if channel == Channel.IBM_QUANTUM else IBM_CLOUD_API_URL + IBM_QUANTUM_API_URL if channel == "ibm_quantum" else IBM_CLOUD_API_URL ) self.channel = channel @@ -79,9 +78,7 @@ def from_saved_format(cls, data: dict) -> "Account": """Creates an account instance from data saved on disk.""" proxies = data.get("proxies") auth = data.get("auth") - channel = ( - Channel.IBM_QUANTUM.value if auth == "legacy" else Channel.IBM_CLOUD.value - ) + channel = "ibm_quantum" if auth == "legacy" else "ibm_cloud" return cls( channel=data.get("channel", channel), url=data.get("url"), @@ -101,7 +98,7 @@ def resolve_crn(self) -> None: Raises: CloudResourceNameResolutionError: if CRN value cannot be resolved. """ - if self.channel == Channel.IBM_CLOUD: + if self.channel == "ibm_cloud": crn = resolve_crn( channel=self.channel, url=self.url, @@ -126,7 +123,7 @@ def resolve_crn(self) -> None: def get_auth_handler(self) -> AuthBase: """Returns the respective authentication handler.""" - if self.channel == Channel.IBM_CLOUD: + if self.channel == "ibm_cloud": return CloudAuth(api_key=self.token, crn=self.instance) return QuantumAuth(access_token=self.token) @@ -165,10 +162,10 @@ def validate(self) -> "Account": @staticmethod def _assert_valid_channel(channel: ChannelType) -> None: """Assert that the channel parameter is valid.""" - if not (channel in [channel.value for channel in Channel]): + if not (channel in ["ibm_cloud", "ibm_quantum"]): raise InvalidAccountError( f"Invalid `channel` value. Expected one of " - f"{[channel.value for channel in Channel]}, got '{channel}'." + f"{['ibm_cloud', 'ibm_quantum']}, got '{channel}'." ) @staticmethod @@ -198,12 +195,12 @@ def _assert_valid_proxies(config: ProxyConfiguration) -> None: @staticmethod def _assert_valid_instance(channel: ChannelType, instance: str) -> None: """Assert that the instance name is valid for the given account type.""" - if channel == Channel.IBM_CLOUD: + if channel == "ibm_cloud": if not (isinstance(instance, str) and len(instance) > 0): raise InvalidAccountError( f"Invalid `instance` value. Expected a non-empty string, got '{instance}'." ) - if channel == Channel.IBM_QUANTUM: + if channel == "ibm_quantum": if instance is not None: try: from_instance_format(instance) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index 2d029f0a8..6932e02b8 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -16,7 +16,6 @@ from typing import Optional, Dict from .exceptions import AccountNotFoundError from .account import Account, ChannelType -from ..channel import Channel from ..proxies import ProxyConfiguration from .storage import save_config, read_config, delete_config @@ -28,8 +27,8 @@ _DEFAULT_ACCOUNT_NAME_CLOUD = "default-cloud" _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM = "default-ibm-quantum" _DEFAULT_ACCOUNT_NAME_IBM_CLOUD = "default-ibm-cloud" -_DEFAULT_CHANNEL_TYPE: ChannelType = Channel.IBM_CLOUD -_CHANNEL_TYPES = [_DEFAULT_CHANNEL_TYPE, Channel.IBM_QUANTUM] +_DEFAULT_CHANNEL_TYPE: ChannelType = "ibm_cloud" +_CHANNEL_TYPES = [_DEFAULT_CHANNEL_TYPE, "ibm_quantum"] class AccountManager: @@ -214,7 +213,7 @@ def _from_env_variables(cls, channel: Optional[ChannelType]) -> Optional[Account def _get_default_account_name(cls, channel: ChannelType) -> str: return ( _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM - if channel == Channel.IBM_QUANTUM + if channel == "ibm_quantum" else _DEFAULT_ACCOUNT_NAME_IBM_CLOUD ) @@ -222,6 +221,6 @@ def _get_default_account_name(cls, channel: ChannelType) -> str: def _get_old_default_account_name(cls, channel: ChannelType) -> str: return ( _DEFAULT_ACCOUNT_NAME_LEGACY - if channel == Channel.IBM_QUANTUM + if channel == "ibm_quantum" else _DEFAULT_ACCOUNT_NAME_CLOUD ) diff --git a/qiskit_ibm_runtime/api/client_parameters.py b/qiskit_ibm_runtime/api/client_parameters.py index 4660a7799..3ff4911fa 100644 --- a/qiskit_ibm_runtime/api/client_parameters.py +++ b/qiskit_ibm_runtime/api/client_parameters.py @@ -17,7 +17,6 @@ from ..utils import get_runtime_api_base_url from ..api.auth import QuantumAuth, CloudAuth from ..proxies import ProxyConfiguration -from ..channel import Channel TEMPLATE_IBM_HUBS = "{prefix}/Network/{hub}/Groups/{group}/Projects/{project}" """str: Template for creating an IBM Quantum URL with hub/group/project information.""" @@ -54,7 +53,7 @@ def __init__( def get_auth_handler(self) -> Union[CloudAuth, QuantumAuth]: """Returns the respective authentication handler.""" - if self.channel == Channel.IBM_CLOUD: + if self.channel == "ibm_cloud": return CloudAuth(api_key=self.token, crn=self.instance) return QuantumAuth(access_token=self.token) diff --git a/qiskit_ibm_runtime/channel.py b/qiskit_ibm_runtime/channel.py deleted file mode 100644 index 6a6a6c1c8..000000000 --- a/qiskit_ibm_runtime/channel.py +++ /dev/null @@ -1,22 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Channel types.""" - -from enum import Enum - - -class Channel(str, Enum): - """Channel types""" - - IBM_QUANTUM = "ibm_quantum" - IBM_CLOUD = "ibm_cloud" diff --git a/qiskit_ibm_runtime/ibm_runtime_service.py b/qiskit_ibm_runtime/ibm_runtime_service.py index 89b72917b..0ee41afdc 100644 --- a/qiskit_ibm_runtime/ibm_runtime_service.py +++ b/qiskit_ibm_runtime/ibm_runtime_service.py @@ -30,7 +30,6 @@ from .api.clients.runtime import RuntimeClient from .api.exceptions import RequestsApiError from .constants import QISKIT_IBM_RUNTIME_API_URL -from .channel import Channel from .exceptions import IBMNotAuthorizedError, IBMInputValueError, IBMAccountError from .exceptions import ( IBMRuntimeError, @@ -189,7 +188,7 @@ def __init__( self._programs: Dict[str, RuntimeProgram] = {} self._backends: Dict[str, "ibm_backend.IBMBackend"] = {} - if self._channel == Channel.IBM_CLOUD: + if self._channel == "ibm_cloud": self._api_client = RuntimeClient(self._client_params) # TODO: We can make the backend discovery lazy self._backends = self._discover_cloud_backends() @@ -215,10 +214,10 @@ def __init__( @staticmethod def _auth_warning() -> None: warnings.warn( - f"Use of `auth` parameter is deprecated and will " - f"be removed in a future release. " - f"You can now use channel='{Channel.IBM_CLOUD}' or " - f"channel='{Channel.IBM_QUANTUM}' instead.", + "Use of `auth` parameter is deprecated and will " + "be removed in a future release. " + "You can now use channel='ibm_cloud' or " + "channel='ibm_quantum' instead.", DeprecationWarning, stacklevel=3, ) @@ -248,15 +247,9 @@ def _discover_account( elif auth or channel: if auth and auth not in ["legacy", "cloud"]: raise ValueError("'auth' can only be 'cloud' or 'legacy'") - if channel and channel not in [channel.value for channel in Channel]: - raise ValueError( - f"'channel' can only be '{Channel.IBM_CLOUD}' " - f"or '{Channel.IBM_QUANTUM}'" - ) - if auth == "cloud": - channel = Channel.IBM_CLOUD - elif auth == "legacy": - channel = Channel.IBM_QUANTUM + if channel and channel not in ["ibm_cloud", "ibm_quantum"]: + raise ValueError("'channel' can only be 'ibm_cloud' or 'ibm_quantum'") + self._get_channel_for_auth(auth=auth) if token: account = Account( channel=channel, @@ -289,7 +282,7 @@ def _discover_account( account.verify = verify # resolve CRN if needed - if account.channel == Channel.IBM_CLOUD: + if account.channel == "ibm_cloud": self._resolve_crn(account) # ensure account is valid, fail early if not @@ -526,7 +519,7 @@ def backends( IBMInputValueError: If an input is invalid. """ # TODO filter out input_allowed not having runtime - if self._channel == Channel.IBM_QUANTUM: + if self._channel == "ibm_quantum": if instance: backends = list(self._get_hgp(instance=instance).backends.values()) else: @@ -575,7 +568,7 @@ def delete_account( """ if auth: IBMRuntimeService._auth_warning() - channel = channel or IBMRuntimeService._get_channel_for_auth(auth) + channel = channel or IBMRuntimeService._get_channel_for_auth(auth=auth) return AccountManager.delete(name=name, channel=channel) @@ -583,8 +576,8 @@ def delete_account( def _get_channel_for_auth(auth: str) -> str: """Returns channel type based on auth""" if auth == "legacy": - return Channel.IBM_QUANTUM - return Channel.IBM_CLOUD + return "ibm_quantum" + return "ibm_cloud" @staticmethod def save_account( @@ -864,7 +857,7 @@ def run( RuntimeProgramNotFound: If the program cannot be found. IBMRuntimeError: An error occurred running the program. """ - if instance and self._channel != Channel.IBM_QUANTUM: + if instance and self._channel != "ibm_quantum": raise IBMInputValueError( "The 'instance' keyword is only supported for ``ibm_quantum`` runtime. " ) @@ -883,7 +876,7 @@ def run( backend = None hgp_name = None - if self._channel == Channel.IBM_QUANTUM: + if self._channel == "ibm_quantum": # Find the right hgp hgp = self._get_hgp(instance=instance, backend_name=options.backend_name) backend = hgp.backend(options.backend_name) @@ -1234,7 +1227,7 @@ def jobs( """ hub = group = project = None if instance: - if self._channel == Channel.IBM_CLOUD: + if self._channel == "ibm_cloud": raise IBMInputValueError( "The 'instance' keyword is only supported for ``ibm_quantum`` runtime." ) @@ -1394,7 +1387,7 @@ def auth(self) -> str: The authentication type used. """ self._auth_warning() - return "cloud" if self._channel == Channel.IBM_CLOUD else "legacy" + return "cloud" if self._channel == "ibm_cloud" else "legacy" @property def channel(self) -> str: diff --git a/qiskit_ibm_runtime/runtime_options.py b/qiskit_ibm_runtime/runtime_options.py index e44b6f7a2..a28795698 100644 --- a/qiskit_ibm_runtime/runtime_options.py +++ b/qiskit_ibm_runtime/runtime_options.py @@ -17,7 +17,6 @@ from dataclasses import dataclass from typing import Optional -from .channel import Channel from .exceptions import IBMInputValueError @@ -54,7 +53,7 @@ def validate(self, channel: str) -> None: ): raise IBMInputValueError('"image" needs to be in form of image_name:tag') - if channel == Channel.IBM_QUANTUM and not self.backend_name: + if channel == "ibm_quantum" and not self.backend_name: raise IBMInputValueError( '"backend_name" is required field in "options" for ``ibm_quantum`` runtime.' ) diff --git a/qiskit_ibm_runtime/utils/utils.py b/qiskit_ibm_runtime/utils/utils.py index 50622f161..f7d4fd975 100644 --- a/qiskit_ibm_runtime/utils/utils.py +++ b/qiskit_ibm_runtime/utils/utils.py @@ -24,7 +24,6 @@ import requests from ibm_cloud_sdk_core.authenticators import IAMAuthenticator from ibm_platform_services import ResourceControllerV2 -from ..channel import Channel def get_iam_api_url(cloud_url: str) -> str: @@ -41,7 +40,7 @@ def get_resource_controller_api_url(cloud_url: str) -> str: def resolve_crn(channel: str, url: str, instance: str, token: str) -> List[str]: """Resolves the Cloud Resource Name (CRN) for the given cloud account.""" - if channel != Channel.IBM_CLOUD: + if channel != "ibm_cloud": raise ValueError("CRN value can only be resolved for cloud accounts.") if is_crn(instance): diff --git a/test/account.py b/test/account.py index 8596c2b4f..a30d4761e 100644 --- a/test/account.py +++ b/test/account.py @@ -21,7 +21,6 @@ from qiskit_ibm_runtime.accounts import management from qiskit_ibm_runtime.accounts.account import IBM_CLOUD_API_URL, IBM_QUANTUM_API_URL -from qiskit_ibm_runtime.channel import Channel class custom_envs(ContextDecorator): @@ -128,7 +127,7 @@ def __exit__(self, *exc): def get_account_config_contents( name=None, - channel=Channel.IBM_CLOUD, + channel="ibm_cloud", token=None, url=None, instance=None, @@ -137,18 +136,16 @@ def get_account_config_contents( ): """Generate qiskitrc content""" if instance is None: - instance = ( - "some_instance" if channel == Channel.IBM_CLOUD else "hub/group/project" - ) + instance = "some_instance" if channel == "ibm_cloud" else "hub/group/project" token = token or uuid.uuid4().hex if name is None: name = ( management._DEFAULT_ACCOUNT_NAME_IBM_CLOUD - if channel == Channel.IBM_CLOUD + if channel == "ibm_cloud" else management._DEFAULT_ACCOUNT_NAME_IBM_QUANTUM ) if url is None: - url = IBM_CLOUD_API_URL if channel == Channel.IBM_CLOUD else IBM_QUANTUM_API_URL + url = IBM_CLOUD_API_URL if channel == "ibm_cloud" else IBM_QUANTUM_API_URL out = { name: { "channel": channel, diff --git a/test/decorators.py b/test/decorators.py index 88ce32a0c..278a1c99c 100644 --- a/test/decorators.py +++ b/test/decorators.py @@ -19,7 +19,7 @@ from unittest import SkipTest from qiskit_ibm_runtime import IBMRuntimeService -from qiskit_ibm_runtime.channel import Channel + from .unit.mock.fake_runtime_service import FakeRuntimeService @@ -29,10 +29,10 @@ def run_quantum_and_cloud_fake(func): @wraps(func) def _wrapper(self, *args, **kwargs): ibm_quantum_service = FakeRuntimeService( - channel=Channel.IBM_QUANTUM.value, token="my_token", instance="h/g/p" + channel="ibm_quantum", token="my_token", instance="h/g/p" ) cloud_service = FakeRuntimeService( - channel=Channel.IBM_CLOUD.value, + channel="ibm_cloud", token="my_token", instance="crn:v1:bluemix:public:quantum-computing:my-region:a/...:...::", ) @@ -51,9 +51,7 @@ def _get_integration_test_config(): os.getenv("QISKIT_IBM_INSTANCE"), ) channel: Any = ( - Channel.IBM_QUANTUM.value - if url.find("quantum-computing.ibm.com") >= 0 - else Channel.IBM_CLOUD.value + "ibm_quantum" if url.find("quantum-computing.ibm.com") >= 0 else "ibm_cloud" ) return channel, token, url, instance @@ -92,7 +90,7 @@ def _decorator(func): @wraps(func) def _wrapper(self, *args, **kwargs): _supported_channel = ( - [channel.value for channel in Channel] + ["ibm_cloud", "ibm_quantum"] if supported_channel is None else supported_channel ) diff --git a/test/e2e/test_tutorials.py b/test/e2e/test_tutorials.py index 89b427351..74bdcfcdc 100644 --- a/test/e2e/test_tutorials.py +++ b/test/e2e/test_tutorials.py @@ -20,7 +20,7 @@ from nbconvert.preprocessors import ExecutePreprocessor from qiskit_ibm_runtime.utils.utils import to_python_identifier -from qiskit_ibm_runtime.channel import Channel + from ..ibm_test_case import IBMIntegrationTestCase TUTORIAL_PATH = "docs/tutorials/**/*.ipynb" @@ -41,7 +41,7 @@ def _is_supported(channel: str, tutorial_filename: str) -> bool: targeted environment.""" allowlist = ( SUPPORTED_TUTORIALS_IBM_QUANTUM - if channel == Channel.IBM_QUANTUM + if channel == "ibm_quantum" else SUPPORTED_TUTORIALS_IBM_CLOUD ) return any(tutorial_filename.endswith(filename) for filename in allowlist) diff --git a/test/integration/test_account.py b/test/integration/test_account.py index 0287fcdd8..d4d2fb622 100644 --- a/test/integration/test_account.py +++ b/test/integration/test_account.py @@ -18,7 +18,6 @@ from qiskit_ibm_runtime import IBMRuntimeService from qiskit_ibm_runtime.accounts import CloudResourceNameResolutionError -from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.utils.utils import ( get_resource_controller_api_url, get_iam_api_url, @@ -51,8 +50,8 @@ class TestIntegrationAccount(IBMIntegrationTestCase): """Integration tests for account management.""" def _skip_on_ibm_quantum(self): - if self.dependencies.channel == Channel.IBM_QUANTUM: - self.skipTest(f"Not supported on {Channel.IBM_QUANTUM}") + if self.dependencies.channel == "ibm_quantum": + self.skipTest("Not supported on ibm_quantum") def test_resolve_crn_for_valid_service_instance_name(self): """Verify if CRN is transparently resolved based for an existing service instance name.""" @@ -61,7 +60,7 @@ def test_resolve_crn_for_valid_service_instance_name(self): service_instance_name = _get_service_instance_name_for_crn(self.dependencies) with self.subTest(instance=service_instance_name): service = IBMRuntimeService( - channel=Channel.IBM_CLOUD, + channel="ibm_cloud", url=self.dependencies.url, token=self.dependencies.token, instance=service_instance_name, @@ -80,7 +79,7 @@ def test_resolve_crn_for_invalid_service_instance_name(self): CloudResourceNameResolutionError ): IBMRuntimeService( - channel=Channel.IBM_CLOUD, + channel="ibm_cloud", url=self.dependencies.url, token=self.dependencies.token, instance=service_instance_name, diff --git a/test/integration/test_auth_client.py b/test/integration/test_auth_client.py index 0028b2bb6..0fce1d77c 100644 --- a/test/integration/test_auth_client.py +++ b/test/integration/test_auth_client.py @@ -14,7 +14,6 @@ import re -from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.api.client_parameters import ClientParameters from qiskit_ibm_runtime.api.clients import AuthClient from qiskit_ibm_runtime.api.exceptions import ApiError @@ -25,41 +24,41 @@ class TestAuthClient(IBMTestCase): """Tests for the AuthClient.""" - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_valid_login(self, dependencies: IntegrationTestDependencies) -> None: """Test valid authentication.""" client = self._init_auth_client(dependencies.token, dependencies.url) self.assertTrue(client.access_token) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_url_404(self, dependencies: IntegrationTestDependencies) -> None: """Test login against a 404 URL""" url_404 = re.sub(r"/api.*$", "/api/TEST_404", dependencies.url) with self.assertRaises(ApiError): _ = self._init_auth_client(dependencies.token, url_404) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_invalid_token(self, dependencies: IntegrationTestDependencies) -> None: """Test login using invalid token.""" qe_token = "INVALID_TOKEN" with self.assertRaises(ApiError): _ = self._init_auth_client(qe_token, dependencies.url) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_url_unreachable(self, dependencies: IntegrationTestDependencies) -> None: """Test login against an invalid (malformed) URL.""" qe_url = "INVALID_URL" with self.assertRaises(ApiError): _ = self._init_auth_client(dependencies.token, qe_url) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_api_version(self, dependencies: IntegrationTestDependencies) -> None: """Check the version of the QX API.""" client = self._init_auth_client(dependencies.token, dependencies.url) version = client.api_version() self.assertIsNotNone(version) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_user_urls(self, dependencies: IntegrationTestDependencies) -> None: """Check the user urls of the QX API.""" client = self._init_auth_client(dependencies.token, dependencies.url) @@ -67,7 +66,7 @@ def test_user_urls(self, dependencies: IntegrationTestDependencies) -> None: self.assertIsNotNone(user_urls) self.assertTrue("http" in user_urls and "ws" in user_urls) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_user_hubs(self, dependencies: IntegrationTestDependencies) -> None: """Check the user hubs of the QX API.""" client = self._init_auth_client(dependencies.token, dependencies.url) @@ -81,5 +80,5 @@ def test_user_hubs(self, dependencies: IntegrationTestDependencies) -> None: def _init_auth_client(self, token, url): """Return an AuthClient.""" - params = ClientParameters(channel=Channel.IBM_QUANTUM, token=token, url=url) + params = ClientParameters(channel="ibm_quantum", token=token, url=url) return AuthClient(params) diff --git a/test/integration/test_backend.py b/test/integration/test_backend.py index c6929187f..52ed519b9 100644 --- a/test/integration/test_backend.py +++ b/test/integration/test_backend.py @@ -15,7 +15,7 @@ from unittest import SkipTest from qiskit.transpiler.target import Target -from qiskit_ibm_runtime.channel import Channel + from ..ibm_test_case import IBMIntegrationTestCase from ..decorators import run_integration_test @@ -52,10 +52,10 @@ def setUpClass(cls): # pylint: disable=arguments-differ # pylint: disable=no-value-for-parameter super().setUpClass() - if cls.dependencies.channel == Channel.IBM_CLOUD: + if cls.dependencies.channel == "ibm_cloud": # TODO use real device when cloud supports it cls.backend = cls.dependencies.service.least_busy(min_num_qubits=5) - if cls.dependencies.channel == Channel.IBM_QUANTUM: + if cls.dependencies.channel == "ibm_quantum": cls.backend = cls.dependencies.service.least_busy( simulator=False, min_num_qubits=5, instance=cls.dependencies.instance ) diff --git a/test/integration/test_backend_serialization.py b/test/integration/test_backend_serialization.py index e6bdb8090..967409c21 100644 --- a/test/integration/test_backend_serialization.py +++ b/test/integration/test_backend_serialization.py @@ -16,7 +16,6 @@ import dateutil.parser -from qiskit_ibm_runtime.channel import Channel from ..ibm_test_case import IBMIntegrationTestCase from ..decorators import ( run_integration_test, @@ -30,9 +29,7 @@ class TestSerialization(IBMIntegrationTestCase): def test_backend_configuration(self, service): """Test deserializing backend configuration.""" instance = ( - self.dependencies.instance - if service.channel == Channel.IBM_QUANTUM - else None + self.dependencies.instance if service.channel == "ibm_quantum" else None ) backends = service.backends( operational=True, simulator=False, instance=instance @@ -62,9 +59,7 @@ def test_backend_configuration(self, service): def test_pulse_defaults(self, service): """Test deserializing backend configuration.""" instance = ( - self.dependencies.instance - if service.channel == Channel.IBM_QUANTUM - else None + self.dependencies.instance if service.channel == "ibm_quantum" else None ) backends = service.backends( operational=True, open_pulse=True, instance=instance @@ -83,9 +78,7 @@ def test_pulse_defaults(self, service): def test_backend_properties(self, service): """Test deserializing backend properties.""" instance = ( - self.dependencies.instance - if service.channel == Channel.IBM_QUANTUM - else None + self.dependencies.instance if service.channel == "ibm_quantum" else None ) backends = service.backends( operational=True, simulator=False, instance=instance diff --git a/test/integration/test_basic_server_paths.py b/test/integration/test_basic_server_paths.py index 4e1098c88..ea4f79c2c 100644 --- a/test/integration/test_basic_server_paths.py +++ b/test/integration/test_basic_server_paths.py @@ -12,7 +12,6 @@ """Tests that hit all the basic server endpoints using both a public and premium h/g/p.""" -from qiskit_ibm_runtime.channel import Channel from ..ibm_test_case import IBMTestCase from ..decorators import integration_test_setup, IntegrationTestDependencies @@ -21,7 +20,7 @@ class TestBasicServerPaths(IBMTestCase): """Test the basic server endpoints using both a public and premium provider.""" @classmethod - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM]) + @integration_test_setup(supported_channel=["ibm_quantum"]) def setUpClass(cls, dependencies: IntegrationTestDependencies) -> None: # pylint: disable=arguments-differ super().setUpClass() diff --git a/test/integration/test_job.py b/test/integration/test_job.py index c8a11f4a3..4ae42bdbf 100644 --- a/test/integration/test_job.py +++ b/test/integration/test_job.py @@ -18,7 +18,6 @@ from qiskit.providers.jobstatus import JOB_FINAL_STATES, JobStatus from qiskit.test.decorators import slow_test -from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.constants import API_TO_JOB_ERROR_MESSAGE from qiskit_ibm_runtime.exceptions import ( RuntimeJobFailureError, @@ -60,8 +59,8 @@ def test_run_program_real_device(self, service): def test_run_program_cloud_no_backend(self, service): """Test running a cloud program with no backend.""" - if self.dependencies.channel == Channel.IBM_QUANTUM: - self.skipTest(f"Not supported on {Channel.IBM_QUANTUM}") + if self.dependencies.channel == "ibm_quantum": + self.skipTest("Not supported on ibm_quantum") job = self._run_program(service, backend="") self.assertTrue(job.backend, f"Job {job.job_id} has no backend.") diff --git a/test/integration/test_proxies.py b/test/integration/test_proxies.py index 36f1d5e62..7feb3119f 100644 --- a/test/integration/test_proxies.py +++ b/test/integration/test_proxies.py @@ -21,7 +21,6 @@ from qiskit_ibm_runtime.api.client_parameters import ClientParameters from qiskit_ibm_runtime.api.clients import AuthClient, VersionClient from qiskit_ibm_runtime.api.clients.runtime import RuntimeClient -from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.api.exceptions import RequestsApiError from qiskit_ibm_runtime.proxies import ProxyConfiguration from ..ibm_test_case import IBMTestCase @@ -56,7 +55,7 @@ def tearDown(self): # wait for the process to terminate self.proxy_process.wait() - @integration_test_setup(supported_channel=[Channel.IBM_CLOUD]) + @integration_test_setup(supported_channel=["ibm_cloud"]) def test_proxies_cloud_runtime_client( self, dependencies: IntegrationTestDependencies ) -> None: @@ -71,13 +70,13 @@ def test_proxies_cloud_runtime_client( proxy_output = self.proxy_process.stdout.read().decode("utf-8") self.assertIn(api_line, proxy_output) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_proxies_ibm_quantum_runtime_client( self, dependencies: IntegrationTestDependencies ) -> None: """Should reach the proxy using RuntimeClient.""" service = IBMRuntimeService( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token=dependencies.token, url=dependencies.url, proxies={"urls": VALID_PROXIES}, @@ -95,13 +94,13 @@ def test_proxies_ibm_quantum_runtime_client( # Check if the API call (querying providers list) went through proxy. self.assertIn(api_line, proxy_output) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_proxies_account_client( self, dependencies: IntegrationTestDependencies ) -> None: """Should reach the proxy using AccountClient.""" service = IBMRuntimeService( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token=dependencies.token, url=dependencies.url, proxies={"urls": VALID_PROXIES}, @@ -119,7 +118,7 @@ def test_proxies_account_client( # Check if the API call (querying providers list) went through proxy. self.assertIn(api_line, proxy_output) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_proxies_authclient( self, dependencies: IntegrationTestDependencies ) -> None: @@ -128,7 +127,7 @@ def test_proxies_authclient( dependencies.url ) params = ClientParameters( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls=VALID_PROXIES), @@ -142,7 +141,7 @@ def test_proxies_authclient( self.proxy_process.stdout.read().decode("utf-8"), ) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_proxies_versionclient( self, dependencies: IntegrationTestDependencies ) -> None: @@ -160,13 +159,13 @@ def test_proxies_versionclient( self.proxy_process.stdout.read().decode("utf-8"), ) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_invalid_proxy_port_runtime_client( self, dependencies: IntegrationTestDependencies ) -> None: """Should raise RequestApiError with ProxyError using RuntimeClient.""" params = ClientParameters( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls=INVALID_PORT_PROXIES), @@ -176,13 +175,13 @@ def test_invalid_proxy_port_runtime_client( client.list_programs(limit=1) self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_invalid_proxy_port_authclient( self, dependencies: IntegrationTestDependencies ) -> None: """Should raise RequestApiError with ProxyError using AuthClient.""" params = ClientParameters( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls=INVALID_PORT_PROXIES), @@ -192,7 +191,7 @@ def test_invalid_proxy_port_authclient( self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_invalid_proxy_port_versionclient( self, dependencies: IntegrationTestDependencies ) -> None: @@ -205,13 +204,13 @@ def test_invalid_proxy_port_versionclient( self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_invalid_proxy_address_runtime_client( self, dependencies: IntegrationTestDependencies ) -> None: """Should raise RequestApiError with ProxyError using RuntimeClient.""" params = ClientParameters( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls=INVALID_ADDRESS_PROXIES), @@ -222,13 +221,13 @@ def test_invalid_proxy_address_runtime_client( self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_invalid_proxy_address_authclient( self, dependencies: IntegrationTestDependencies ) -> None: """Should raise RequestApiError with ProxyError using AuthClient.""" params = ClientParameters( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls=INVALID_ADDRESS_PROXIES), @@ -238,7 +237,7 @@ def test_invalid_proxy_address_authclient( self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_invalid_proxy_address_versionclient( self, dependencies: IntegrationTestDependencies ) -> None: @@ -251,7 +250,7 @@ def test_invalid_proxy_address_versionclient( self.assertIsInstance(context_manager.exception.__cause__, ProxyError) - @integration_test_setup(supported_channel=[Channel.IBM_QUANTUM], init_service=False) + @integration_test_setup(supported_channel=["ibm_quantum"], init_service=False) def test_proxy_urls(self, dependencies: IntegrationTestDependencies) -> None: """Test different forms of the proxy urls.""" test_urls = [ @@ -262,7 +261,7 @@ def test_proxy_urls(self, dependencies: IntegrationTestDependencies) -> None: for proxy_url in test_urls: with self.subTest(proxy_url=proxy_url): params = ClientParameters( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token=dependencies.token, url=dependencies.url, proxies=ProxyConfiguration(urls={"https": proxy_url}), diff --git a/test/integration/test_retrieve_job.py b/test/integration/test_retrieve_job.py index f892d4b29..ee94c4bc2 100644 --- a/test/integration/test_retrieve_job.py +++ b/test/integration/test_retrieve_job.py @@ -16,7 +16,6 @@ from qiskit.providers.jobstatus import JobStatus -from qiskit_ibm_runtime.channel import Channel from ..ibm_test_case import IBMIntegrationJobTestCase from ..decorators import run_integration_test from ..utils import wait_for_status, get_real_device @@ -131,8 +130,8 @@ def test_retrieve_jobs_by_program_id(self, service): @run_integration_test def test_jobs_filter_by_hgp(self, service): """Test retrieving jobs by hgp.""" - if self.dependencies.channel == Channel.IBM_CLOUD: - self.skipTest(f"Not supported on {Channel.IBM_CLOUD}") + if self.dependencies.channel == "ibm_cloud": + self.skipTest("Not supported on ibm_cloud") default_hgp = list(service._hgps.keys())[0] program_id = self._upload_program(service) diff --git a/test/unit/mock/fake_runtime_client.py b/test/unit/mock/fake_runtime_client.py index e9eb5e819..01606da05 100644 --- a/test/unit/mock/fake_runtime_client.py +++ b/test/unit/mock/fake_runtime_client.py @@ -21,7 +21,6 @@ from typing import Optional, Dict, Any from qiskit_ibm_runtime.api.exceptions import RequestsApiError -from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.utils import RuntimeEncoder from qiskit_ibm_runtime.utils.hgp import from_instance_format from .fake_account_client import BaseFakeAccountClient @@ -32,10 +31,8 @@ def cloud_only(func): @wraps(func) def _wrapper(self, *args, **kwargs): - if self._channel != Channel.IBM_CLOUD: - raise ValueError( - f"Method {func} called by an {Channel.IBM_QUANTUM} client!" - ) + if self._channel != "ibm_cloud": + raise ValueError(f"Method {func} called by an ibm_quantum client!") return func(self, *args, **kwargs) return _wrapper @@ -267,7 +264,7 @@ def __init__(self, *args, **kwargs): self._backend_client = test_options.get( "backend_client", BaseFakeAccountClient() ) - self._channel = test_options.get("channel", Channel.IBM_QUANTUM) + self._channel = test_options.get("channel", "ibm_quantum") def set_job_classes(self, classes): """Set job classes to use.""" @@ -517,5 +514,5 @@ def backend_pulse_defaults(self, backend_name: str) -> Dict[str, Any]: @cloud_only def _check_cloud_only(self): - if self._channel != Channel.IBM_CLOUD: + if self._channel != "ibm_cloud": raise ValueError("A backend method is called by an ibm_quantum client!") diff --git a/test/unit/mock/fake_runtime_service.py b/test/unit/mock/fake_runtime_service.py index 692ee0c44..eacac8270 100644 --- a/test/unit/mock/fake_runtime_service.py +++ b/test/unit/mock/fake_runtime_service.py @@ -17,7 +17,6 @@ from unittest import mock from qiskit_ibm_runtime.accounts import Account -from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.api.client_parameters import ClientParameters from qiskit_ibm_runtime.api.clients import AuthClient from qiskit_ibm_runtime.hub_group_project import HubGroupProject @@ -74,7 +73,7 @@ def _initialize_hgps( hgp_name = self.DEFAULT_HGPS[idx] hgp_params = ClientParameters( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token="some_token", url="some_url", instance=hgp_name, @@ -114,7 +113,7 @@ def _discover_cloud_backends(self): test_options = { "backend_client": self._fake_account_client, - "channel": Channel.IBM_CLOUD, + "channel": "ibm_cloud", } self._api_client = BaseFakeRuntimeClient(test_options=test_options) return super()._discover_cloud_backends() diff --git a/test/unit/test_account.py b/test/unit/test_account.py index ec42fdd78..4adee3a48 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -26,7 +26,6 @@ AccountNotFoundError, InvalidAccountError, ) -from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.accounts.account import IBM_CLOUD_API_URL, IBM_QUANTUM_API_URL from qiskit_ibm_runtime.accounts.management import ( _DEFAULT_ACCOUNT_NAME_LEGACY, @@ -45,14 +44,14 @@ ) _TEST_IBM_QUANTUM_ACCOUNT = Account( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token="token-x", url="https://auth.quantum-computing.ibm.com/api", instance="ibm-q/open/main", ) _TEST_IBM_CLOUD_ACCOUNT = Account( - channel=Channel.IBM_CLOUD, + channel="ibm_cloud", token="token-y", url="https://cloud.ibm.com", instance="crn:v1:bluemix:public:quantum-computing:us-east:a/...::", @@ -114,7 +113,7 @@ def test_invalid_token(self): with self.subTest(token=token): with self.assertRaises(InvalidAccountError) as err: Account( - channel=Channel.IBM_CLOUD, + channel="ibm_cloud", token=token, url=self.dummy_ibm_cloud_url, ).validate() @@ -124,7 +123,7 @@ def test_invalid_url(self): """Test invalid values for url parameter.""" subtests = [ - {"channel": Channel.IBM_CLOUD, "url": 123}, + {"channel": "ibm_cloud", "url": 123}, ] for params in subtests: with self.subTest(params=params): @@ -136,9 +135,9 @@ def test_invalid_instance(self): """Test invalid values for instance parameter.""" subtests = [ - {"channel": Channel.IBM_CLOUD, "instance": ""}, - {"channel": Channel.IBM_CLOUD}, - {"channel": Channel.IBM_QUANTUM, "instance": "no-hgp-format"}, + {"channel": "ibm_cloud", "instance": ""}, + {"channel": "ibm_cloud"}, + {"channel": "ibm_quantum", "instance": "no-hgp-format"}, ] for params in subtests: with self.subTest(params=params): @@ -167,7 +166,7 @@ def test_invalid_proxy_config(self): with self.assertRaises(ValueError) as err: Account( **params, - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token=self.dummy_token, url=self.dummy_ibm_cloud_url, ).validate() @@ -190,7 +189,7 @@ def test_save_without_overwrite(self): token=_TEST_IBM_CLOUD_ACCOUNT.token, url=_TEST_IBM_CLOUD_ACCOUNT.url, instance=_TEST_IBM_CLOUD_ACCOUNT.instance, - channel=Channel.IBM_CLOUD, + channel="ibm_cloud", overwrite=False, ) @@ -207,7 +206,7 @@ def test_save_channel_ibm_cloud_over_auth_cloud_without_overwrite(self): token=_TEST_IBM_CLOUD_ACCOUNT.token, url=_TEST_IBM_CLOUD_ACCOUNT.url, instance=_TEST_IBM_CLOUD_ACCOUNT.instance, - channel=Channel.IBM_CLOUD, + channel="ibm_cloud", name=None, overwrite=False, ) @@ -225,7 +224,7 @@ def test_save_channel_ibm_quantum_over_auth_legacy_without_overwrite(self): token=_TEST_IBM_QUANTUM_ACCOUNT.token, url=_TEST_IBM_QUANTUM_ACCOUNT.url, instance=_TEST_IBM_QUANTUM_ACCOUNT.instance, - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", name=None, overwrite=False, ) @@ -242,12 +241,12 @@ def test_save_channel_ibm_quantum_over_auth_legacy_with_overwrite(self): token=_TEST_IBM_QUANTUM_ACCOUNT.token, url=_TEST_IBM_QUANTUM_ACCOUNT.url, instance=_TEST_IBM_QUANTUM_ACCOUNT.instance, - channel=Channel.IBM_QUANTUM.value, + channel="ibm_quantum", name=None, overwrite=True, ) self.assertEqual( - _TEST_IBM_QUANTUM_ACCOUNT, AccountManager.get(channel=Channel.IBM_QUANTUM) + _TEST_IBM_QUANTUM_ACCOUNT, AccountManager.get(channel="ibm_quantum") ) # TODO remove test when removing auth parameter @@ -262,13 +261,13 @@ def test_save_channel_ibm_cloud_over_auth_cloud_with_overwrite(self): token=_TEST_IBM_CLOUD_ACCOUNT.token, url=_TEST_IBM_CLOUD_ACCOUNT.url, instance=_TEST_IBM_CLOUD_ACCOUNT.instance, - channel=Channel.IBM_CLOUD.value, + channel="ibm_cloud", proxies=_TEST_IBM_CLOUD_ACCOUNT.proxies, name=None, overwrite=True, ) self.assertEqual( - _TEST_IBM_CLOUD_ACCOUNT, AccountManager.get(channel=Channel.IBM_CLOUD) + _TEST_IBM_CLOUD_ACCOUNT, AccountManager.get(channel="ibm_cloud") ) # TODO remove test when removing auth parameter @@ -280,7 +279,7 @@ def test_save_channel_ibm_cloud_with_name_over_auth_cloud_with_overwrite(self): token=_TEST_IBM_CLOUD_ACCOUNT.token, url=_TEST_IBM_CLOUD_ACCOUNT.url, instance=_TEST_IBM_CLOUD_ACCOUNT.instance, - channel=Channel.IBM_CLOUD.value, + channel="ibm_cloud", proxies=_TEST_IBM_CLOUD_ACCOUNT.proxies, name="personal-account", overwrite=True, @@ -299,7 +298,7 @@ def test_save_channel_ibm_cloud_with_name_over_auth_cloud_without_overwrite(self token=_TEST_IBM_CLOUD_ACCOUNT.token, url=_TEST_IBM_CLOUD_ACCOUNT.url, instance=_TEST_IBM_CLOUD_ACCOUNT.instance, - channel=Channel.IBM_CLOUD.value, + channel="ibm_cloud", proxies=_TEST_IBM_CLOUD_ACCOUNT.proxies, name="personal-account", overwrite=False, @@ -314,7 +313,7 @@ def test_save_channel_ibm_quantum_with_name_over_auth_legacy_with_overwrite(self token=_TEST_IBM_QUANTUM_ACCOUNT.token, url=_TEST_IBM_QUANTUM_ACCOUNT.url, instance=_TEST_IBM_QUANTUM_ACCOUNT.instance, - channel=Channel.IBM_QUANTUM.value, + channel="ibm_quantum", proxies=_TEST_IBM_QUANTUM_ACCOUNT.proxies, name="personal-account", overwrite=True, @@ -335,7 +334,7 @@ def test_save_channel_ibm_quantum_with_name_over_auth_legacy_without_overwrite( token=_TEST_IBM_QUANTUM_ACCOUNT.token, url=_TEST_IBM_QUANTUM_ACCOUNT.url, instance=_TEST_IBM_QUANTUM_ACCOUNT.instance, - channel=Channel.IBM_QUANTUM.value, + channel="ibm_quantum", proxies=_TEST_IBM_QUANTUM_ACCOUNT.proxies, name="personal-account", overwrite=False, @@ -441,29 +440,29 @@ def test_list(self): "key1": _TEST_IBM_CLOUD_ACCOUNT.to_saved_format(), "key2": _TEST_IBM_QUANTUM_ACCOUNT.to_saved_format(), _DEFAULT_ACCOUNT_NAME_IBM_CLOUD: Account( - Channel.IBM_CLOUD, "token-ibm-cloud", instance="crn:123" + "ibm_cloud", "token-ibm-cloud", instance="crn:123" ).to_saved_format(), _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM: Account( - Channel.IBM_QUANTUM, "token-ibm-quantum" + "ibm_quantum", "token-ibm-quantum" ).to_saved_format(), } ), self.subTest("filtered list of accounts"): - accounts = list(AccountManager.list(channel=Channel.IBM_CLOUD).keys()) + accounts = list(AccountManager.list(channel="ibm_cloud").keys()) self.assertEqual(len(accounts), 2) self.assertListEqual(accounts, ["key1", _DEFAULT_ACCOUNT_NAME_IBM_CLOUD]) - accounts = list(AccountManager.list(channel=Channel.IBM_QUANTUM).keys()) + accounts = list(AccountManager.list(channel="ibm_quantum").keys()) self.assertEqual(len(accounts), 2) self.assertListEqual(accounts, ["key2", _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM]) accounts = list( - AccountManager.list(channel=Channel.IBM_CLOUD, default=True).keys() + AccountManager.list(channel="ibm_cloud", default=True).keys() ) self.assertEqual(len(accounts), 1) self.assertListEqual(accounts, [_DEFAULT_ACCOUNT_NAME_IBM_CLOUD]) accounts = list( - AccountManager.list(channel=Channel.IBM_CLOUD, default=False).keys() + AccountManager.list(channel="ibm_cloud", default=False).keys() ) self.assertEqual(len(accounts), 1) self.assertListEqual(accounts, ["key1"]) @@ -481,22 +480,22 @@ def test_list(self): _DEFAULT_ACCOUNT_NAME_LEGACY: _TEST_LEGACY_ACCOUNT, } ), self.subTest("filtered list of auth accounts"): - accounts = list(AccountManager.list(channel=Channel.IBM_CLOUD).keys()) + accounts = list(AccountManager.list(channel="ibm_cloud").keys()) self.assertEqual(len(accounts), 2) self.assertListEqual(accounts, ["key1", _DEFAULT_ACCOUNT_NAME_IBM_CLOUD]) - accounts = list(AccountManager.list(channel=Channel.IBM_QUANTUM).keys()) + accounts = list(AccountManager.list(channel="ibm_quantum").keys()) self.assertEqual(len(accounts), 2) self.assertListEqual(accounts, ["key2", _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM]) accounts = list( - AccountManager.list(channel=Channel.IBM_CLOUD, default=True).keys() + AccountManager.list(channel="ibm_cloud", default=True).keys() ) self.assertEqual(len(accounts), 1) self.assertListEqual(accounts, [_DEFAULT_ACCOUNT_NAME_IBM_CLOUD]) accounts = list( - AccountManager.list(channel=Channel.IBM_CLOUD, default=False).keys() + AccountManager.list(channel="ibm_cloud", default=False).keys() ) self.assertEqual(len(accounts), 1) self.assertListEqual(accounts, ["key1"]) @@ -520,7 +519,7 @@ def test_delete(self): self.assertFalse(AccountManager.delete(name="key1")) with self.subTest("delete default ibm_quantum account"): - self.assertTrue(AccountManager.delete(channel=Channel.IBM_QUANTUM)) + self.assertTrue(AccountManager.delete(channel="ibm_quantum")) with self.subTest("delete default ibm_cloud account"): self.assertTrue(AccountManager.delete()) @@ -542,7 +541,7 @@ def test_delete_auth(self): self.assertFalse(AccountManager.delete(name="key1")) with self.subTest("delete default auth='legacy' account using channel"): - self.assertTrue(AccountManager.delete(channel=Channel.IBM_QUANTUM)) + self.assertTrue(AccountManager.delete(channel="ibm_quantum")) with self.subTest("delete default auth='cloud' account using channel"): self.assertTrue(AccountManager.delete()) @@ -570,7 +569,7 @@ def test_enable_account_by_name(self): def test_enable_account_by_channel(self): """Test initializing account by channel.""" - for channel in [channel.value for channel in Channel]: + for channel in ["ibm_cloud", "ibm_quantum"]: with self.subTest(channel=channel), no_envs(["QISKIT_IBM_TOKEN"]): token = uuid.uuid4().hex with temporary_account_config_file(channel=channel, token=token): @@ -594,10 +593,10 @@ def test_enable_account_by_token_url(self): def test_enable_account_by_name_and_other(self): """Test initializing account by name and other.""" subtests = [ - {"channel": Channel.IBM_CLOUD}, + {"channel": "ibm_cloud"}, {"token": "some_token"}, {"url": "some_url"}, - {"channel": Channel.IBM_CLOUD, "token": "some_token", "url": "some_url"}, + {"channel": "ibm_cloud", "token": "some_token", "url": "some_url"}, ] name = "foo" @@ -621,9 +620,7 @@ def test_enable_cloud_account_by_channel_token_url(self): with self.subTest(url=url), no_envs(["QISKIT_IBM_TOKEN"]): token = uuid.uuid4().hex with self.assertRaises(InvalidAccountError) as err: - _ = FakeRuntimeService( - channel=Channel.IBM_CLOUD, token=token, url=url - ) + _ = FakeRuntimeService(channel="ibm_cloud", token=token, url=url) self.assertIn("instance", str(err.exception)) def test_enable_ibm_quantum_account_by_channel_token_url(self): @@ -633,7 +630,7 @@ def test_enable_ibm_quantum_account_by_channel_token_url(self): with self.subTest(url=url), no_envs(["QISKIT_IBM_TOKEN"]): token = uuid.uuid4().hex service = FakeRuntimeService( - channel=Channel.IBM_QUANTUM, token=token, url=url + channel="ibm_quantum", token=token, url=url ) self.assertTrue(service._account) self.assertEqual(service._account.token, token) @@ -641,7 +638,7 @@ def test_enable_ibm_quantum_account_by_channel_token_url(self): def test_enable_account_by_channel_url(self): """Test initializing ibm_quantum account by channel, token, url.""" - subtests = [channel.value for channel in Channel] + subtests = ["ibm_cloud", "ibm_quantum"] for channel in subtests: with self.subTest(channel=channel): token = uuid.uuid4().hex @@ -656,16 +653,14 @@ def test_enable_account_by_channel_url(self): self.assertTrue(service._account) self.assertEqual(service._account.token, token) expected = ( - IBM_CLOUD_API_URL - if channel == Channel.IBM_CLOUD - else IBM_QUANTUM_API_URL + IBM_CLOUD_API_URL if channel == "ibm_cloud" else IBM_QUANTUM_API_URL ) self.assertEqual(service._account.url, expected) self.assertIn("url", logged.output[0]) def test_enable_account_by_only_channel(self): """Test initializing account with single saved account.""" - subtests = [channel.value for channel in Channel] + subtests = ["ibm_cloud", "ibm_quantum"] for channel in subtests: with self.subTest(channel=channel): token = uuid.uuid4().hex @@ -676,9 +671,7 @@ def test_enable_account_by_only_channel(self): self.assertTrue(service._account) self.assertEqual(service._account.token, token) expected = ( - IBM_CLOUD_API_URL - if channel == Channel.IBM_CLOUD - else IBM_QUANTUM_API_URL + IBM_CLOUD_API_URL if channel == "ibm_cloud" else IBM_QUANTUM_API_URL ) self.assertEqual(service._account.url, expected) self.assertEqual(service._account.channel, channel) @@ -686,11 +679,9 @@ def test_enable_account_by_only_channel(self): def test_enable_account_both_channel(self): """Test initializing account with both saved types.""" token = uuid.uuid4().hex - contents = get_account_config_contents(channel=Channel.IBM_CLOUD, token=token) + contents = get_account_config_contents(channel="ibm_cloud", token=token) contents.update( - get_account_config_contents( - channel=Channel.IBM_QUANTUM, token=uuid.uuid4().hex - ) + get_account_config_contents(channel="ibm_quantum", token=uuid.uuid4().hex) ) with temporary_account_config_file(contents=contents), no_envs( ["QISKIT_IBM_TOKEN"] @@ -699,11 +690,11 @@ def test_enable_account_both_channel(self): self.assertTrue(service._account) self.assertEqual(service._account.token, token) self.assertEqual(service._account.url, IBM_CLOUD_API_URL) - self.assertEqual(service._account.channel, Channel.IBM_CLOUD) + self.assertEqual(service._account.channel, "ibm_cloud") def test_enable_account_by_env_channel(self): """Test initializing account by environment variable and channel.""" - subtests = [Channel.IBM_QUANTUM, Channel.IBM_CLOUD, None] + subtests = ["ibm_quantum", "ibm_cloud", None] for channel in subtests: with self.subTest(channel=channel): token = uuid.uuid4().hex @@ -712,7 +703,7 @@ def test_enable_account_by_env_channel(self): "QISKIT_IBM_TOKEN": token, "QISKIT_IBM_URL": url, "QISKIT_IBM_INSTANCE": "h/g/p" - if channel == Channel.IBM_QUANTUM + if channel == "ibm_quantum" else "crn:12", } with custom_envs(envs): @@ -721,7 +712,7 @@ def test_enable_account_by_env_channel(self): self.assertTrue(service._account) self.assertEqual(service._account.token, token) self.assertEqual(service._account.url, url) - channel = channel or Channel.IBM_CLOUD + channel = channel or "ibm_cloud" self.assertEqual(service._account.channel, channel) def test_enable_account_by_env_token_url(self): @@ -782,7 +773,7 @@ def test_enable_account_by_channel_pref(self): {"instance": "h/g/p"}, {"proxies": MOCK_PROXY_CONFIG_DICT, "verify": False, "instance": "h/g/p"}, ] - for channel in [channel.value for channel in Channel]: + for channel in ["ibm_cloud", "ibm_quantum"]: for extra in subtests: with self.subTest( channel=channel, extra=extra @@ -830,8 +821,8 @@ def test_enable_account_by_name_input_instance(self): def test_enable_account_by_channel_input_instance(self): """Test initializing account by channel and input instance.""" instance = uuid.uuid4().hex - with temporary_account_config_file(channel=Channel.IBM_CLOUD, instance="bla"): - service = FakeRuntimeService(channel=Channel.IBM_CLOUD, instance=instance) + with temporary_account_config_file(channel="ibm_cloud", instance="bla"): + service = FakeRuntimeService(channel="ibm_cloud", instance=instance) self.assertTrue(service._account) self.assertEqual(service._account.instance, instance) @@ -844,7 +835,7 @@ def test_enable_account_by_env_input_instance(self): "QISKIT_IBM_INSTANCE": "some_instance", } with custom_envs(envs): - service = FakeRuntimeService(channel=Channel.IBM_CLOUD, instance=instance) + service = FakeRuntimeService(channel="ibm_cloud", instance=instance) self.assertTrue(service._account) self.assertEqual(service._account.instance, instance) diff --git a/test/unit/test_account_client.py b/test/unit/test_account_client.py index c0e065012..84ff8310f 100644 --- a/test/unit/test_account_client.py +++ b/test/unit/test_account_client.py @@ -15,7 +15,7 @@ from qiskit_ibm_runtime.api.client_parameters import ClientParameters from qiskit_ibm_runtime.api.clients import AccountClient from qiskit_ibm_runtime.api.exceptions import RequestsApiError -from qiskit_ibm_runtime.channel import Channel + from .mock.http_server import SimpleServer, ClientErrorHandler from ..ibm_test_case import IBMTestCase from ..account import custom_envs, no_envs @@ -39,7 +39,7 @@ def _get_client(self): """Helper for instantiating an AccountClient.""" # pylint: disable=no-value-for-parameter params = ClientParameters( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", url=SimpleServer.URL, token="foo", instance="h/g/p", diff --git a/test/unit/test_backend_retrieval.py b/test/unit/test_backend_retrieval.py index 4f13993b6..652057c99 100644 --- a/test/unit/test_backend_retrieval.py +++ b/test/unit/test_backend_retrieval.py @@ -15,7 +15,6 @@ from qiskit.providers.exceptions import QiskitBackendNotFoundError from qiskit.test.mock.backends import FakeLima -from qiskit_ibm_runtime.channel import Channel from .mock.fake_account_client import BaseFakeAccountClient from .mock.fake_runtime_service import FakeRuntimeService from ..ibm_test_case import IBMTestCase @@ -45,7 +44,7 @@ def test_filter_by_name(self, service): def test_filter_by_instance_ibm_quantum(self): """Test filtering by instance (works only on ibm_quantum).""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") for hgp in FakeRuntimeService.DEFAULT_HGPS: with self.subTest(hgp=hgp): backends = service.backends(instance=hgp) @@ -156,7 +155,7 @@ def test_filter_by_hgp(self): "num_hgps": 2, } ibm_quantum_service = FakeRuntimeService( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token="my_token", instance="h/g/p", test_options=test_options, @@ -181,13 +180,13 @@ def _get_services(self, fake_backends): """Get both ibm_cloud and ibm_quantum services initialized with fake backends.""" test_options = {"account_client": BaseFakeAccountClient(specs=fake_backends)} ibm_quantum_service = FakeRuntimeService( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token="my_token", instance="h/g/p", test_options=test_options, ) cloud_service = FakeRuntimeService( - channel=Channel.IBM_CLOUD, + channel="ibm_cloud", token="my_token", instance="my_instance", test_options=test_options, @@ -200,27 +199,27 @@ class TestGetBackend(IBMTestCase): def test_get_common_backend(self): """Test getting a backend that is in default and non-default hgp.""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") backend = service.backend(FakeRuntimeService.DEFAULT_COMMON_BACKEND) self.assertEqual(backend._api_client.hgp, list(service._hgps.keys())[0]) def test_get_unique_backend_default_hgp(self): """Test getting a backend in the default hgp.""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") backend_name = FakeRuntimeService.DEFAULT_UNIQUE_BACKEND_PREFIX + "0" backend = service.backend(backend_name) self.assertEqual(backend._api_client.hgp, list(service._hgps.keys())[0]) def test_get_unique_backend_non_default_hgp(self): """Test getting a backend in the non default hgp.""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") backend_name = FakeRuntimeService.DEFAULT_UNIQUE_BACKEND_PREFIX + "1" backend = service.backend(backend_name) self.assertEqual(backend._api_client.hgp, list(service._hgps.keys())[1]) def test_get_phantom_backend(self): """Test getting a phantom backend.""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") with self.assertRaises(QiskitBackendNotFoundError): service.backend("phantom") @@ -228,7 +227,7 @@ def test_get_backend_by_hgp(self): """Test getting a backend by hgp.""" hgp = FakeRuntimeService.DEFAULT_HGPS[1] backend_name = FakeRuntimeService.DEFAULT_COMMON_BACKEND - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") backend = service.backend(backend_name, instance=hgp) self.assertEqual(backend._api_client.hgp, hgp) @@ -236,6 +235,6 @@ def test_get_backend_by_bad_hgp(self): """Test getting a backend not in hgp.""" hgp = FakeRuntimeService.DEFAULT_HGPS[1] backend_name = FakeRuntimeService.DEFAULT_UNIQUE_BACKEND_PREFIX + "0" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") with self.assertRaises(QiskitBackendNotFoundError): _ = service.backend(backend_name, instance=hgp) diff --git a/test/unit/test_client_parameters.py b/test/unit/test_client_parameters.py index 5c144c8cf..65dc2315a 100644 --- a/test/unit/test_client_parameters.py +++ b/test/unit/test_client_parameters.py @@ -15,7 +15,7 @@ import uuid from requests_ntlm import HttpNtlmAuth -from qiskit_ibm_runtime.channel import Channel + from qiskit_ibm_runtime.proxies import ProxyConfiguration from qiskit_ibm_runtime.api.client_parameters import ClientParameters from qiskit_ibm_runtime.api.auth import CloudAuth, QuantumAuth @@ -54,19 +54,19 @@ def test_get_runtime_api_base_url(self) -> None: """Test resolution of runtime API base URL.""" test_specs = [ ( - Channel.IBM_CLOUD.value, + "ibm_cloud", "crn:v1:bluemix:public:quantum-computing:us-east:a/...:...::", "https://cloud.ibm.com", "https://us-east.quantum-computing.cloud.ibm.com", ), ( - Channel.IBM_CLOUD.value, + "ibm_cloud", "crn:v1:bluemix:public:quantum-computing:my-region:a/...:...::", "https://cloud.ibm.com", "https://my-region.quantum-computing.cloud.ibm.com", ), ( - Channel.IBM_QUANTUM.value, + "ibm_quantum", "h/g/p", "https://auth.quantum-computing.ibm.com/api", "https://auth.quantum-computing.ibm.com/api", @@ -126,7 +126,7 @@ def test_malformed_ntlm_params(self) -> None: def test_auth_handler_quantum(self): """Test getting quantum auth handler.""" token = uuid.uuid4().hex - params = self._get_client_params(channel=Channel.IBM_QUANTUM, token=token) + params = self._get_client_params(channel="ibm_quantum", token=token) handler = params.get_auth_handler() self.assertIsInstance(handler, QuantumAuth) self.assertIn(token, handler.get_headers().values()) @@ -136,7 +136,7 @@ def test_auth_handler_cloud(self): token = uuid.uuid4().hex instance = uuid.uuid4().hex params = self._get_client_params( - channel=Channel.IBM_CLOUD, token=token, instance=instance + channel="ibm_cloud", token=token, instance=instance ) handler = params.get_auth_handler() self.assertIsInstance(handler, CloudAuth) @@ -145,7 +145,7 @@ def test_auth_handler_cloud(self): def _get_client_params( self, - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token="dummy_token", url="https://dummy_url", instance=None, diff --git a/test/unit/test_data_serialization.py b/test/unit/test_data_serialization.py index a0f980e8b..1f843337f 100644 --- a/test/unit/test_data_serialization.py +++ b/test/unit/test_data_serialization.py @@ -61,7 +61,6 @@ from qiskit.quantum_info import SparsePauliOp, Pauli, PauliTable, Statevector from qiskit.result import Result -from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.utils import RuntimeEncoder, RuntimeDecoder from .mock.fake_runtime_client import CustomResultRuntimeJob from .mock.fake_runtime_service import FakeRuntimeService @@ -289,7 +288,7 @@ def test_result_decoder(self): job_cls = CustomResultRuntimeJob job_cls.custom_result = custom_result ibm_quantum_service = FakeRuntimeService( - channel=Channel.IBM_QUANTUM, token="some_token" + channel="ibm_quantum", token="some_token" ) sub_tests = [(SerializableClassDecoder, None), (None, SerializableClassDecoder)] diff --git a/test/unit/test_job_retrieval.py b/test/unit/test_job_retrieval.py index d9e69ebf5..826c2a248 100644 --- a/test/unit/test_job_retrieval.py +++ b/test/unit/test_job_retrieval.py @@ -12,7 +12,6 @@ """Tests for runtime job retrieval.""" -from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.exceptions import IBMInputValueError from .mock.fake_runtime_service import FakeRuntimeService from ..ibm_test_case import IBMTestCase @@ -28,7 +27,7 @@ def setUp(self): """Initial test setup.""" super().setUp() self._ibm_quantum_service = FakeRuntimeService( - channel=Channel.IBM_QUANTUM, token="my_token" + channel="ibm_quantum", token="my_token" ) @run_quantum_and_cloud_fake @@ -221,7 +220,7 @@ def test_different_hgps(self): """Test retrieving job submitted with different hgp.""" # Initialize with hgp0 service = FakeRuntimeService( - channel=Channel.IBM_QUANTUM, + channel="ibm_quantum", token="some_token", instance=FakeRuntimeService.DEFAULT_HGPS[0], ) diff --git a/test/unit/test_jobs.py b/test/unit/test_jobs.py index 9345791e5..b949cc060 100644 --- a/test/unit/test_jobs.py +++ b/test/unit/test_jobs.py @@ -19,7 +19,6 @@ from qiskit.providers.jobstatus import JobStatus from qiskit_ibm_runtime import RuntimeJob -from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.constants import API_TO_JOB_ERROR_MESSAGE from qiskit_ibm_runtime.exceptions import ( RuntimeJobFailureError, @@ -72,14 +71,14 @@ def test_run_program_phantom_backend(self, service): def test_run_program_missing_backend_ibm_quantum(self): """Test running an ibm_quantum program with no backend.""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") with self.assertRaises(IBMInputValueError): _ = run_program(service=service, backend_name="") def test_run_program_missing_backend_ibm_cloud(self): """Test running an ibm_cloud program with no backend.""" service = FakeRuntimeService( - channel=Channel.IBM_CLOUD, + channel="ibm_cloud", token="my_token", instance="crn:v1:bluemix:public:quantum-computing:my-region:a/...:...::", ) @@ -88,7 +87,7 @@ def test_run_program_missing_backend_ibm_cloud(self): def test_run_program_default_hgp_backend(self): """Test running a program with a backend in default hgp.""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") backend = FakeRuntimeService.DEFAULT_COMMON_BACKEND default_hgp = list(service._hgps.values())[0] self.assertIn(backend, default_hgp.backends.keys()) @@ -100,7 +99,7 @@ def test_run_program_default_hgp_backend(self): def test_run_program_non_default_hgp_backend(self): """Test running a program with a backend in non-default hgp.""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") backend = FakeRuntimeService.DEFAULT_UNIQUE_BACKEND_PREFIX + "1" default_hgp = list(service._hgps.values())[0] self.assertNotIn(backend, default_hgp.backends.keys()) @@ -109,7 +108,7 @@ def test_run_program_non_default_hgp_backend(self): def test_run_program_by_hgp_backend(self): """Test running a program with both backend and hgp.""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") backend = FakeRuntimeService.DEFAULT_COMMON_BACKEND non_default_hgp = list(service._hgps.keys())[1] job = run_program( @@ -120,7 +119,7 @@ def test_run_program_by_hgp_backend(self): def test_run_program_by_hgp_bad_backend(self): """Test running a program with backend not in hgp.""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") backend = FakeRuntimeService.DEFAULT_UNIQUE_BACKEND_PREFIX + "1" default_hgp = list(service._hgps.values())[0] self.assertNotIn(backend, default_hgp.backends.keys()) @@ -131,13 +130,13 @@ def test_run_program_by_hgp_bad_backend(self): def test_run_program_by_phantom_hgp(self): """Test running a program with a phantom hgp.""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") with self.assertRaises(IBMInputValueError): _ = run_program(service=service, instance="h/g/p") def test_run_program_by_bad_hgp(self): """Test running a program with a bad hgp.""" - service = FakeRuntimeService(channel=Channel.IBM_QUANTUM, token="my_token") + service = FakeRuntimeService(channel="ibm_quantum", token="my_token") with self.assertRaises(IBMInputValueError): _ = run_program(service=service, instance="foo") diff --git a/test/unit/test_runtime_ws.py b/test/unit/test_runtime_ws.py index fcdfc2b4e..e71d79738 100644 --- a/test/unit/test_runtime_ws.py +++ b/test/unit/test_runtime_ws.py @@ -18,7 +18,6 @@ from qiskit_ibm_runtime import RuntimeJob from qiskit_ibm_runtime.api.client_parameters import ClientParameters -from qiskit_ibm_runtime.channel import Channel from qiskit_ibm_runtime.exceptions import RuntimeInvalidStateError from .mock.fake_runtime_client import BaseFakeRuntimeClient from .mock.ws_handler import ( @@ -188,7 +187,7 @@ def result_callback(job_id, interim_result): def _get_job(self, callback=None, job_id=JOB_ID_PROGRESS_DONE): """Get a runtime job.""" params = ClientParameters( - channel=Channel.IBM_QUANTUM, token="my_token", url=MockWsServer.VALID_WS_URL + channel="ibm_quantum", token="my_token", url=MockWsServer.VALID_WS_URL ) job = RuntimeJob( backend=FakeQasmSimulator(), diff --git a/test/utils.py b/test/utils.py index 0a045da46..573193ec8 100644 --- a/test/utils.py +++ b/test/utils.py @@ -26,7 +26,6 @@ from qiskit_ibm_runtime.ibm_backend import IBMBackend from qiskit_ibm_runtime.runtime_job import RuntimeJob from qiskit_ibm_runtime.exceptions import RuntimeInvalidStateError -from qiskit_ibm_runtime.channel import Channel def setup_test_logging(logger: logging.Logger, filename: str) -> None: @@ -88,7 +87,7 @@ def get_hgp(qe_token: str, qe_url: str, default: bool = True) -> HubGroupProject A HubGroupProject, as specified by `default`. """ service = IBMRuntimeService( - channel=Channel.IBM_QUANTUM, token=qe_token, url=qe_url + channel="ibm_quantum", token=qe_token, url=qe_url ) # Default hub/group/project. open_hgp = service._get_hgp() # Open access hgp hgp_to_return = open_hgp From 3d6b2b2a9ffc6d6b6d06e689a2e8fc6b396b4a1d Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Mon, 28 Mar 2022 14:39:19 -0400 Subject: [PATCH 23/28] Ban latest released click version to temp fix make style build https://github.com/Qiskit/qiskit-ibm-runtime/runs/5725271694?check_suite_focus=true#step:5:13 --- constraints.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constraints.txt b/constraints.txt index 15b72fd0b..78a76b110 100644 --- a/constraints.txt +++ b/constraints.txt @@ -1,2 +1,2 @@ -# 3.1.0 is buggy -Jinja2!=3.1.0 \ No newline at end of file +# 8.1.0 is buggy +click!=8.1.0 \ No newline at end of file From e6c778eaf92e00090f205119bbc86822a6fbd380 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Tue, 29 Mar 2022 00:51:40 -0400 Subject: [PATCH 24/28] Add update account function --- constraints.txt | 2 - qiskit_ibm_runtime/accounts/management.py | 70 ++++++++++++++--------- qiskit_ibm_runtime/accounts/storage.py | 20 ++----- qiskit_ibm_runtime/ibm_runtime_service.py | 12 +++- 4 files changed, 57 insertions(+), 47 deletions(-) diff --git a/constraints.txt b/constraints.txt index 78a76b110..e69de29bb 100644 --- a/constraints.txt +++ b/constraints.txt @@ -1,2 +0,0 @@ -# 8.1.0 is buggy -click!=8.1.0 \ No newline at end of file diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index 6932e02b8..9034f7935 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -48,13 +48,9 @@ def save( ) -> None: """Save account on disk.""" name = name or cls._get_default_account_name(channel) - old_name = None - if name in [_DEFAULT_ACCOUNT_NAME_IBM_CLOUD, _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM]: - old_name = cls._get_old_default_account_name(channel) return save_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=name, - old_name=old_name, overwrite=overwrite, config=Account( token=token, @@ -98,14 +94,7 @@ def _matching_default(account_name: str) -> bool: # load all accounts all_accounts = map( lambda kv: ( - kv[0] - if kv[0] - not in [_DEFAULT_ACCOUNT_NAME_CLOUD, _DEFAULT_ACCOUNT_NAME_LEGACY] - else ( - _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM - if kv[0] == _DEFAULT_ACCOUNT_NAME_LEGACY - else _DEFAULT_ACCOUNT_NAME_IBM_CLOUD - ), + kv[0], Account.from_saved_format(kv[1]), ), read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE).items(), @@ -160,7 +149,6 @@ def get( saved_account = read_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=cls._get_default_account_name(channel=channel), - old_name=cls._get_old_default_account_name(channel=channel), ) if saved_account is None: raise AccountNotFoundError(f"No default {channel} account saved.") @@ -169,11 +157,8 @@ def get( all_config = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE) for channel_type in _CHANNEL_TYPES: account_name = cls._get_default_account_name(channel=channel_type) - old_account_name = cls._get_old_default_account_name(channel=channel_type) if account_name in all_config: return Account.from_saved_format(all_config[account_name]) - if old_account_name in all_config: - return Account.from_saved_format(all_config[old_account_name]) raise AccountNotFoundError("Unable to find account.") @@ -186,15 +171,52 @@ def delete( """Delete account from disk.""" name = name or cls._get_default_account_name(channel) - old_name = None - if name in [_DEFAULT_ACCOUNT_NAME_IBM_CLOUD, _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM]: - old_name = cls._get_old_default_account_name(channel) return delete_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=name, - old_name=old_name, ) + @classmethod + def update(cls) -> dict: + """Update accounts on disk by removing `auth` and adding `channel`.""" + data = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE) + for key, value in data.items(): + if key == _DEFAULT_ACCOUNT_NAME_CLOUD: + value.pop("auth", None) + value.update(channel="ibm_cloud") + delete_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=key) + save_config( + filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, + name=_DEFAULT_ACCOUNT_NAME_IBM_CLOUD, + config=value, + overwrite=False, + ) + elif key == _DEFAULT_ACCOUNT_NAME_LEGACY: + value.pop("auth", None) + value.update(channel="ibm_quantum") + delete_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=key) + save_config( + filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, + name=_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM, + config=value, + overwrite=False, + ) + else: + if "auth" in value: + if value["auth"] == "cloud": + value.update(channel="ibm_cloud") + elif value["auth"] == "legacy": + value.update(channel="ibm_quantum") + value.pop("auth", None) + save_config( + filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, + name=key, + config=value, + overwrite=True, + ) + data = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE) + return data + @classmethod def _from_env_variables(cls, channel: Optional[ChannelType]) -> Optional[Account]: """Read account from environment variable.""" @@ -216,11 +238,3 @@ def _get_default_account_name(cls, channel: ChannelType) -> str: if channel == "ibm_quantum" else _DEFAULT_ACCOUNT_NAME_IBM_CLOUD ) - - @classmethod - def _get_old_default_account_name(cls, channel: ChannelType) -> str: - return ( - _DEFAULT_ACCOUNT_NAME_LEGACY - if channel == "ibm_quantum" - else _DEFAULT_ACCOUNT_NAME_CLOUD - ) diff --git a/qiskit_ibm_runtime/accounts/storage.py b/qiskit_ibm_runtime/accounts/storage.py index 5e32feeec..7e6fcdcee 100644 --- a/qiskit_ibm_runtime/accounts/storage.py +++ b/qiskit_ibm_runtime/accounts/storage.py @@ -21,9 +21,7 @@ logger = logging.getLogger(__name__) -def save_config( - filename: str, name: str, old_name: str, config: dict, overwrite: bool -) -> None: +def save_config(filename: str, name: str, config: dict, overwrite: bool) -> 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) @@ -31,7 +29,7 @@ def save_config( with open(filename, mode="r", encoding="utf-8") as json_in: data = json.load(json_in) - if (data.get(name) or (old_name and data.get(old_name))) and not overwrite: + if data.get(name) and not overwrite: raise AccountAlreadyExistsError( f"Named account ({name}) already exists. " f"Set overwrite=True to overwrite." @@ -39,15 +37,12 @@ def save_config( with open(filename, mode="w", encoding="utf-8") as json_out: data[name] = config - if old_name and old_name in data: - del data[old_name] json.dump(data, json_out, sort_keys=True, indent=4) def read_config( filename: str, name: Optional[str] = None, - old_name: Optional[str] = None, ) -> Optional[Dict]: """Read configuration data from a JSON file.""" logger.debug("Read configuration data for '%s' from '%s'", name, filename) @@ -59,16 +54,12 @@ def read_config( return data if name in data: return data[name] - if old_name in data: - return data[old_name] - return None def delete_config( filename: str, name: str, - old_name: str, ) -> bool: """Delete configuration data from a JSON file.""" @@ -78,12 +69,9 @@ def delete_config( with open(filename, mode="r", encoding="utf-8") as json_in: data = json.load(json_in) - if name in data or old_name in data: + if name in data: with open(filename, mode="w", encoding="utf-8") as json_out: - if name in data: - del data[name] - elif old_name in data: - del data[old_name] + del data[name] json.dump(data, json_out, sort_keys=True, indent=4) return True diff --git a/qiskit_ibm_runtime/ibm_runtime_service.py b/qiskit_ibm_runtime/ibm_runtime_service.py index 0ee41afdc..fe28f3eb2 100644 --- a/qiskit_ibm_runtime/ibm_runtime_service.py +++ b/qiskit_ibm_runtime/ibm_runtime_service.py @@ -249,7 +249,7 @@ def _discover_account( raise ValueError("'auth' can only be 'cloud' or 'legacy'") if channel and channel not in ["ibm_cloud", "ibm_quantum"]: raise ValueError("'channel' can only be 'ibm_cloud' or 'ibm_quantum'") - self._get_channel_for_auth(auth=auth) + channel = channel or self._get_channel_for_auth(auth=auth) if token: account = Account( channel=channel, @@ -625,6 +625,16 @@ def save_account( overwrite=overwrite, ) + @staticmethod + def update_account() -> dict: + """Updates all stored credentials with `channel` parameter + and removes `auth` parameter. + Returns: + The credentials in new format if updating is successful + or ``None`` otherwise. + """ + return AccountManager.update() + @staticmethod def saved_accounts( default: Optional[bool] = None, From 7b9672790702a7d4e6ddf6790464287e522aaf7c Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Tue, 29 Mar 2022 09:03:43 -0400 Subject: [PATCH 25/28] Update black --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index d44d342cd..6453d26b2 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -17,7 +17,7 @@ nbconvert>=5.3.1 qiskit-aer websockets>=8 scikit-quant;platform_system != 'Windows' -black==22.1.0 +black==22.3.0 coverage>=6.3 pylatexenc mthree From 2ec5f48e5a8c35a936e230b9a5770b9943808caf Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Tue, 29 Mar 2022 09:03:55 -0400 Subject: [PATCH 26/28] Fix docs build --- qiskit_ibm_runtime/ibm_runtime_service.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qiskit_ibm_runtime/ibm_runtime_service.py b/qiskit_ibm_runtime/ibm_runtime_service.py index fe28f3eb2..e06696c53 100644 --- a/qiskit_ibm_runtime/ibm_runtime_service.py +++ b/qiskit_ibm_runtime/ibm_runtime_service.py @@ -627,11 +627,10 @@ def save_account( @staticmethod def update_account() -> dict: - """Updates all stored credentials with `channel` parameter - and removes `auth` parameter. + """Updates all stored credentials with `channel` parameter and removes `auth` parameter. + Returns: - The credentials in new format if updating is successful - or ``None`` otherwise. + The credentials in new format if updating is successful or ``None`` otherwise. """ return AccountManager.update() From 16a46cf6823417aa7fba3153648c0034a797b4cb Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Tue, 29 Mar 2022 11:22:26 -0400 Subject: [PATCH 27/28] Call migrate function to update config file automatically vs have the user do it --- qiskit_ibm_runtime/accounts/management.py | 13 +++++++------ qiskit_ibm_runtime/ibm_runtime_service.py | 9 --------- test/unit/test_account.py | 4 ++-- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/qiskit_ibm_runtime/accounts/management.py b/qiskit_ibm_runtime/accounts/management.py index 9034f7935..f1afd5f5b 100644 --- a/qiskit_ibm_runtime/accounts/management.py +++ b/qiskit_ibm_runtime/accounts/management.py @@ -47,6 +47,7 @@ def save( overwrite: Optional[bool] = False, ) -> None: """Save account on disk.""" + cls.migrate() name = name or cls._get_default_account_name(channel) return save_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, @@ -71,6 +72,7 @@ def list( name: Optional[str] = None, ) -> Dict[str, Account]: """List all accounts saved on disk.""" + AccountManager.migrate() def _matching_name(account_name: str) -> bool: return name is None or name == account_name @@ -130,6 +132,7 @@ def get( Raises: AccountNotFoundError: If the input value cannot be found on disk. """ + cls.migrate() if name: saved_account = read_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, name=name @@ -169,7 +172,7 @@ def delete( channel: Optional[ChannelType] = None, ) -> bool: """Delete account from disk.""" - + cls.migrate() name = name or cls._get_default_account_name(channel) return delete_config( filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE, @@ -177,8 +180,8 @@ def delete( ) @classmethod - def update(cls) -> dict: - """Update accounts on disk by removing `auth` and adding `channel`.""" + def migrate(cls) -> None: + """Migrate accounts on disk by removing `auth` and adding `channel`.""" data = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE) for key, value in data.items(): if key == _DEFAULT_ACCOUNT_NAME_CLOUD: @@ -202,7 +205,7 @@ def update(cls) -> dict: overwrite=False, ) else: - if "auth" in value: + if hasattr(value, "auth"): if value["auth"] == "cloud": value.update(channel="ibm_cloud") elif value["auth"] == "legacy": @@ -214,8 +217,6 @@ def update(cls) -> dict: config=value, overwrite=True, ) - data = read_config(filename=_DEFAULT_ACCOUNT_CONFIG_JSON_FILE) - return data @classmethod def _from_env_variables(cls, channel: Optional[ChannelType]) -> Optional[Account]: diff --git a/qiskit_ibm_runtime/ibm_runtime_service.py b/qiskit_ibm_runtime/ibm_runtime_service.py index e06696c53..fd8283f53 100644 --- a/qiskit_ibm_runtime/ibm_runtime_service.py +++ b/qiskit_ibm_runtime/ibm_runtime_service.py @@ -625,15 +625,6 @@ def save_account( overwrite=overwrite, ) - @staticmethod - def update_account() -> dict: - """Updates all stored credentials with `channel` parameter and removes `auth` parameter. - - Returns: - The credentials in new format if updating is successful or ``None`` otherwise. - """ - return AccountManager.update() - @staticmethod def saved_accounts( default: Optional[bool] = None, diff --git a/test/unit/test_account.py b/test/unit/test_account.py index 4adee3a48..fd3dc2655 100644 --- a/test/unit/test_account.py +++ b/test/unit/test_account.py @@ -482,11 +482,11 @@ def test_list(self): ), self.subTest("filtered list of auth accounts"): accounts = list(AccountManager.list(channel="ibm_cloud").keys()) self.assertEqual(len(accounts), 2) - self.assertListEqual(accounts, ["key1", _DEFAULT_ACCOUNT_NAME_IBM_CLOUD]) + self.assertListEqual(accounts, [_DEFAULT_ACCOUNT_NAME_IBM_CLOUD, "key1"]) accounts = list(AccountManager.list(channel="ibm_quantum").keys()) self.assertEqual(len(accounts), 2) - self.assertListEqual(accounts, ["key2", _DEFAULT_ACCOUNT_NAME_IBM_QUANTUM]) + self.assertListEqual(accounts, [_DEFAULT_ACCOUNT_NAME_IBM_QUANTUM, "key2"]) accounts = list( AccountManager.list(channel="ibm_cloud", default=True).keys() From 7d8ee741130f1c35d81ba2959e588daf836e15ad Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Tue, 29 Mar 2022 12:56:26 -0400 Subject: [PATCH 28/28] Add release note --- ...channel-deprecate-auth-4e021efeb527818c.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 releasenotes/notes/add-channel-deprecate-auth-4e021efeb527818c.yaml diff --git a/releasenotes/notes/add-channel-deprecate-auth-4e021efeb527818c.yaml b/releasenotes/notes/add-channel-deprecate-auth-4e021efeb527818c.yaml new file mode 100644 index 000000000..089b27b2d --- /dev/null +++ b/releasenotes/notes/add-channel-deprecate-auth-4e021efeb527818c.yaml @@ -0,0 +1,17 @@ +--- +upgrade: + - | + A new parameter ``channel`` has now been added to :class:`qiskit_ibm_runtime.IBMRuntimeService` + class and also to methods like :meth:`~qiskit_ibm_runtime.IBMRuntimeService.save_account`, + :meth:`~qiskit_ibm_runtime.IBMRuntimeService.saved_accounts` and + :meth:`~qiskit_ibm_runtime.IBMRuntimeService.delete_account`. It can be set to ``ibm_quantum`` + or ``ibm_cloud`` to authenticate to either of the two different channels through which + Qiskit Runtime service is currently offered. ``channel`` replaces the ``auth`` parameter which + has now been deprecated. +deprecations: + - | + The ``auth`` parameter to :class:`qiskit_ibm_runtime.IBMRuntimeService` class + and also to methods like :meth:`~qiskit_ibm_runtime.IBMRuntimeService.save_account`, + :meth:`~qiskit_ibm_runtime.IBMRuntimeService.saved_accounts` and + :meth:`~qiskit_ibm_runtime.IBMRuntimeService.delete_account` has now been deprecated + and will be removed in a future release. Please use the new ``channel`` parameter instead.