diff --git a/qiskit/__init__.py b/qiskit/__init__.py index a32d77ef91b4..5824a5c61913 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -15,7 +15,6 @@ """Main Qiskit public functionality.""" - import pkgutil import sys import warnings @@ -49,28 +48,6 @@ _config = _user_config.get_config() -# Try to import the Aer provider if installed. -try: - from qiskit.providers.aer import Aer -except ImportError: - suppress_warnings = os.environ.get('QISKIT_SUPPRESS_PACKAGING_WARNINGS', '') - if suppress_warnings.upper() != 'Y': - if not _config.get('suppress_packaging_warnings') or suppress_warnings.upper() == 'N': - warnings.warn('Could not import the Aer provider from the qiskit-aer ' - 'package. Install qiskit-aer or check your installation.', - RuntimeWarning) -# Try to import the IBMQ provider if installed. -try: - from qiskit.providers.ibmq import IBMQ -except ImportError: - suppress_warnings = os.environ.get('QISKIT_SUPPRESS_PACKAGING_WARNINGS', '') - if suppress_warnings.upper() != 'Y': - if not _config.get('suppress_packaging_warnings') or suppress_warnings.upper() == 'N': - warnings.warn('Could not import the IBMQ provider from the ' - 'qiskit-ibmq-provider package. Install ' - 'qiskit-ibmq-provider or check your installation.', - RuntimeWarning) - # Moved to after IBMQ and Aer imports due to import issues # with other modules that check for IBMQ (tools) from qiskit.execute import execute # noqa @@ -88,3 +65,61 @@ "Using Qiskit with Python 3.6 is deprecated as of the 0.17.0 release. " "Support for running Qiskit with Python 3.6 will be removed in a " "future release.", DeprecationWarning) + + +class AerWrapper: + + def __init__(self): + self.aer = None + + def __bool__(self): + if self.aer is None: + try: + from qiskit.providers.aer import Aer + self.aer = Aer + except ImportError: + return False + return True + + def __getattr__(self, attr): + if not self.aer: + try: + from qiskit.providers.aer import Aer + self.aer = Aer + except ImportError: + raise ImportError('Could not import the Aer provider from the ' + 'qiskit-aer package. Install qiskit-aer or ' + 'check your installation.') + return getattr(self.aer, attr) + + + +class IBMQWrapper: + + def __init__(self): + self.ibmq = None + + def __bool__(self): + if self.ibmq is None: + try: + from qiskit.providers.ibmq import IBMQ + self.ibmq = IBMQ + except ImportError: + return False + return True + + def __getattr__(self, attr): + if not self.ibmq: + try: + from qiskit.providers.ibmq import IBMQ + self.ibmq = IBMQ + except ImportError: + raise ImportError('Could not import the IBMQ provider from the ' + 'qiskit-ibmq-provider package. Install ' + 'qiskit-ibmq-provider or check your ' + 'installation.') + return getattr(self.ibmq, attr) + + +Aer = AerWrapper() +IBMQ = IBMQWrapper() diff --git a/qiskit/test/mock/fake_backend.py b/qiskit/test/mock/fake_backend.py index 1c96936cfc72..0fa2491a65ea 100644 --- a/qiskit/test/mock/fake_backend.py +++ b/qiskit/test/mock/fake_backend.py @@ -23,7 +23,7 @@ from qiskit.exceptions import QiskitError try: - from qiskit import Aer + from qiskit.providers.aer import Aer HAS_AER = True except ImportError: HAS_AER = False diff --git a/qiskit/user_config.py b/qiskit/user_config.py index d609a41547cf..b9c0e6faab3c 100644 --- a/qiskit/user_config.py +++ b/qiskit/user_config.py @@ -41,7 +41,6 @@ class UserConfig: circuit_mpl_style = default circuit_mpl_style_path = ~/.qiskit: transpile_optimization_level = 1 - suppress_packaging_warnings = False parallel = False num_processes = 4 @@ -116,12 +115,6 @@ def read_config_file(self): self.settings['transpile_optimization_level'] = ( transpile_optimization_level) - # Parse package warnings - package_warnings = self.config_parser.getboolean( - 'default', 'suppress_packaging_warnings', fallback=False) - if package_warnings: - self.settings['suppress_packaging_warnings'] = package_warnings - # Parse parallel parallel_enabled = self.config_parser.getboolean( 'default', 'parallel', fallback=PARALLEL_DEFAULT) diff --git a/releasenotes/notes/lazy-load-provider-factory-bdfb3925a2514ef7.yaml b/releasenotes/notes/lazy-load-provider-factory-bdfb3925a2514ef7.yaml new file mode 100644 index 000000000000..7d8425558dc9 --- /dev/null +++ b/releasenotes/notes/lazy-load-provider-factory-bdfb3925a2514ef7.yaml @@ -0,0 +1,22 @@ +--- +upgrade: + - | + The ``qiskit.Aer`` and ``qiskit.IBMQ`` top level attributes are now lazy + loaded. This means that the objects will always now exist and warnings will + no longer be raised on import if ``qiskit-aer`` or ``qiskit-ibmq-provider`` + are not installed (or can't be found by Python). If you were checking for + the presence of ``qiskit-aer`` or ``qiskit-ibmq-provider`` using these + module attributes and explicitly comparing to ``None`` or looking for the + absence of the attribute this no longer will work because they are always + defined as an object now. You can either explicitly use + ``qiskit.providers.aer.Aer`` and ``qiskit.providers.ibmq.IBMQ`` or + check ``bool(qiskit.Aer)`` and ``bool(qiskit.IBMQ)`` instead to determine + if those providers are present. This change was necessary to avoid potential + import cycle issues between the qiskit packages and also to improve the + import time when Aer or IBMQ is not being used. + - | + The user config file option ``suppress_packaging_warnings`` option in the + user config file and the ``QISKIT_SUPPRESS_PACKAGING_WARNINGS`` environment + variable no longer has any effect and will be silently ignored. These + warnings have been removed and will no longer be emitted at import time + from the qiskit module. diff --git a/test/python/algorithms/test_vqe.py b/test/python/algorithms/test_vqe.py index 438b2f02a7b0..b62a2b30bc57 100644 --- a/test/python/algorithms/test_vqe.py +++ b/test/python/algorithms/test_vqe.py @@ -167,7 +167,7 @@ def test_with_aer_statevector(self): """Test VQE with Aer's statevector_simulator.""" try: # pylint: disable=import-outside-toplevel - from qiskit import Aer + from qiskit.providers.aer import Aer except Exception as ex: # pylint: disable=broad-except self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return @@ -190,7 +190,7 @@ def test_with_aer_qasm(self): """Test VQE with Aer's qasm_simulator.""" try: # pylint: disable=import-outside-toplevel - from qiskit import Aer + from qiskit.providers.aer import Aer except Exception as ex: # pylint: disable=broad-except self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return @@ -215,7 +215,7 @@ def test_with_aer_qasm_snapshot_mode(self): """Test the VQE using Aer's qasm_simulator snapshot mode.""" try: # pylint: disable=import-outside-toplevel - from qiskit import Aer + from qiskit.providers.aer import Aer except Exception as ex: # pylint: disable=broad-except self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return @@ -308,7 +308,7 @@ def test_vqe_expectation_select(self): """Test expectation selection with Aer's qasm_simulator.""" try: # pylint: disable=import-outside-toplevel - from qiskit import Aer + from qiskit.providers.aer import Aer except Exception as ex: # pylint: disable=broad-except self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return diff --git a/test/python/opflow/legacy/test_weighted_pauli_operator.py b/test/python/opflow/legacy/test_weighted_pauli_operator.py index 283dacd2a4dc..530b9bd75dd7 100644 --- a/test/python/opflow/legacy/test_weighted_pauli_operator.py +++ b/test/python/opflow/legacy/test_weighted_pauli_operator.py @@ -498,7 +498,7 @@ def test_evaluate_with_aer_mode(self): """ evaluate with aer mode test """ try: # pylint: disable=import-outside-toplevel - from qiskit import Aer + from qiskit.providers.aer import Aer except Exception as ex: # pylint: disable=broad-except self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return diff --git a/test/python/opflow/test_state_op_meas_evals.py b/test/python/opflow/test_state_op_meas_evals.py index 7268bc5e768e..8edf2c401827 100644 --- a/test/python/opflow/test_state_op_meas_evals.py +++ b/test/python/opflow/test_state_op_meas_evals.py @@ -63,7 +63,7 @@ def test_coefficients_correctly_propagated(self): """Test that the coefficients in SummedOp and states are correctly used.""" try: # pylint: disable=import-outside-toplevel - from qiskit import Aer + from qiskit.providers.aer import Aer except Exception as ex: # pylint: disable=broad-except self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return @@ -89,7 +89,7 @@ def test_is_measurement_correctly_propagated(self): """Test if is_measurement property of StateFn is propagated to converted StateFn.""" try: # pylint: disable=import-outside-toplevel - from qiskit import Aer + from qiskit.providers.aer import Aer except Exception as ex: # pylint: disable=broad-except self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return @@ -103,7 +103,7 @@ def test_parameter_binding_on_listop(self): """Test passing a ListOp with differing parameters works with the circuit sampler.""" try: # pylint: disable=import-outside-toplevel - from qiskit import Aer + from qiskit.providers.aer import Aer except Exception as ex: # pylint: disable=broad-except self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return diff --git a/test/python/test_user_config.py b/test/python/test_user_config.py index 9f70a9918b81..36dc3ee5cc3e 100644 --- a/test/python/test_user_config.py +++ b/test/python/test_user_config.py @@ -31,18 +31,6 @@ def test_empty_file_read(self): self.assertEqual({}, config.settings) - def test_invalid_suppress_packaging_warnings(self): - test_config = """ - [default] - suppress_packaging_warnings = 76 - """ - self.addCleanup(os.remove, self.file_path) - with open(self.file_path, 'w') as file: - file.write(test_config) - file.flush() - config = user_config.UserConfig(self.file_path) - self.assertRaises(ValueError, config.read_config_file) - def test_invalid_optimization_level(self): test_config = """ [default] @@ -100,37 +88,6 @@ def test_optimization_level_valid(self): 'parallel_enabled': user_config.PARALLEL_DEFAULT}, config.settings) - def test_valid_suppress_packaging_warnings_false(self): - test_config = """ - [default] - suppress_packaging_warnings = false - """ - self.addCleanup(os.remove, self.file_path) - with open(self.file_path, 'w') as file: - file.write(test_config) - file.flush() - config = user_config.UserConfig(self.file_path) - config.read_config_file() - self.assertEqual( - {'parallel_enabled': user_config.PARALLEL_DEFAULT}, - config.settings) - - def test_valid_suppress_packaging_warnings_true(self): - test_config = """ - [default] - suppress_packaging_warnings = true - """ - self.addCleanup(os.remove, self.file_path) - with open(self.file_path, 'w') as file: - file.write(test_config) - file.flush() - config = user_config.UserConfig(self.file_path) - config.read_config_file() - self.assertEqual( - {'parallel_enabled': user_config.PARALLEL_DEFAULT, - 'suppress_packaging_warnings': True}, - config.settings) - def test_invalid_num_processes(self): test_config = """ [default] @@ -197,6 +154,5 @@ def test_all_options_valid(self): 'circuit_mpl_style_path': ['~', '~/.qiskit'], 'transpile_optimization_level': 3, 'num_processes': 15, - 'parallel_enabled': False, - 'suppress_packaging_warnings': True}, + 'parallel_enabled': False}, config.settings)