Search code examples
pythonexceptionpycharmimporterror

Checking module name inside 'except ImportError'


try:
   import MySQLdb
   # some action
except ImportError as err:
   # fallback code

PyCharm gives a code inspection warning on that:

'MySQLdb' in try block with 'except ImportError' should also be defined in except block

This inspection detects names that should resolve but don't. Due to dynamic dispatch and duck typing, this is possible in a limited but useful number of cases. Top-level and class-level items are supported better than instance items.

Ok, I thought the warning is reasonable, because the fallback code assumes that 'MySQLdb' is not installed, while it could be some different error that just raised ImportError. So I used something like:

try:
   import MySQLdb
   # some action
except ImportError as err:
   if "MySQLdb" in repr(err):
       # fallback code
   else:
       raise

The PyCharm alert is still exists, but It could be just a PyCharm issue (google shows issues with such inspections)

Questions:

  1. Is it really worth checking for name when you "except ImportError"? Even in simple cases (no some action after import MySQLdb)?

  2. If it worth checking, Is the above example the right way to do it? If no - what is the right way?

P.S. MySQLdb is just an example of a module that could be absent in the system.


Solution

  • I think you misunderstood the warning, if you do not define a variable called MySQLdb in the except block then later on when you try to use the module you would get a NameError:

    try:
        import foo
    except ImportError:
        pass
    
    foo.say_foo() #foo may or may not be defined at this point!
    

    If the module is only used in the try: clause then this is no issue. But for the more general case the checker expects you to define the variable in the except block:

    try:
        import foo
    except ImportError:
        foo = None  #now foo always exists
    
    if foo: #if the module is present
        foo.say_foo()
    else:
        print("foo") #backup use
    

    If the module is only used in the try block then you can indicate to the checker (and yourself) that you can't use the module later by deleting it from the namespace:

    try:
        import foo
    except ImportError:
        pass
    else:
        # if it was able to import use it then get rid of the local variable
        foo.do_thing()
        del foo #optional module should not be relied on outside 
     
    
    # now foo never exists here, checker is happy.