Enhanced Module Import Error Suggestions

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.

This doesn’t need a PEP.

1 Like

Not only doesn’t it need a PEP, this is something that’s mostly already implemented for 3.14 in the REPL context.

Closing the topic.