Thanks for providing a clear example case! I agree that delayed errors can be a problem. I am a bit confused as to exactly where your disagreement with PEP 690 is, though, since I think the PEP already provides the tools needed to handle this case: in fact what the PEP already proposes is very similar to your
But this is not true! Quoting directly from the PEP:
So the PEP spells it
importlib.set_eager_imports(['fast_csv']) instead of
sys.lazy_import_unsafe.add('fast_csv.config'); otherwise it seems quite similar to what you propose.
In fact the callback option also allows your “opt in just a few modules” case, too: define a callback that implements an allow-list instead of a block-list.
One difference is that the PEP proposes to name modules within which imports are eager, instead of modules whose import will always be eager. I don’t think this is a critical difference either way; as I described above I don’t really think describing
fast_csv.config as “lazy import unsafe” is accurate: rather I would say the usage of
fast_csv.config in the specific context of
fast_csv (whatever module defines
parse_float) is not safe to be lazy. (Although if we make the
LazyImportError change, then I think even that alone makes it OK.)
The other case the PEP does not currently support is the library author marking
fast_csv.config as lazy import unsafe. But if the library author is willing to bother accounting for lazy imports in the first place, they can just as easily “opt out” for a potentially problematic import by doing this:
with importlib.eager_imports(): import config def parse_float(value): ...
So in sum: I agree with your concern, and your example case, and I think the PEP already provides all the tools required to handle it in a way that is not very different from what you propose; it seems more like API bikeshedding than a real difference in capability.
I still think the concern about delayed errors from imports biting someone is very real, and I love your idea for that:
I am inclined to think the PEP should include this. That way lazy errors will not silently pass as some other error in the way shown in your example.
I do think inheriting
BaseException is a step too far:
except Exception: should still catch
LazyImportError. If someone is catching all exceptions, they don’t want errors bubbling through and they are already accepting the risk that they might catch any random thing they don’t expect. I don’t think
LazyImportError is parallel to
KeyboardInterrupt and deserves to be treated so differently; being a distinct exception type is sufficient to handle your example case.