1

In Python, when an import fails, how can I differentiate between:

  • The module doesn't exist.
  • The module exists, but it tried importing another module that didn't exist.

Example

# ./first.py

try:
    import second
except ImportError:
    print("second.py doesn't exist")
# ./second.py

import third  # ModuleNotFoundError: No module named "third"

# Do stuff...
def foo():
   ...
>>> import first
second.py doesn't exist

The error message printed in this example is incorrect. second.py does exist, and the ImportError is actually due to second.py itself containing an invalid import.

In this case, I want all transitive errors in second.py to propagate un-caught. The only exception I want to catch is the case where there is no file second.py to import.


This paradigm has been discussed on Software Engineering SE but without a method for differentiation.

Yes, I am fully aware that this is a strange situation and probably smells like an xy-problem. This is all for some temporary testing, where second.py is a script I'm creating and deleting as I test things: not strictly necessary, but now I'm interested in the question theoretically.

2 Answers 2

3

In your question you have already noted that ModuleNotFoundError exists as a separate exception to ImportError (in fact it is a subclass of ImportError). If you catch this more narrow exception, and then check its name attributes then you can be resonably sure what happened. eg.

try:
   import second
except ModuleNotFoundError as ex:
   if ex.name == 'second':
       print('second.py does not exist')
   else:
       raise

The one exception where second.py might exist is if someone replaced the second module in sys.modules with None eg.

import sys
import second
sys.modules['second'] = None
# all attempts to import second will now fail, even though this bit
# of code still has a reference to it

import second  # ModuleNotFoundError
Sign up to request clarification or add additional context in comments.

3 Comments

Keeping an eye on sys.modules is a good thought! Though it's unlikely to come up while OP is using this to test their own code. That said, I won't say I've never been a victim of my own coding self-sabotage!
Could this ever run into a collision issue where the name "second" refers to some other file in another package? So that second imports third, which imports a different, unrelated second? I think the answer is no, that it would be "something.else.second" in that case, but my grasp of Python imports is intermediate at best.
Each fully qualified module name is unique in a python program. So yes, there can only be one second module. Any other second.py would need to qualified such as in your example. Is is possible that there could be multiple candidates for second. For instance, if PYTHONPATH has multiple locations, or when using a namespace package. However, python will only ever attempt to import the first found. All others will be shadowed by this one, and so unreachable. Python has a strict order in how it searches for modules. So which file will be imported is repeatable and predictable.
1

Would something like this suit you?

# first.py

try:
    import second
except (ImportError, ModuleNotFoundError) as err:
    match str(err):
        case "No module named 'second'":
            print('"second.py" doesn\'t exist')
        case "No module named 'third'":
            print('"third.py" doesn\'t exist')

        # add other cases for dependencies in the chain as needed

        case _:  # everything else
            print(str(err))
else:
    print('everything is fine')

It's certainly a bit rudimentary, but it does work.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.