PEP Proposal: Enhanced Module Import Error Suggestions
Abstract
This PEP proposes enhancing Python’s ModuleNotFoundError messages to include intelligent suggestions for similar module names when an import fails. The enhancement will work for both top-level modules and nested submodules, helping developers quickly identify and correct typos in import statements.
Motivation
Currently, when a module import fails due to a typo or incorrect name, Python simply reports that the module was not found. This can be frustrating for developers, especially when the intended module name is very similar to what was typed. The proposed change will make Python more helpful by suggesting possible correct module names based on what is available in the environment.
Rationale
Modern IDEs often provide similar functionality, but this proposal would bring this helpful feature to all Python environments, including command-line usage. The implementation will:
-
Reduce developer frustration when dealing with import errors
-
Speed up debugging of import-related issues
-
Work consistently across all Python environments
-
Handle both top-level modules and nested submodules
Specification
Top-Level Module Suggestions
When a top-level module import fails, Python will:
-
Collect all available top-level module names
-
Compute similarity scores between the requested name and available names
-
If a sufficiently similar name exists, include it in the error message
Example:
>>>import ant
Traceback (most recent call last):
File "<python-input-0>", line 1, in <module>
import ant
^^^
ModuleNotFoundError: no module named 'ant'. Did you mean 'ast'?
Nested Module Suggestions
For nested module imports, Python will:
-
Identify exactly which part of the module path failed
-
For the failing component, suggest similar names from the parent module’s contents
-
Clearly indicate which part of the path was problematic
Examples:
>>>import multprocessing.dummy.connection #Wrong top-level module
Traceback(most recent call last):
File "<python-input-0>", line 1, in <module>
import multprocessing.dummy.connection #Wrong top-level module
^^^^^^^^^^^^^^
ModuleNotFoundError: no module named 'multprocessing'. Did you mean 'multiprocessing'?
>>>import multiprocessing.dumy.connection #Wrong first submodule
Traceback(most recent call last):
File "<python-input-1>", line 1, in <module>
import multiprocessing.dumy.connection #Wrong first submodule
~~~~~~~~~~~~~~~~^^^^
ModuleNotFoundError: module 'multiprocessing' has no child module 'dumy'. Did you mean 'dummy'?
>>>import multiprocessing.dummy.connections #Wrong first submodule
Traceback(most recent call last):
File "<python-input-2>", line 1, in <module>
import multiprocessing.dummy.connections #Wrong first submodule
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
ModuleNotFoundError: module 'multiprocessing.dummy' has no child module 'connections'. Did you mean 'connection'?
Implementation Details
Module Discovery: Use pkgutil.iter_modules() to find available top-level modules
Similarity Calculation: Use string similarity algorithms (e.g., Levenshtein distance) to find close matches
Submodule Inspection: For nested modules, inspect the parent module’s “_path_” attribute to find available submodules
Performance: Cache module lists to avoid performance impact on repeated import attempts
Threshold: Only show suggestions when the similarity score exceeds a reasonable threshold
Backwards Compatibility
This change is fully backwards compatible as it only enhances error messages without modifying any existing behavior. The change only affects the content of ModuleNotFoundError messages.
Security Considerations
The enhanced error messages will reveal information about installed modules on the system. However, this information is already available through other means (like help(‘modules’)), so the security impact is minimal.
Reference Implementation
proof-of-concept implementation could use the following approach:
import pkgutil
import difflib
def get_similar_modules(name):
available = [m.name for m in pkgutil.iter_modules()]
return difflib.get_close_matches(name, available, n=1, cutoff=0.6)
def enhance_import_error(name, error):
if isinstance(error, ModuleNotFoundError):
if '.' in name:
# Handle nested modules
parts = name.split('.')
# Implementation would check each part sequentially
else:
suggestions = get_similar_modules(name)
if suggestions:
error.msg = f"{error.msg}. Did you mean '{suggestions[0]}'?"
return error
Rejected Alternatives
-
Showing multiple suggestions: While possible, showing just the top suggestion keeps messages concise
-
Making it a warning first: The error should remain fatal as the import cannot proceed
-
Configurable similarity threshold: A reasonable default should work for most cases
Copyright
This document has been placed in the public domain.