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

Poison import if qiskit-terra detected #11618

Merged
merged 4 commits into from
Jan 31, 2024

Conversation

jakelishman
Copy link
Member

Summary

This uses importlib.metadata to search for installations of qiskit-terra at runtime, and poison the import if one is detected. The two packages are incompatible in a way that we cannot express to pip without having poisoned the qiskit-terra package as a whole, which is something we're less willing to do in order to keep it easier to install old versions of Qiskit-dependent software, and to keep the Qiskit 1.0 packaging story clean for the coming years.

qiskit>=1.0 and qiskit-terra<1.0 both attempt to install files in the same directories, so we can't do the detection by looking at the Qiskit version; we'll likely get whichever was installed second if they've both been installed to site-packages, or otherwise, whichever comes first on the search path. This uses the distribution component of importlib.metadata to detect installed distributions rather than imported files, allowing us to distinguish qiskit from qiskit-terra.

Details and comments

See #11617 for a similar PR made to the 0.45 series (which I propose we release as 0.45.3) and to be ported to the 0.46 branch. The reason I think we also need the 0.45.x and 0.46.x series to have this error is in case they get accidentally installed after Qiskit 1.0 (such as during a subsequent pip install command), and the import system resolves qiskit.__file__ to the qiskit-terra __init__.py rather than the Qiskit 1.0 init.

A similar false-positive to the one I mentioned on #11617 applies here too, but is probably a fair amount more likely if a developer still has a qiskit_terra.egg-info directory in their path, such as in the repo root of Qiskit. The remedy is to delete that directory; it's not part of the actual installation (and tbh, I'm not sure why pip/setuptools drops it there given that pip uninstall doesn't clear it up).

@jakelishman jakelishman added this to the 1.0.0 milestone Jan 22, 2024
@jakelishman jakelishman requested a review from a team as a code owner January 22, 2024 18:47
@qiskit-bot
Copy link
Collaborator

One or more of the the following people are requested to review this:

  • @Qiskit/terra-core

@jakelishman
Copy link
Member Author

Given that this 1.0-branch poisoning is a bit more likely to trip up developers (the conditions needed for a false positive are much simpler), perhaps it might be worth putting in an existence check for a file that's known to only exist in qiskit-terra as well. For example, if qiskit/namespace.py or qiskit/tools/events/pubsub.py fail to import, then we're almost certainly in false-positive land, and that's a fairly direct test of "is this environment in a conflicted state?".

@coveralls
Copy link

coveralls commented Jan 22, 2024

Pull Request Test Coverage Report for Build 7731909408

Warning: This coverage report may be inaccurate.

We've detected an issue with your CI configuration that might affect the accuracy of this pull request's coverage report.
To ensure accuracy in future PRs, please see these guidelines.
A quick fix for this PR: rebase it; your next report should be accurate.

  • -4 of 11 (63.64%) changed or added relevant lines in 1 file are covered.
  • 542 unchanged lines in 48 files lost coverage.
  • Overall coverage increased (+0.04%) to 89.575%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/init.py 7 11 63.64%
Files with Coverage Reduction New Missed Lines %
qiskit/circuit/classical/types/ordering.py 1 94.64%
qiskit/circuit/controlflow/control_flow.py 1 92.86%
qiskit/circuit/parametervector.py 1 93.33%
qiskit/circuit/tools/pi_check.py 1 91.15%
qiskit/passmanager/passmanager.py 1 93.62%
qiskit/primitives/containers/data_bin.py 1 96.97%
qiskit/primitives/statevector_sampler.py 1 99.09%
qiskit/transpiler/passes/layout/set_layout.py 1 95.65%
qiskit/assembler/disassemble.py 2 98.78%
qiskit/exceptions.py 2 93.75%
Totals Coverage Status
Change from base Build 7632033511: 0.04%
Covered Lines: 59396
Relevant Lines: 66309

💛 - Coveralls

@jakelishman
Copy link
Member Author

The docs failure here is actually the poisoning working correctly; the problem is that tox builds the environment by installing the environment dependencies first, and only then building and installing the package. Because of the bi-directional dependency from Aer, that's causing the docs build to be in an invalid environment, which this PR just found for us.

