Skip to content
This repository has been archived by the owner on Jul 28, 2023. It is now read-only.

Commit

Permalink
disable, save, and stored account (#212)
Browse files Browse the repository at this point in the history
* disable, save, and stored account

* Docstring and style fixes

* Fix moved context manager
  • Loading branch information
jyu00 authored and diego-plan9 committed Jun 28, 2019
1 parent b972f3b commit 5061c92
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 29 deletions.
71 changes: 62 additions & 9 deletions qiskit/providers/ibmq/ibmqfactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
from .accountprovider import AccountProvider
from .api_v2.clients import AuthClient, VersionClient
from .credentials import Credentials, discover_credentials
from .credentials.configrc import read_credentials_from_qiskitrc, remove_credentials
from .credentials.configrc import (read_credentials_from_qiskitrc,
remove_credentials,
store_credentials)
from .credentials.updater import update_credentials
from .exceptions import IBMQAccountError, IBMQApiUrlError, IBMQProviderError
from .ibmqprovider import IBMQProvider
Expand Down Expand Up @@ -97,9 +99,20 @@ def disable_account(self):
"""Disable the account in the current session.
Raises:
IBMQAccountError: if no account is in use in the session.
IBMQAccountError: if API 1 credentials are found, or if no account
is in use in the session.
"""
raise NotImplementedError
if self._v1_provider._accounts:
raise IBMQAccountError('An IBM Q Experience 1 account is enabled. '
'Please use IBMQ.disable_accounts() to '
'disable the account.')

if self._credentials is not None:
self._credentials = None
self._providers = OrderedDict()

else:
raise IBMQAccountError('No account is in use for this session.')

def load_account(self):
"""Authenticate against IBM Q Experience from stored credentials.
Expand All @@ -109,9 +122,27 @@ def load_account(self):
"""
raise NotImplementedError

def save_account(self):
"""Save an account to disk for future use."""
raise NotImplementedError
@staticmethod
def save_account(token, url=QX_AUTH_URL, overwrite=False, **kwargs):
"""Save the account to disk for future use.
Args:
token (str): IBM Q Experience API token.
url (str): URL for the IBM Q Experience auth server.
overwrite (bool): overwrite existing credentials.
**kwargs (dict):
* proxies (dict): Proxy configuration for the API.
* verify (bool): If False, ignores SSL certificates errors
Raises:
IBMQAccountError: if attempting to save an IBM Q Experience 1
account.
"""
if url != QX_AUTH_URL:
raise IBMQAccountError('IBM Q Experience 1 accounts are deprecated.')

credentials = Credentials(token, url, **kwargs)
store_credentials(credentials, overwrite=overwrite)

@staticmethod
def delete_account():
Expand All @@ -137,9 +168,31 @@ def delete_account():

remove_credentials(credentials)

def stored_account(self):
"""List the account stored on disk"""
raise NotImplementedError
@staticmethod
def stored_account():
"""List the account stored on disk.
Returns:
dict: dictionary with information about the account stored on disk.
Raises:
IBMQAccountError: if no valid API 2 account information found.
"""
stored_credentials = read_credentials_from_qiskitrc()
if not stored_credentials:
return {}

if (len(stored_credentials) > 1 or
list(stored_credentials.values())[0].url != QX_AUTH_URL):
raise IBMQAccountError('Credentials from the API 1 found. Please use '
'IBMQ.update_account() for updating your '
'stored credentials.')

credentials = list(stored_credentials.values())[0]
return {
'token': credentials.token,
'url': credentials.url
}

@staticmethod
def update_account(force=False):
Expand Down
21 changes: 21 additions & 0 deletions test/contextmanagers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

import os
from contextlib import contextmanager
from tempfile import NamedTemporaryFile

from qiskit.providers.ibmq.credentials import configrc


@contextmanager
Expand Down Expand Up @@ -56,3 +59,21 @@ def no_envs(vars_to_remove):
finally:
# Restore the original `os.environ`.
os.environ = os_environ_original


@contextmanager
def custom_qiskitrc(contents=b''):
"""Context manager that uses a temporary qiskitrc."""
# Create a temporary file with the contents.
tmp_file = NamedTemporaryFile()
tmp_file.write(contents)
tmp_file.flush()

# Temporarily modify the default location of the qiskitrc file.
default_qiskitrc_file_original = configrc.DEFAULT_QISKITRC_FILE
configrc.DEFAULT_QISKITRC_FILE = tmp_file.name
yield

# Delete the temporary file and restore the default location.
tmp_file.close()
configrc.DEFAULT_QISKITRC_FILE = default_qiskitrc_file_original
58 changes: 58 additions & 0 deletions test/ibmq/test_ibmq_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

"""Tests for the IBMQFactory."""

import os
import warnings
from unittest import skipIf

from qiskit.providers.ibmq.accountprovider import AccountProvider
from qiskit.providers.ibmq.exceptions import IBMQAccountError, IBMQApiUrlError
Expand All @@ -26,6 +28,7 @@
from ..decorators import (requires_qe_access,
requires_new_api_auth,
requires_classic_api)
from ..contextmanagers import custom_qiskitrc


API1_URL = 'https://quantumexperience.ng.bluemix.net/api'
Expand Down Expand Up @@ -160,3 +163,58 @@ def test_backends(self, qe_token, qe_url):
set(ibmq_provider_backend_names))
self.assertTrue(issubclass(warnings_list[0].category,
DeprecationWarning))


