Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add namespace redirect hook for qiskit-aer #5089

Merged
merged 31 commits into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2356149
Remove namespace packaging and hardcode elements (attempt 2)
mtreinish Sep 18, 2020
3174149
Merge branch 'master' into namespace-no-more
mtreinish Oct 8, 2020
990864b
Try using new aer path for qiskit.Aer alias
mtreinish Oct 8, 2020
6e896c5
Try moving Aer and IBMQ alias to the end
mtreinish Oct 8, 2020
0f985de
Merge branch 'master' into namespace-no-more
mtreinish Feb 5, 2021
d2a93b9
Fix typo
mtreinish Feb 9, 2021
32b5499
Merge branch 'master' into namespace-no-more
mtreinish Feb 9, 2021
c17e0c1
Merge remote-tracking branch 'origin/master' into namespace-no-more
mtreinish Feb 12, 2021
fa1781e
Fix circular import issue
mtreinish Feb 12, 2021
9483708
Fix typos
mtreinish Feb 12, 2021
208131e
Merge branch 'main' into namespace-no-more
mtreinish May 5, 2022
5e493fc
Run black
mtreinish May 9, 2022
e3866ea
Remove file encoding
mtreinish May 9, 2022
f759582
Adjust init with import re-direct
mtreinish May 11, 2022
4836642
Merge remote-tracking branch 'origin/main' into namespace-no-more
mtreinish May 17, 2022
643cbb6
Make qiskit_aer a opportunistic load
mtreinish May 17, 2022
90240d1
Deprecate qiskit.Aer entrypoint
mtreinish May 17, 2022
945bf7a
Merge branch 'main' into namespace-no-more
mtreinish May 17, 2022
08cb82f
Fix lint
mtreinish May 18, 2022
fe8f90a
Remove unnecessary noqa comments
mtreinish May 18, 2022
c841200
Revert version.py change and use old aer import for now
mtreinish May 18, 2022
1aa04ef
Fix typo
mtreinish May 18, 2022
876c630
Add back pkgutil extension to root init
mtreinish May 18, 2022
4cc552b
Make redirect hook more generic
mtreinish May 18, 2022
84166c0
Add comments explaining the various hooks
mtreinish May 18, 2022
8eb7ce7
Merge remote-tracking branch 'origin/main' into namespace-no-more
mtreinish Jun 21, 2022
5e43989
Make qiskit.Aer a pending deprecation instead of a deprecation
mtreinish Jun 21, 2022
6ba893a
Restrict namespace redirect error to ModuleNotFoundError
mtreinish Jun 21, 2022
20c63c8
Apply suggestions from code review
mtreinish Jun 21, 2022
132c894
Merge branch 'main' into namespace-no-more
mtreinish Jun 21, 2022
9e11213
Merge branch 'main' into namespace-no-more
mergify[bot] Jun 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@
sys.modules["qiskit._accelerate.results"] = qiskit._accelerate.results


# Extend namespace for backwards compat
from qiskit import namespace
jakelishman marked this conversation as resolved.
Show resolved Hide resolved

# 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

# qiskit errors operator
from qiskit.exceptions import QiskitError, MissingOptionalLibraryError

Expand All @@ -52,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.
Expand All @@ -61,11 +73,11 @@

# 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()
Expand All @@ -83,6 +95,13 @@ def __bool__(self):
from qiskit.providers import aer

self.aer = aer.Aer
warnings.warn(
"The qiskit.Aer entry point will be deprecated in a future release and "
"subsequently removed. Instead you should use this "
"directly from the root of the qiskit-aer package.",
PendingDeprecationWarning,
stacklevel=2,
)
except ImportError:
return False
return True
Expand All @@ -93,6 +112,13 @@ def __getattr__(self, attr):
from qiskit.providers import aer

self.aer = aer.Aer
warnings.warn(
"The qiskit.Aer entry point will be deprecated in a future release and "
"subsequently removed. Instead you should use this "
"directly from the root of the qiskit-aer package.",
PendingDeprecationWarning,
stacklevel=2,
)
except ImportError as ex:
raise MissingOptionalLibraryError(
"qiskit-aer", "Aer provider", "pip install qiskit-aer"
Expand Down
69 changes: 69 additions & 0 deletions qiskit/namespace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# 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


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."""

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
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):
"""Meta importer to enable unified qiskit namespace."""

def __init__(self, old_namespace, new_package):
super().__init__()
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."""
if fullname.startswith(self.old_namespace):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose technically this might break some compatibility if some random package is extending the namespace with qiskit.providers.aer_with_my_modifications (still startswith("qiskit.providers.aer"), but we probably don't need to care about that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure namespace packaging would work for someone trying to extend qiskit.providers.aer since aer itself doesn't add the same pkgutil hooks. In some cases someone probably could extend the namespace with their custom subpackage but not in all permutations of packaging as namespace packaging isn't reliable (this is why I couldn't remove the pkgutil calls otherwise I'd break compat with old/current aer releases).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean if somebody extends qiskit.providers with a name that starts with aer, like aer_but_actually_not. I think it's probably not worth worrying about, though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok now I follow what you're saying. Yeah I should have done a proper regex match something like ^qiskit\.provider\.aer\.* to match on exactly the aer namespace. But yeah I'm not too worried a potential name squatter like that. The bigger concern for me is how we deprecate and remove the pkgutil namespace hooks longer term. I know there are external users of it and trying to migrate them off will be a pain.

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 ModuleNotFoundError:
return None
return None
2 changes: 2 additions & 0 deletions qiskit/providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
2 changes: 2 additions & 0 deletions qiskit/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ def _load_versions(self):
import pkg_resources

try:
# TODO: Update to use qiskit_aer instead when we remove the
# namespace redirect
from qiskit.providers import aer

self._version_dict["qiskit-aer"] = aer.__version__
Expand Down