@jakelishman
Copy link
Member Author

jakelishman commented Jan 22, 2024

I've made #11621 that is one way of fixing the tox environments, and for now I've cherry-picked it into this branch to see if there's any further problems lurking.

edit: this PR now also contains #11625, which is fixes for more bugs in Qiskit 1.0 found by the fix of #11621. I'm just cherry-picking in those commits to check this PR ahead of time; it should merge last in the chain.

This uses `importlib.metadata` to search for installations of
`qiskit-terra` at runtime, and poison the import if one is detected.
The two packages are incompatible in a way that we cannot express to
`pip` without having poisoned the `qiskit-terra` package as a whole,
which is something we're less willing to do in order to keep it easier
to install old versions of Qiskit-dependent software, and to keep the
Qiskit 1.0 packaging story clean for the coming years.

`qiskit>=1.0` and `qiskit-terra<1.0` both attempt to install files in
the same directories, so we can't do the detection by looking at the
Qiskit version; we'll likely get whichever was installed second if
they've both been installed to `site-packages`, or otherwise, whichever
comes first on the search path.  This uses the distribution component of
`importlib.metadata` to detect installed _distributions_ rather than
imported files, allowing us to distinguish `qiskit` from `qiskit-terra`.
@jakelishman
Copy link
Member Author

Now rebased over #11621 and #11625, but I'll just mark on hold for the same reason as #11617: I need to write a migration guide and tips for setting up environments during the transition, then short-link to it in the error message.

@jakelishman jakelishman added on hold Can not fix yet type: qa Issues and PRs that relate to testing and code quality Changelog: None Do not include in changelog labels Jan 23, 2024
@jakelishman jakelishman removed the on hold Can not fix yet label Jan 31, 2024
@jakelishman
Copy link
Member Author

Qiskit/documentation#710 should be merged and live now before any release would actually land.

Comment on lines +29 to +33
# 'qiskit.tools' is present in all 0.x series of Qiskit and not in Qiskit 1.0+. If a dev has an
# editable install and switches from 0.x branches to 1.0+ branches, they might have an empty
# `qiskit/tools` folder in their path, which will appear as a "namespace package" with no valid
# location. We catch that case as "not actually having Qiskit 0.x" as a convenience to devs.
_has_tools = getattr(importlib.util.find_spec("qiskit.tools"), "has_location", False)
Copy link
Member

Choose a reason for hiding this comment

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

This is a clever trick relying on the 1.0 removals to determine if the terra import is breaking anything or not.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks!

Will (@wshanks) alluded to a different case we might want to catch / improve the documentation to handle over in Qiskit/documentation#710 (comment) in addition to this. If he's got time to find the repro steps he mentions I can see if there's anything we might want to do in the import mechanism.

I don't want it to get too complex, though - I'd rather have a couple of (minor) false negatives for devs than slow imports or false positives for all users

Copy link
Contributor

Choose a reason for hiding this comment

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

I wasn't able to reproduce it by installing 0.25->0.45->main, so I don't think we need to worry about it. Either it takes an intricate set of steps to reproduce or I was just confused about something when I saw it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok cool, thanks Will. If you do come across another case, let us know - it's possible for us to update this check without much pain (though I am really nervous about how I've been deliberately breaking import!)

qiskit/__init__.py Outdated Show resolved Hide resolved
Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

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

This LGTM, if there is another edge case we need to detect here we can always expand change the check condition down the road. The one I'm thinking about is with editable installs and qiskit<0.43 leaving bits of qiskit.providers in an odd state, but I haven't though throught how we would detect that short of import qiskit not even working.

This will catch the vast majority of the common invalid install cases so lets move forward with this for now.

@mtreinish mtreinish enabled auto-merge January 31, 2024 20:53
@mtreinish mtreinish added this pull request to the merge queue Jan 31, 2024
Merged via the queue into Qiskit:main with commit 151fb79 Jan 31, 2024
12 checks passed
@jakelishman jakelishman deleted the import-error/1.0 branch January 31, 2024 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: None Do not include in changelog type: qa Issues and PRs that relate to testing and code quality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants