21
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.

Community
  • 1
  • 1
MajesticRa
  • 13,770
  • 12
  • 63
  • 77

4 Answers4

23

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.
Tadhg McDonald-Jensen
  • 20,699
  • 5
  • 35
  • 59
  • Probably you are right... In my context I just didn't thought the problem is in naming error, because what I actually do in fallback is ```... connection_string.replace("mysql://", "mysql+mysqlconnector://")``` so my mind was too far away from the naming error. – MajesticRa Mar 09 '16 at 20:01
  • if you don't use the module later then delete it from the name space to indicate to pycharm (and yourself so you don't run into a NameError) that it was only intended to be used for the one part – Tadhg McDonald-Jensen Mar 09 '16 at 20:18
  • 1
    In a couple of my functions with such features, if I cannot import the library, I simply `return`. I would like to think that this acceptable behavior as well. – Bobort Oct 27 '16 at 16:23
4

In Python 3.3+, an ImportError has the attribute name that tells the name of the module whose import failed. Then of course MySQLdb would hint that you're stuck with Python 2.

4

One method of getting the name, already mentioned is using the name attribute:

try:
    import pty
except ImportError as e:
    print(e.name)

However, it is important to note that you might not get the module name you started with; namely, running the above on Windows gives you termios as the output, not pty.

To maintain compatibility with python 2.7 (for those who haven't made the switch, you're on the clock)

try:
    import pty
except ImportError as e:
    print(e.args[0].rsplit(' ',1)[-1])  # `msg` attribute is `message` on python2...stick with args

# prints the same result: termios


Some bonus tidbits:
  • Python 3.6 was released around the end of 2016. Since it's 2019+, you can opt for the clearer exception ModuleNotFoundError:

    A subclass of ImportError which is raised by import when a module could not be located. It is also raised when None is found in sys.modules.

  • If you are using Python 3.3+, don't forget about the path attribute (in addition to the name already mentioned), which gives the path to any file that raised the exception (only useful in more complex cases -- in simple examples like the one I gave path will return None.)

YenForYang
  • 2,998
  • 25
  • 22
-1

Try this:

try:
  import libname
except ImportError as e:
       e = e[0][16:]
       print("\n[!] Error: Please Install Module Name:[ {} ] And Try Again !!!".format(e))
       exit(1)

Output:

[!] Error: Please Install Module Name:[ libname ] And Try Again !!!

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Oseid
  • 31
  • 2
  • What will happen when error message is changed with python version? (That happened before). And... Thank you for investing your time in this answer, but this answer is not for this question. – MajesticRa Nov 16 '18 at 18:06