From 2356149961bd25ae50bebc3c5fa9a1f0e8a8b586 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 18 Sep 2020 17:32:47 -0400 Subject: [PATCH 01/21] Remove namespace packaging and hardcode elements (attempt 2) Namespace packages are constant source of problems for users. The python packaging ecosystem around splitting packages across namespaces is fragile at the best of times and can often leave a you with an environment that isn't recoverable (especially when mixing install methods). There is also a performance hit whenever there is a piece of the namespace we allow external packages to extend since it requires doing a full python path search which can be slow depending on the backing I/O and the number of paths in sys.path for an environment. This commit starts the process of addressing this by removing the arbitrary namespace hook points and hard coding the element namespace maps via a custom import loader at the root of the namespace. This has 2 advantages it removes the use of namespace packages so the fragility and performance impact are fixed since every element will be renamed to use 'qiskit_' instead of 'qiskit.', but it also makes it explicit where we extend the namespace. The previous method allowed any package to extend qiskit.* and qiskit.providers.* with whatever they wanted. We'll need to coordinate updating the elements with this merging, because it is a breaking change for each element (although not for end users). A potential follow on is to add a plugin interface for 3rd party providers like what was proposed in #1465 so that we can make external providers externally discoverable without needing to add manual hook points moving forward (this was done for backwards compat with the aqt and honeywell provider). This is a second attempt at removing namespace packaging. The first attempt in PR #4767 was merged and had to be reverted because there were some circular import error issues that needed to be resolved. Since having this in terra blocks CI for all the qiskit elements a revert was necessary to unblock developement for the entire project while those were resolved. --- qiskit/__init__.py | 82 ++++++++++++++++++----------- qiskit/namespace.py | 60 +++++++++++++++++++++ qiskit/providers/__init__.py | 6 --- qiskit/tools/jupyter/job_watcher.py | 5 +- qiskit/version.py | 10 ++-- 5 files changed, 120 insertions(+), 43 deletions(-) create mode 100644 qiskit/namespace.py diff --git a/qiskit/__init__.py b/qiskit/__init__.py index dd34d09f4616..c2387172b724 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -12,11 +12,8 @@ # pylint: disable=wrong-import-order,invalid-name,wrong-import-position - """Main Qiskit public functionality.""" - -import pkgutil import sys import warnings import os @@ -24,37 +21,70 @@ # First, check for required Python and API version from . import util +# Extend namespace for backwards compat +from qiskit import namespace +new_meta_path = [] +new_meta_path.append(namespace.QiskitElementImport( + 'qiskit_aer', 'qiskit.providers.aer')) +new_meta_path.append(namespace.QiskitElementImport( + 'qiskit_ignis', 'qiskit.ignis')) +new_meta_path.append(namespace.QiskitElementImport( + 'qiskit_aqua', 'qiskit.aqua')) +new_meta_path.append(namespace.QiskitElementImport( + 'qiskit_aqua.chemistry', 'qiskit.chemistry')) +new_meta_path.append(namespace.QiskitElementImport( + 'qiskit_aqua.finance', 'qiskit.finance')) +new_meta_path.append(namespace.QiskitElementImport( + 'qiskit_aqua.ml', 'qiskit.ml')) +new_meta_path.append(namespace.QiskitElementImport( + 'qiskit_aqua.optimization', 'qiskit.optimization')) +new_meta_path.append(namespace.QiskitElementImport( + 'qiskit_ibmq_provider', 'qiskit.providers.ibmq')) +new_meta_path.append(namespace.QiskitElementImport( + 'qiskit_aqt_provider', 'qiskit.providers.aqt')) +new_meta_path.append(namespace.QiskitElementImport( + 'qiskit_honeywell_provider', 'qiskit.providers.honeywell')) +# Add Qiskit importers to meta_path before PathFinder in the default +# sys.meta_path to avoid the miss penalty on trying to import a module +# which does not exist +old_meta_path = sys.meta_path +sys.meta_path = old_meta_path[:-1] + new_meta_path + [old_meta_path[-1]] + # qiskit errors operator -from qiskit.exceptions import QiskitError +from qiskit.exceptions import QiskitError # noqa # The main qiskit operators -from qiskit.circuit import ClassicalRegister -from qiskit.circuit import QuantumRegister -from qiskit.circuit import AncillaRegister -from qiskit.circuit import QuantumCircuit +from qiskit.circuit import ClassicalRegister # noqa +from qiskit.circuit import QuantumRegister # noqa +from qiskit.circuit import AncillaRegister # noqa +from qiskit.circuit import QuantumCircuit # noqa # user config -from qiskit import user_config as _user_config +from qiskit import user_config as _user_config # noqa # The qiskit.extensions.x imports needs to be placed here due to the # mechanism for adding gates dynamically. -import qiskit.extensions -import qiskit.circuit.measure -import qiskit.circuit.reset - -# Allow extending this namespace. Please note that currently this line needs -# to be placed *before* the wrapper imports or any non-import code AND *before* -# importing the package you want to allow extensions for (in this case `backends`). -__path__ = pkgutil.extend_path(__path__, __name__) +import qiskit.extensions # noqa +import qiskit.circuit.measure # noqa +import qiskit.circuit.reset # noqa # Please note these are global instances, not modules. -from qiskit.providers.basicaer import BasicAer +from qiskit.providers.basicaer import BasicAer # noqa _config = _user_config.get_config() +from qiskit.execute import execute # noqa +from qiskit.compiler import transpile, assemble, schedule # noqa + +from .version import __version__ # noqa +from .version import _get_qiskit_versions # noqa + +__qiskit_version__ = _get_qiskit_versions() + # Try to import the Aer provider if installed. try: - from qiskit.providers.aer import Aer + from qiskit.providers import aer + Aer = aer.Aer except ImportError: suppress_warnings = os.environ.get('QISKIT_SUPPRESS_PACKAGING_WARNINGS', '') if suppress_warnings.upper() != 'Y': @@ -62,9 +92,11 @@ 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 + from qiskit.providers import ibmq + IBMQ = ibmq.IBMQ except ImportError: suppress_warnings = os.environ.get('QISKIT_SUPPRESS_PACKAGING_WARNINGS', '') if suppress_warnings.upper() != 'Y': @@ -74,13 +106,3 @@ '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 -from qiskit.compiler import transpile, assemble, schedule # noqa - -from .version import __version__ # noqa -from .version import _get_qiskit_versions # noqa - - -__qiskit_version__ = _get_qiskit_versions() diff --git a/qiskit/namespace.py b/qiskit/namespace.py new file mode 100644 index 000000000000..33a7fae79b93 --- /dev/null +++ b/qiskit/namespace.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# 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. + +# pylint: disable=unused-argument + +"""Module for utilities to manually construct qiskit namespace""" + +import sys +from importlib.abc import MetaPathFinder, Loader +import importlib + + +class QiskitLoader(Loader): + """Load qiskit element as a namespace package.""" + def __init__(self, new_package, old_namespace): + super().__init__() + self.new_package = new_package + self.old_namespace = old_namespace + + def module_repr(self, module): + return repr(module) + + def load_module(self, fullname): + old_name = fullname + names = fullname.split(".") + new_namespace_names = self.new_package.split('.') + old_namespace_names = self.old_namespace.split('.') + fullname = ".".join( + new_namespace_names + names[len(old_namespace_names):]) + module = importlib.import_module(fullname) + sys.modules[old_name] = module + return module + + +class QiskitElementImport(MetaPathFinder): + """Meta importer to enable unified qiskit namespace.""" + def __init__(self, new_package, old_namespace): + super().__init__() + self.new_package = new_package + self.old_namespace = old_namespace + + def find_spec(self, fullname, path=None, target=None): + """Return the ModuleSpec for Qiskit element.""" + if fullname.startswith(self.old_namespace): + return importlib.util.spec_from_loader( + fullname, + QiskitLoader(self.new_package, self.old_namespace), + origin='qiskit') + return None diff --git a/qiskit/providers/__init__.py b/qiskit/providers/__init__.py index fd53fbcbe3e3..c944c09c3f69 100644 --- a/qiskit/providers/__init__.py +++ b/qiskit/providers/__init__.py @@ -47,15 +47,9 @@ JobTimeoutError """ -import pkgutil - from .basebackend import BaseBackend from .baseprovider import BaseProvider from .basejob import BaseJob from .exceptions import (JobError, JobTimeoutError, QiskitBackendNotFoundError, BackendPropertyError, BackendConfigurationError) from .jobstatus import JobStatus - - -# Allow extending this namespace. -__path__ = pkgutil.extend_path(__path__, __name__) diff --git a/qiskit/tools/jupyter/job_watcher.py b/qiskit/tools/jupyter/job_watcher.py index dda4591309e7..98cca99c5fc6 100644 --- a/qiskit/tools/jupyter/job_watcher.py +++ b/qiskit/tools/jupyter/job_watcher.py @@ -176,5 +176,6 @@ def qiskit_disable_job_watcher(self, line='', cell=None): _JOB_WATCHER.stop_viewer() -# The Jupyter job watcher instance -_JOB_WATCHER = JobWatcher() +if HAS_IBMQ: + # The Jupyter job watcher instance + _JOB_WATCHER = JobWatcher() diff --git a/qiskit/version.py b/qiskit/version.py index 2c2f2776fcac..fb300f3b3bfa 100644 --- a/qiskit/version.py +++ b/qiskit/version.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# pylint: disable=no-name-in-module,broad-except,cyclic-import +# pylint: disable=no-name-in-module,broad-except,cyclic-import,import-error """Contains the terra version.""" @@ -85,22 +85,22 @@ def _get_qiskit_versions(): out_dict = {} out_dict['qiskit-terra'] = __version__ try: - from qiskit.providers import aer + import qiskit_aer as aer out_dict['qiskit-aer'] = aer.__version__ except Exception: out_dict['qiskit-aer'] = None try: - from qiskit import ignis + import qiskit_ignis as ignis out_dict['qiskit-ignis'] = ignis.__version__ except Exception: out_dict['qiskit-ignis'] = None try: - from qiskit.providers import ibmq + import qiskit_ibmq_provider as ibmq out_dict['qiskit-ibmq-provider'] = ibmq.__version__ except Exception: out_dict['qiskit-ibmq-provider'] = None try: - from qiskit import aqua + import qiskit_aqua as aqua out_dict['qiskit-aqua'] = aqua.__version__ except Exception: out_dict['qiskit-aqua'] = None From 990864b2df031d6adb6a3da26007f4956300436c Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 8 Oct 2020 15:58:39 -0400 Subject: [PATCH 02/21] Try using new aer path for qiskit.Aer alias --- qiskit/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 1611ec9a3e74..398460262b81 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -75,8 +75,8 @@ # Try to import the Aer provider if installed. try: - from qiskit.providers import aer - Aer = aer.Aer + import qiskit_aer + Aer = qiskit_aer.Aer except ImportError: suppress_warnings = os.environ.get('QISKIT_SUPPRESS_PACKAGING_WARNINGS', '') if suppress_warnings.upper() != 'Y': From 6e896c5f158b90c99c81b60f8fe52a24843581d4 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 8 Oct 2020 16:02:34 -0400 Subject: [PATCH 03/21] Try moving Aer and IBMQ alias to the end --- qiskit/__init__.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 398460262b81..96a5dc850ee5 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -73,6 +73,15 @@ _config = _user_config.get_config() +from qiskit.execute import execute # noqa +from qiskit.compiler import transpile, assemble, schedule, sequence # noqa + +from .version import __version__ # noqa +from .version import _get_qiskit_versions # noqa + + +__qiskit_version__ = _get_qiskit_versions() + # Try to import the Aer provider if installed. try: import qiskit_aer @@ -87,7 +96,7 @@ # Try to import the IBMQ provider if installed. try: - from qiskit.providers import ibmq + import qiskit_ibmq_provider as ibmq IBMQ = ibmq.IBMQ except ImportError: suppress_warnings = os.environ.get('QISKIT_SUPPRESS_PACKAGING_WARNINGS', '') @@ -98,14 +107,3 @@ '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 -from qiskit.compiler import transpile, assemble, schedule, sequence # noqa - -from .version import __version__ # noqa -from .version import _get_qiskit_versions # noqa - - -__qiskit_version__ = _get_qiskit_versions() From d2a93b9a82c6ef8cd4ed6f5ebd26e18d17b3e5a3 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 9 Feb 2021 10:21:12 -0500 Subject: [PATCH 04/21] Fix typo --- qiskit/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index c264911b0743..5d767a79eafa 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -101,7 +101,7 @@ def __init__(self): def __bool__(self): if self.aer is None: try: - from qiskit_aer + import qiskit_aer self.aer = qiskit_aer.Aer except ImportError: return False @@ -128,7 +128,7 @@ def __init__(self): def __bool__(self): if self.ibmq is None: try: - from qiskit_ibmq_provider as ibmq + import qiskit_ibmq_provider as ibmq self.ibmq = ibmq.IBMQ except ImportError: return False @@ -137,7 +137,7 @@ def __bool__(self): def __getattr__(self, attr): if not self.ibmq: try: - from qiskit_ibmq_provider as ibmq + import qiskit_ibmq_provider as ibmq self.ibmq = ibmq.IBMQ except ImportError as exc: raise ImportError('Could not import the IBMQ provider from the ' From fa1781ef9e1d189cbac904db96282f9d185a2fa5 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 12 Feb 2021 11:03:49 -0500 Subject: [PATCH 05/21] Fix circular import issue --- qiskit/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 5d767a79eafa..065f2d274897 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -18,9 +18,6 @@ import warnings import os -# First, check for required Python and API version -from . import util - # Extend namespace for backwards compat from qiskit import namespace new_meta_path = [] From 94837089131ca22201e810ff70f8f095c8d2a571 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 12 Feb 2021 11:04:25 -0500 Subject: [PATCH 06/21] Fix typos --- qiskit/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/version.py b/qiskit/version.py index 5e5dc1fda534..a2e0212ce655 100644 --- a/qiskit/version.py +++ b/qiskit/version.py @@ -109,12 +109,12 @@ def _load_versions(self): except Exception: self._version_dict['qiskit-ignis'] = None try: - from qiskit_ibmq_provider as ibmq + import qiskit_ibmq_provider as ibmq self._version_dict['qiskit-ibmq-provider'] = ibmq.__version__ except Exception: self._version_dict['qiskit-ibmq-provider'] = None try: - from qiskit_aqua as aqua + import qiskit_aqua as aqua self._version_dict['qiskit-aqua'] = aqua.__version__ except Exception: self._version_dict['qiskit-aqua'] = None From 5e493fce80006e6a225db12158a44b7a1b254f88 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 9 May 2022 14:18:59 -0400 Subject: [PATCH 07/21] Run black --- qiskit/__init__.py | 4 ++-- qiskit/namespace.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 843e4b8e8d56..5a92aa83c0b2 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -30,9 +30,9 @@ # Extend namespace for backwards compat from qiskit import namespace + new_meta_path = [] -new_meta_path.append(namespace.QiskitElementImport( - 'qiskit_aer', 'qiskit.providers.aer')) +new_meta_path.append(namespace.QiskitElementImport("qiskit_aer", "qiskit.providers.aer")) # Add Qiskit importers to meta_path before PathFinder in the default # sys.meta_path to avoid the miss penalty on trying to import a module # which does not exist diff --git a/qiskit/namespace.py b/qiskit/namespace.py index 33a7fae79b93..5656baeb017d 100644 --- a/qiskit/namespace.py +++ b/qiskit/namespace.py @@ -23,6 +23,7 @@ class QiskitLoader(Loader): """Load qiskit element as a namespace package.""" + def __init__(self, new_package, old_namespace): super().__init__() self.new_package = new_package @@ -34,10 +35,9 @@ def module_repr(self, module): def load_module(self, fullname): old_name = fullname names = fullname.split(".") - new_namespace_names = self.new_package.split('.') - old_namespace_names = self.old_namespace.split('.') - fullname = ".".join( - new_namespace_names + names[len(old_namespace_names):]) + new_namespace_names = self.new_package.split(".") + old_namespace_names = self.old_namespace.split(".") + fullname = ".".join(new_namespace_names + names[len(old_namespace_names) :]) module = importlib.import_module(fullname) sys.modules[old_name] = module return module @@ -45,6 +45,7 @@ def load_module(self, fullname): class QiskitElementImport(MetaPathFinder): """Meta importer to enable unified qiskit namespace.""" + def __init__(self, new_package, old_namespace): super().__init__() self.new_package = new_package @@ -54,7 +55,6 @@ def find_spec(self, fullname, path=None, target=None): """Return the ModuleSpec for Qiskit element.""" if fullname.startswith(self.old_namespace): return importlib.util.spec_from_loader( - fullname, - QiskitLoader(self.new_package, self.old_namespace), - origin='qiskit') + fullname, QiskitLoader(self.new_package, self.old_namespace), origin="qiskit" + ) return None From e3866ea6151aabbc6785238655fd4f3317289b0f Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 9 May 2022 15:03:57 -0400 Subject: [PATCH 08/21] Remove file encoding --- qiskit/namespace.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit/namespace.py b/qiskit/namespace.py index 5656baeb017d..a18aa29c48e8 100644 --- a/qiskit/namespace.py +++ b/qiskit/namespace.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # This code is part of Qiskit. # # (C) Copyright IBM 2020. From f759582fdd6f10adec1f797b57fd79ab9013b689 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 11 May 2022 09:29:41 -0400 Subject: [PATCH 09/21] Adjust init with import re-direct --- qiskit/__init__.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 5a92aa83c0b2..96f71df21a4c 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -31,13 +31,8 @@ # Extend namespace for backwards compat from qiskit import namespace -new_meta_path = [] -new_meta_path.append(namespace.QiskitElementImport("qiskit_aer", "qiskit.providers.aer")) -# Add Qiskit importers to meta_path before PathFinder in the default -# sys.meta_path to avoid the miss penalty on trying to import a module -# which does not exist -old_meta_path = sys.meta_path -sys.meta_path = old_meta_path[:-1] + new_meta_path + [old_meta_path[-1]] +sys.meta_path.append(namespace.QiskitElementImport("qiskit_aer", "qiskit.providers.aer")) + # qiskit errors operator from qiskit.exceptions import QiskitError, MissingOptionalLibraryError From 643cbb60a608aa0cc1132eb0d6b855f608b444b1 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 17 May 2022 17:26:32 -0400 Subject: [PATCH 10/21] Make qiskit_aer a opportunistic load To ensure that someone with an old version of Aer installed can still access it via the old namespace this changes the meta finder logic to first try qiskit_aer, if it's importable then we build the redirect path first and use that for the legacy qiskit.providers.aer path. If it's not then we just return None and fall back to the other finders in sys.meta_path. To support this the pkgutil hook is added back to qiskit.providers to add the namespace hook for old version of aer using namespace packagingm although not strictly necessary because the implicit support for namespace packages will still likely work we can remove it at a later date. --- qiskit/__init__.py | 4 ++-- qiskit/namespace.py | 33 ++++++++++++++++++++++----------- qiskit/providers/__init__.py | 5 +++++ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 89612a448301..395decd94ce1 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -33,8 +33,8 @@ # Extend namespace for backwards compat from qiskit import namespace -sys.meta_path.append(namespace.QiskitElementImport("qiskit_aer", "qiskit.providers.aer")) - +new_meta_path_finder = namespace.QiskitAerImport() +sys.meta_path = [new_meta_path_finder] + sys.meta_path # qiskit errors operator from qiskit.exceptions import QiskitError, MissingOptionalLibraryError diff --git a/qiskit/namespace.py b/qiskit/namespace.py index a18aa29c48e8..984baac7e34c 100644 --- a/qiskit/namespace.py +++ b/qiskit/namespace.py @@ -19,6 +19,14 @@ import importlib +def _new_namespace(fullname, old_namespace, new_package): + names = fullname.split(".") + new_namespace_names = new_package.split(".") + old_namespace_names = old_namespace.split(".") + fullname = ".".join(new_namespace_names + names[len(old_namespace_names) :]) + return fullname + + class QiskitLoader(Loader): """Load qiskit element as a namespace package.""" @@ -32,27 +40,30 @@ def module_repr(self, module): def load_module(self, fullname): old_name = fullname - names = fullname.split(".") - new_namespace_names = self.new_package.split(".") - old_namespace_names = self.old_namespace.split(".") - fullname = ".".join(new_namespace_names + names[len(old_namespace_names) :]) + fullname = _new_namespace(fullname, self.old_namespace, self.new_package) module = importlib.import_module(fullname) sys.modules[old_name] = module return module -class QiskitElementImport(MetaPathFinder): +class QiskitAerImport(MetaPathFinder): """Meta importer to enable unified qiskit namespace.""" - def __init__(self, new_package, old_namespace): + def __init__(self): super().__init__() - self.new_package = new_package - self.old_namespace = old_namespace + self.old_namespace = "qiskit.providers.aer" + self.new_package = "qiskit_aer" def find_spec(self, fullname, path=None, target=None): """Return the ModuleSpec for Qiskit element.""" if fullname.startswith(self.old_namespace): - return importlib.util.spec_from_loader( - fullname, QiskitLoader(self.new_package, self.old_namespace), origin="qiskit" - ) + try: + importlib.import_module( + _new_namespace(fullname, self.old_namespace, self.new_package) + ) + return importlib.util.spec_from_loader( + fullname, QiskitLoader(self.new_package, self.old_namespace), origin="qiskit" + ) + except Exception: # pylint: disable=broad-except + return None return None diff --git a/qiskit/providers/__init__.py b/qiskit/providers/__init__.py index 847534a23547..7cca9ed49e49 100644 --- a/qiskit/providers/__init__.py +++ b/qiskit/providers/__init__.py @@ -668,6 +668,7 @@ def status(self): separately in a :class:`~qiskit.transpiler.Target`. """ +import pkgutil # Providers interface from qiskit.providers.provider import Provider @@ -688,3 +689,7 @@ def status(self): BackendConfigurationError, ) from qiskit.providers.jobstatus import JobStatus + + +# Allow extending this namespace. +__path__ = pkgutil.extend_path(__path__, __name__) From 90240d1726a23598cdbc259f1a9476041107d4aa Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 17 May 2022 18:24:13 -0400 Subject: [PATCH 11/21] Deprecate qiskit.Aer entrypoint --- qiskit/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 395decd94ce1..f4fc8847ecbf 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -83,6 +83,11 @@ def __bool__(self): from qiskit.providers import aer self.aer = aer.Aer + warnings.warn( + "The qiskit.Aer entry point is deprecated instead you should use this directly from the root of the qiskit-aer package.", + DeprecationWarning, + stacklevel=2, + ) except ImportError: return False return True @@ -93,6 +98,11 @@ def __getattr__(self, attr): from qiskit.providers import aer self.aer = aer.Aer + warnings.warn( + "The qiskit.Aer entry point is deprecated instead you should use this directly from the root of the qiskit-aer package", + DeprecationWarning, + stacklevel=2, + ) except ImportError as ex: raise MissingOptionalLibraryError( "qiskit-aer", "Aer provider", "pip install qiskit-aer" From 08cb82fe8009ba28c1da82b00f3adb0381714555 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 18 May 2022 07:44:01 -0400 Subject: [PATCH 12/21] Fix lint --- qiskit/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index f4fc8847ecbf..aa960415bbdd 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -84,7 +84,8 @@ def __bool__(self): self.aer = aer.Aer warnings.warn( - "The qiskit.Aer entry point is deprecated instead you should use this directly from the root of the qiskit-aer package.", + "The qiskit.Aer entry point is deprecated instead you should use this " + "directly from the root of the qiskit-aer package.", DeprecationWarning, stacklevel=2, ) @@ -99,7 +100,8 @@ def __getattr__(self, attr): self.aer = aer.Aer warnings.warn( - "The qiskit.Aer entry point is deprecated instead you should use this directly from the root of the qiskit-aer package", + "The qiskit.Aer entry point is deprecated instead you should use this " + "directly from the root of the qiskit-aer package", DeprecationWarning, stacklevel=2, ) From fe8f90a12cd3dea0a1c97ff34ed7b02fe66a5961 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 18 May 2022 07:46:41 -0400 Subject: [PATCH 13/21] Remove unnecessary noqa comments --- qiskit/__init__.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index aa960415bbdd..e63f90067b24 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -40,32 +40,32 @@ from qiskit.exceptions import QiskitError, MissingOptionalLibraryError # The main qiskit operators -from qiskit.circuit import ClassicalRegister # noqa -from qiskit.circuit import QuantumRegister # noqa -from qiskit.circuit import AncillaRegister # noqa -from qiskit.circuit import QuantumCircuit # noqa +from qiskit.circuit import ClassicalRegister +from qiskit.circuit import QuantumRegister +from qiskit.circuit import AncillaRegister +from qiskit.circuit import QuantumCircuit # user config -from qiskit import user_config as _user_config # noqa +from qiskit import user_config as _user_config # The qiskit.extensions.x imports needs to be placed here due to the # mechanism for adding gates dynamically. -import qiskit.extensions # noqa -import qiskit.circuit.measure # noqa -import qiskit.circuit.reset # noqa +import qiskit.extensions +import qiskit.circuit.measure +import qiskit.circuit.reset # Please note these are global instances, not modules. -from qiskit.providers.basicaer import BasicAer # noqa +from qiskit.providers.basicaer import BasicAer _config = _user_config.get_config() # Moved to after IBMQ and Aer imports due to import issues # with other modules that check for IBMQ (tools) -from qiskit.execute_function import execute # noqa -from qiskit.compiler import transpile, assemble, schedule, sequence # noqa +from qiskit.execute_function import execute +from qiskit.compiler import transpile, assemble, schedule, sequence -from .version import __version__ # noqa -from .version import QiskitVersion # noqa +from .version import __version__ +from .version import QiskitVersion __qiskit_version__ = QiskitVersion() From c841200afafdf1aaafa7151612f996b30b3ca570 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 18 May 2022 07:47:52 -0400 Subject: [PATCH 14/21] Revert version.py change and use old aer import for now --- qiskit/version.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qiskit/version.py b/qiskit/version.py index 76bb094088d9..ef3be51b357a 100644 --- a/qiskit/version.py +++ b/qiskit/version.py @@ -105,7 +105,9 @@ def _load_versions(self): import pkg_resources try: - import qiskit_aer as aer + # TODO: Update to use qiskit_aer instead when we remove the + # namespace redirect + import qiskit.providers import aer self._version_dict["qiskit-aer"] = aer.__version__ except Exception: From 1aa04ef297aeb63a54472d93d326ec5ca732024d Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 18 May 2022 07:53:16 -0400 Subject: [PATCH 15/21] Fix typo --- qiskit/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/version.py b/qiskit/version.py index ef3be51b357a..9dc1a921c60e 100644 --- a/qiskit/version.py +++ b/qiskit/version.py @@ -107,7 +107,7 @@ def _load_versions(self): try: # TODO: Update to use qiskit_aer instead when we remove the # namespace redirect - import qiskit.providers import aer + from qiskit.providers import aer self._version_dict["qiskit-aer"] = aer.__version__ except Exception: From 876c6302dad3988416944127cdff4ff7ae61b7ab Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 18 May 2022 08:00:41 -0400 Subject: [PATCH 16/21] Add back pkgutil extension to root init --- qiskit/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index e63f90067b24..94df4b1c9e28 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -14,6 +14,7 @@ """Main Qiskit public functionality.""" +import pkgutil import sys import warnings @@ -54,6 +55,11 @@ import qiskit.circuit.measure import qiskit.circuit.reset +# Allow extending this namespace. Please note that currently this line needs +# to be placed *before* the wrapper imports or any non-import code AND *before* +# importing the package you want to allow extensions for (in this case `backends`). +__path__ = pkgutil.extend_path(__path__, __name__) + # Please note these are global instances, not modules. from qiskit.providers.basicaer import BasicAer From 4cc552b9c49258b6f9a58f7133dc5a789083da05 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 18 May 2022 08:35:37 -0400 Subject: [PATCH 17/21] Make redirect hook more generic --- qiskit/__init__.py | 2 +- qiskit/namespace.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 94df4b1c9e28..c97d70133992 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -34,7 +34,7 @@ # Extend namespace for backwards compat from qiskit import namespace -new_meta_path_finder = namespace.QiskitAerImport() +new_meta_path_finder = namespace.QiskitElementImport("qiskit.providers.aer", "qiskit_aer") sys.meta_path = [new_meta_path_finder] + sys.meta_path # qiskit errors operator diff --git a/qiskit/namespace.py b/qiskit/namespace.py index 984baac7e34c..d07656a12e03 100644 --- a/qiskit/namespace.py +++ b/qiskit/namespace.py @@ -46,13 +46,13 @@ def load_module(self, fullname): return module -class QiskitAerImport(MetaPathFinder): +class QiskitElementImport(MetaPathFinder): """Meta importer to enable unified qiskit namespace.""" - def __init__(self): + def __init__(self, old_namespace, new_package): super().__init__() - self.old_namespace = "qiskit.providers.aer" - self.new_package = "qiskit_aer" + self.old_namespace = old_namespace + self.new_package = new_package def find_spec(self, fullname, path=None, target=None): """Return the ModuleSpec for Qiskit element.""" From 84166c0e4835792094fae218766ae052c825d1b5 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 18 May 2022 08:38:13 -0400 Subject: [PATCH 18/21] Add comments explaining the various hooks --- qiskit/__init__.py | 6 ++++++ qiskit/providers/__init__.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index c97d70133992..9c5aeabd1afa 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -34,6 +34,9 @@ # Extend namespace for backwards compat from qiskit import namespace +# Add hook to redirect imports from qiskit.providers.aer* to qiskit_aer* +# this is necessary for backwards compatibility for users when qiskit-aer +# and qiskit-terra shared the qiskit namespace new_meta_path_finder = namespace.QiskitElementImport("qiskit.providers.aer", "qiskit_aer") sys.meta_path = [new_meta_path_finder] + sys.meta_path @@ -58,6 +61,9 @@ # Allow extending this namespace. Please note that currently this line needs # to be placed *before* the wrapper imports or any non-import code AND *before* # importing the package you want to allow extensions for (in this case `backends`). + +# TODO: Remove when we drop support for importing qiskit-aer < 0.11.0 and the +# qiskit-ibmq-provider package is retired/archived. __path__ = pkgutil.extend_path(__path__, __name__) # Please note these are global instances, not modules. diff --git a/qiskit/providers/__init__.py b/qiskit/providers/__init__.py index 7cca9ed49e49..e83b145b2f5a 100644 --- a/qiskit/providers/__init__.py +++ b/qiskit/providers/__init__.py @@ -692,4 +692,6 @@ def status(self): # Allow extending this namespace. +# TODO: Remove when we drop support for importing qiskit-aer < 0.11.0 and the +# qiskit-ibmq-provider package is retired/archived. __path__ = pkgutil.extend_path(__path__, __name__) From 5e43989f4011dfe9de56615578d71ab56d0bca99 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 21 Jun 2022 13:56:01 -0400 Subject: [PATCH 19/21] Make qiskit.Aer a pending deprecation instead of a deprecation --- qiskit/__init__.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 9c5aeabd1afa..2a5c2a379238 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -96,9 +96,10 @@ def __bool__(self): self.aer = aer.Aer warnings.warn( - "The qiskit.Aer entry point is deprecated instead you should use this " + "The qiskit.Aer entry point will be deprecated in a future release and " + "will subsequently removed after that. Instead you should use this " "directly from the root of the qiskit-aer package.", - DeprecationWarning, + PendingDeprecationWarning, stacklevel=2, ) except ImportError: @@ -112,9 +113,10 @@ def __getattr__(self, attr): self.aer = aer.Aer warnings.warn( - "The qiskit.Aer entry point is deprecated instead you should use this " - "directly from the root of the qiskit-aer package", - DeprecationWarning, + "The qiskit.Aer entry point will be deprecated in a future release and " + "will subsequently removed after that. Instead you should use this " + "directly from the root of the qiskit-aer package.", + PendingDeprecationWarning, stacklevel=2, ) except ImportError as ex: From 6ba893ad7c12158012e8f02e79aa3d1719abed18 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 21 Jun 2022 13:58:23 -0400 Subject: [PATCH 20/21] Restrict namespace redirect error to ModuleNotFoundError --- qiskit/namespace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/namespace.py b/qiskit/namespace.py index d07656a12e03..fd05f43fc0f4 100644 --- a/qiskit/namespace.py +++ b/qiskit/namespace.py @@ -64,6 +64,6 @@ def find_spec(self, fullname, path=None, target=None): return importlib.util.spec_from_loader( fullname, QiskitLoader(self.new_package, self.old_namespace), origin="qiskit" ) - except Exception: # pylint: disable=broad-except + except ModuleNotFoundError: # pylint: disable=broad-except return None return None From 20c63c8435d0d0d73e744ce0923052cfed6dc8db Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 21 Jun 2022 15:22:40 -0400 Subject: [PATCH 21/21] Apply suggestions from code review Co-authored-by: Jake Lishman --- qiskit/__init__.py | 4 ++-- qiskit/namespace.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 2a5c2a379238..3a0da934e4c0 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -97,7 +97,7 @@ def __bool__(self): self.aer = aer.Aer warnings.warn( "The qiskit.Aer entry point will be deprecated in a future release and " - "will subsequently removed after that. Instead you should use this " + "subsequently removed. Instead you should use this " "directly from the root of the qiskit-aer package.", PendingDeprecationWarning, stacklevel=2, @@ -114,7 +114,7 @@ def __getattr__(self, attr): self.aer = aer.Aer warnings.warn( "The qiskit.Aer entry point will be deprecated in a future release and " - "will subsequently removed after that. Instead you should use this " + "subsequently removed. Instead you should use this " "directly from the root of the qiskit-aer package.", PendingDeprecationWarning, stacklevel=2, diff --git a/qiskit/namespace.py b/qiskit/namespace.py index fd05f43fc0f4..e6b78d3a8d48 100644 --- a/qiskit/namespace.py +++ b/qiskit/namespace.py @@ -64,6 +64,6 @@ def find_spec(self, fullname, path=None, target=None): return importlib.util.spec_from_loader( fullname, QiskitLoader(self.new_package, self.old_namespace), origin="qiskit" ) - except ModuleNotFoundError: # pylint: disable=broad-except + except ModuleNotFoundError: return None return None