Repository navigation
Description
Bug report
Edit: Originally this bug was expressed through shutil's support detection. shutil now directly imports from _bz2 which works around this issue.
Bug description:
Under lazy_imports=all, modules imported in a try/except block are still imported eagerly. However, imports that occur within that module are still imported lazily.
This may be intentional, but this bug is the result of that behaviour.
On CPython built without bz2 support:
$ PYTHON_LAZY_IMPORTS=normal ./python -c "import shutil; print(shutil._BZ2_SUPPORTED)"
False
$ PYTHON_LAZY_IMPORTS=all ./python -c "import shutil; print(shutil._BZ2_SUPPORTED)"
TrueThe try/except check is on bz2, which succeeds because the import to _bz2 which would fail is converted to a lazy import and is not used at top level so the import which would fail isn't triggered.
Lines 21 to 26 in 87a879f
| try: | |
| import bz2 | |
| del bz2 | |
| _BZ2_SUPPORTED = True | |
| except ImportError: | |
| _BZ2_SUPPORTED = False |
shutil could directly check _bz2 in this particular case, but I expect this pattern isn't uncommon. I looked into this after I ran into a similar issue with pip and rich with an OS-specific import that should have failed in a similar manner.
Related: #149640 as this also causes test_shutil to fail under lazy_imports=all if _bz2 isn't present.
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Activity
vstinner commentedon May 21, 2026
I can reproduce the issue on Linux using:
_ZLIB_SUPPORTED,_LZMA_SUPPORTEDand_ZSTD_SUPPORTEDare correct sinceimport zlib,import lzmaandfrom compression import zstdfail immediately withModuleNotFoundeven withPYTHON_LAZY_IMPORTS=all._BZ2_SUPPORTEDis incorrect becauseimport bz2completes successfully even ifimport _bz2will fail. It behaves differently since_bz2symbols are only accessed whenbz2functions are called, not at import time.DavidCEllis commentedon May 22, 2026
lzmais working somewhat by luck as it uses a*import on the_lzmamodule and*imports can't be made lazy.zstduses_zstdat module level so similarly escapes this issue. I say by luck as these aren't done with the intention of preventing the lazy import, it's just a side-effect.I believe
zlibitself is the module that would fail so its check isn't affected but I haven't confirmed this.The simplest solution for
_bz2specifically is probably to addimport _bz2; _bz2tobz2.pyto force the resolution.lzmaandzstdcould also do the same, but I'm not sure it's necessary as long as there are tests. (I'm not actually sure how you'd write the test for this case given it requires missing modules and the lazy import flag.)We could instead check the internal modules directly in
shutilbut that makesshutildependant on internals of the compression libraries which is probably undesirable?pythongh-150167: Make shutil compression probes eager
DavidCEllis commentedon Jun 2, 2026
Perhaps
compressionshould have some kind of function or attribute that provides a list or set of available compression types?I think there it would be fine to only check the internal modules and
shutilcould then use this directly rather than checking all of the modules itself.DavidCEllis commentedon Jul 27, 2026
I was having a look at writing a test for this and noticed that the
test.support.requires_<format>functions have the same logic and probably the same issue.cpython/Lib/test/support/__init__.py
Lines 548 to 581 in 993a0c6
DavidCEllis commentedon Sep 8, 2026
So I believe the specific case of failure in shutil has been resolved as a side effect of #154908 but the logical issue still exists in other places that do check the modules directly like
test.support. Not sure what to do here.[-]`shutil._BZ2_SUPPORTED` is always `True` under `lazy_imports=all` even if it is not supported[/-][+]Importing `bz2` will succeed under `lazy_imports=all` even if `bz2` is not supported[/+][-]Importing `bz2` will succeed under `lazy_imports=all` even if `bz2` is not supported[/-][+]Importing `bz2` will succeed under `lazy_imports=all` even if bz2 is not supported[/+]