Issue
It’s common practice for Python packages to define extras for optional features, e.g. pandas[excel]. When developing such packages, you cannot import the extra packages at top level — otherwise, a ModuleNotFoundError will be raised at import time on installations missing that extra.
Typical workarounds are:
-
Importing the dependency inside every function that needs it, or
-
Doing a top-level
try/exceptand setting the module toNone, later checkingif mod is Noneinside functions to disable the optional feature.
If the same dependency is used in multiple places, this logic must be repeated everywhere.
There’s currently no clean language-level mechanism for this, even though optional extras are a well-established concept in Python packaging.
Proposition
Inspired by PEP 810 – Explicit lazy imports, I’d like to propose a similar construct:
optional import numpy as np
This would attempt to import the module, and if it fails with ModuleNotFoundError, assign a special sentinel object (e.g. an instance of MissingOptionalDependency). This object would raise a MissingOptionalDependencyError on any access — much like how lazy imports reify on PEP 810.
Example:
optional import numpy as np
def non_optional_func():
return "foo"
def optional_func1():
return np.random.random()
def optional_func2():
return np.random.random() * 3
On an installation without numpy, you will be able to import and use non_optional_func. Only when calling optional_func1() or optional_func2() would the user get a clear MissingOptionalDependencyError explaining that numpy is not installed.
Benefits
-
Keeps optional imports at the top level, where imports naturally belong.
-
Reduces boilerplate and repetition of
try/exceptorNonechecks. -
Produces clearer error messages (
MissingOptionalDependencyErrorvs.ModuleNotFoundErroror‘NoneType’ object has no attribute ‘random’). -
Provides a standardized, language-level way to express a very common pattern in Python packages.
Compatibility
This introduces new syntax that currently raises a SyntaxError, so it would not conflict with existing code.