@skipIf(os.name == 'nt', 'Test not supported in Windows')
class TestIBMQFactoryAccountsOnDisk(QiskitTestCase):
"""Tests for the IBMQ account handling on disk."""

@classmethod
def setUpClass(cls):
cls.v2_token = 'API2_TOKEN'
cls.v1_token = 'API1_TOKEN'

def setUp(self):
super().setUp()

# Reference for saving accounts.
self.factory = IBMQFactory()
self.provider = IBMQProvider()

def test_save_account_v2(self):
"""Test saving an API 2 account."""
with custom_qiskitrc():
self.factory.save_account(self.v2_token, url=AUTH_URL)
stored_cred = self.factory.stored_account()

self.assertEqual(stored_cred['token'], self.v2_token)
self.assertEqual(stored_cred['url'], AUTH_URL)

def test_save_account_v1(self):
"""Test saving an API 1 account."""
with custom_qiskitrc():
with self.assertRaises(IBMQAccountError):
self.factory.save_account(self.v1_token, url=API1_URL)

def test_stored_account_v1(self):
"""Test listing a stored API 1 account."""
with custom_qiskitrc():
self.provider.save_account(self.v1_token, url=API1_URL)
with self.assertRaises(IBMQAccountError):
self.factory.stored_account()

def test_delete_account_v2(self):
"""Test deleting an API 2 account."""
with custom_qiskitrc():
self.factory.save_account(self.v2_token, url=AUTH_URL)
self.factory.delete_account()
stored_cred = self.factory.stored_account()

self.assertEqual(len(stored_cred), 0)

def test_delete_account_v1(self):
"""Test deleting an API 1 account."""
with custom_qiskitrc():
self.provider.save_account(self.v1_token, url=API1_URL)
with self.assertRaises(IBMQAccountError):
self.factory.delete_account()
22 changes: 2 additions & 20 deletions test/ibmq/test_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from qiskit.providers.ibmq import IBMQ
from qiskit.providers.ibmq.credentials import (
Credentials, configrc, discover_credentials, qconfig,
Credentials, discover_credentials, qconfig,
read_credentials_from_qiskitrc, store_credentials)
from qiskit.providers.ibmq.credentials.environ import VARIABLES_MAP
from qiskit.providers.ibmq.credentials.updater import update_credentials, QE2_AUTH_URL, QE2_URL
Expand All @@ -34,7 +34,7 @@
from qiskit.providers.ibmq.ibmqsingleprovider import IBMQSingleProvider
from qiskit.test import QiskitTestCase

from ..contextmanagers import custom_envs, no_envs
from ..contextmanagers import custom_envs, no_envs, custom_qiskitrc

IBMQ_TEMPLATE = 'https://localhost/api/Hubs/{}/Groups/{}/Projects/{}'

Expand Down Expand Up @@ -461,24 +461,6 @@ def side_effect(filename_):
patcher.stop()


@contextmanager
def custom_qiskitrc(contents=b''):
"""Context manager that uses a temporary qiskitrc."""
# Create a temporary file with the contents.
tmp_file = NamedTemporaryFile()
tmp_file.write(contents)
tmp_file.flush()

# Temporarily modify the default location of the qiskitrc file.
default_qiskitrc_file_original = configrc.DEFAULT_QISKITRC_FILE
configrc.DEFAULT_QISKITRC_FILE = tmp_file.name
yield

# Delete the temporary file and restore the default location.
tmp_file.close()
configrc.DEFAULT_QISKITRC_FILE = default_qiskitrc_file_original


@contextmanager
def custom_qconfig(contents=b''):
"""Context manager that uses a temporary qconfig.py."""
Expand Down

0 comments on commit 5061c92

Please sign in to comment.