简体   繁体   English

捕获Python异常并打印出单独的消息

[英]Catching Python Exceptions and printing out a separate message

I'm currently trying to write code to catch exceptions, and depending upon which exception is thrown, a different module will be imported than when no exception was thrown. 我当前正在尝试编写代码以捕获异常,并且取决于引发哪个异常,将导入与不引发异常时不同的模块。

try:
  import sge_execution_engine as execution_engine
except ImportError: 
  print "Use local execution engine because SGE support is missing!"
  print sys.exc_info() # Print out the exception message
  import local_execution_engine as execution_engine
except RuntimeError:
  print "Using local execution engine because SGE support is missing!"
  print sys.exc_info()
  import local_execution_engine as execution_engine

The first exception, ImportError that is caught, catches the exception thrown when python drmaa module cannot be found during the execution of import sge_execution_engine (inside sge_execution_engine , there is an import drmaa statement). 第一个异常是捕获的ImportError ,它捕获在import sge_execution_engine执行期间找不到python drmaa模块时抛出的异常(在sge_execution_engine内部,有一个import drmaa语句)。 The second exception, RuntimeError , is caught when the drmaa python library is found (likewsie during the execution of the import drmaa statement inside the sge_execution_engine ), but the drmaa C library is not installed into the OS. 当找到drmaa python库时(在sge_execution_engine内的import drmaa语句执行期间执行likewsie),但捕获了第二个异常RuntimeError ,但drmaa C库未安装到操作系统中。 We hope that these two except statements are sufficient to catch all possible exceptions that can be thrown when a user attempts to run this module on a machine that just does not have the python drmaa library, the drmaa C library, or does not have Sun Grid Engine installed. 我们希望这两个except语句足以捕获当用户尝试在不具有python drmaa库, drmaa C库或不具有Sun Grid的计算机上运行该模块时可能引发的所有可能的异常。已安装引擎。 without any of these proceeds, the module proceeds to then import local_execution_engine and so the code can then execute on the user's machine locally. 在没有任何这些收益的情况下,模块继续进行,然后import local_execution_engine ,因此代码可以在本地用户计算机上执行。 Right now the code works as expected in the sense that it goes to import local when it finds exceptions with sge, but we are still looking to improve the exception handling here to make it more robust. 现在,代码可以按预期的方式工作,因为它会在找到带有sge的异常时将其导入本地,但我们仍在寻求改进此处的异常处理以使其更健壮。

In my opinion I think having the actual Exception message that was thrown be printed to stdout is good as it will allow the user to know why he was unable to import sge_execution_engine especially if he was not expecting it to fail being imported. 在我看来,我认为可以将实际抛出的异常消息打印到stdout很好,因为这将使用户知道为什么他无法导入sge_execution_engine,尤其是在他不希望导入失败的情况下。

However, instead of using print sys.exc_info() to actually have the actual exception message be printed on screen, I realized that perhaps a better way would be to use the except EXCEPTION as some_variable_name format and then print out print some_variable_name and also call some of the attributes associated with the Exception that is thrown and assigned to some_variable_name . 但是,我没有使用print sys.exc_info()实际在屏幕上打印实际的异常消息,而是意识到一种更好的方法可能是使用except EXCEPTION as some_variable_name格式except EXCEPTION as some_variable_name格式,然后打印出print some_variable_name并调用一些与抛出并分配给some_variable_name的Exception关联的属性的属性。

I saw this being done in the Python tutorial on exceptions where there was this chunk of code: 我在Python教程中看到了完成此代码的异常

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as e:
    print "I/O error({0}): {1}".format(e.errno, e.strerror)
except ValueError:
    print "Could not convert data to an integer."
except:
    print "Unexpected error:", sys.exc_info()[0]
    raise

It seems like the except IOError as e chunk is handling the exception message in a fine-grained way by specifically calling on the errno and strerror attributes of the IOError object. 似乎except IOError as e块通过专门调用IOError对象的errnostrerror属性以细粒度的方式处理异常消息。 However, when I look at the IOError documentation , I do not see these specific attributes being listed out as part of the documentation for the exception. 但是,当我查看IOError 文档时 ,看不到这些特定属性作为该例外的文档列出。 In fact, this is also the case for all the other exceptions under the Python documentation, so it seems there is no way we can figure out what attributes will be associated with a particular exception. 实际上,Python文档中的所有其他异常也都是这种情况,因此似乎无法确定与特定异常相关的属性。 If we don't know anything about this, then how will we be able to figure out what attributes to call on the some_variable_name object when we use the import EXCEPTION as some_variable_name syntax to handle our exceptions? 如果我们对此一无所知,那么当我们使用import EXCEPTION as some_variable_name语法来处理异常时,如何找出在some_variable_name对象上调用的属性?

I would appreciate anyone's suggestion on this, and even if your answer is not directly answering my question, but if you have another entirely different suggestion on how I could better handle my exception here, please don't hesitate to make a post! 我会很高兴有人提出的建议,即使您的回答没有直接回答我的问题,但是如果您对我如何能够更好地处理我的例外情况有另一种完全不同的建议,请不要犹豫发表评论!

Thank you very much! 非常感谢你!

First you're right that it's better to catch the exception into a variable than to ignore it and then pull it back out with sys.exc_info() . 首先您是正确的,将异常捕获到变量中比忽略它,然后使用sys.exc_info()将其撤回是更好的选择。 There are a few good reasons to use exc_info (low-level code, code that has to work with both pre-2.6 and 3.x, etc.), but in general, when you get can do it your way, you should. 有很多充分的理由使用exc_info (低级代码,必须与2.6之前的版本和3.x兼容的代码等),但是总的来说,当您可以按自己的方式进行操作时,应该这样做。

And this works even for your last bare except. 这甚至适用于您的最后一个裸手。 In 2.7, a plain except: means the same thing as except Exception: , so you can write except Exception as e: , and then use the e value. 在2.7中,简单的except:表示与except Exception:相同的东西,因此可以将except Exception as e:编写except Exception as e:然后使用e值。

Also note that if you want to do exactly the same thing for multiple exception types, you can write except (RuntimeError, ImportError) as e: . 还要注意,如果要对多种异常类型执行完全相同的操作,则可以将except (RuntimeError, ImportError) as e:except (RuntimeError, ImportError) as e:

As for "making it more robust", that's not an absolute thing. 至于“使其更强大”,这不是绝对的事情。 For example, if there's some unexpected exception that's neither a RuntimeError nor an ImportError , do you want to log it and try the fallback code, or quite and dump the traceback? 例如,如果存在一个既不是RuntimeError也不是ImportError意外异常,是否要记录该日志并尝试使用回退代码,还是完全将其转回追溯? If it's, eg, a SyntaxError caused by someone checking in a bad edit to your program, you may not want to treat that like a runtime error… or maybe you do; 如果是例如由于有人对程序进行错误编辑而导致的SyntaxError ,则您可能不希望将其视为运行时错误……或者您可以这样做。 that really depends on your development practices and target userbase. 这实际上取决于您的开发实践和目标用户群。


Meanwhile: 与此同时:

It seems like the except IOError as e chunk is handling the exception message in a fine-grained way by specifically calling on the errno and strerror attributes of the IOError object. 似乎except IOError as e块通过专门调用IOError对象的errnostrerror属性以细粒度的方式处理异常消息。 However, when I look at the IOError documentation , I do not see these specific attributes being listed out as part of the documentation for the exception. 但是,当我查看IOError文档时 ,看不到这些特定属性作为该例外的文档列出。

You need to look up the hierarchy . 您需要查找层次结构 Notice that IOError is a subclass of EnvironmentError , which does document the errno and strerror attributes. 请注意, IOErrorEnvironmentError的子类,它确实记录了errnostrerror属性。 (What these attributes actually mean is only documneted for OSError and its subclasses, but the fact that they exist is documented.) (这些属性的实际含义仅是记录了OSError及其子类的内容,但是记录了它们存在的事实。)

If you think this is all a bit of a mess… well, it is. 如果您认为这有点混乱……是的。 It's all cleaned up in Python 3.x, where IOError and EnvironmentError are merged into OSError , which clearly documents its attributes, and where you usually don't have to switch on errno in the first place because common errno values generate a specific subclass like FileNotFoundError , and so on. 所有这些都在Python 3.x中进行了清理,其中IOErrorEnvironmentError合并到OSError ,该文件清楚地记录了其属性,并且通常不必首先打开errno ,因为通用的errno值会生成特定的子类,例如FileNotFoundError ,依此类推。 But as long as you're using 2.7, you don't get the benefits of the last 6 years of improvements to the language. 但是,只要您使用的是2.7,就无法享受到最近6年对该语言的改进所带来的好处。


For example, looking at the hierarchy or ValueError=>StandardError=>Exception (from lowest to highest in the hierarchy), I can't find any attributes about it. 例如,查看层次结构或ValueError=>StandardError=>Exception (从层次结构的最低到最高),我找不到关于它的任何属性。

If you dir a ValueError , you'll see that it only has two attributes (besides the usual special stuff like __repr__ and __class_ ): args and message . 如果您将ValueError dir ,则会看到它只有两个属性(除了__repr____class_等常用的特殊东西): argsmessage

message isn't documented because it was deprecated in 2.5, and only exists in 2.7 to allow some pre-2.5 code to keep running.* But args is documented; message未记录,因为它是在2.5过时,只有在2.7存在允许一些预先2.5代码继续运行*但是, args被记录在案; you just need to go up one level further, to BaseException : 您只需BaseException一层,就可以BaseException

args

The tuple of arguments given to the exception constructor. 给异常构造函数的参数的元组。 Some built-in exceptions (like IOError ) expect a certain number of arguments and assign a special meaning to the elements of this tuple, while others are usually called only with a single string giving an error message. 一些内置的异常(例如IOError )期望有一定数量的参数,并为此元组的元素分配特殊的含义,而其他一些异常通常仅使用给出错误消息的单个字符串来调用。

So, the reason you can't find the other attributes in ValueError is that there are no other attributes to find. 因此,在ValueError找不到其他属性的原因是没有其他属性可以找到。 And the same goes for those other classes. 其他类别也是如此。 The handful of types that have special attributes ( OSError , SyntaxError , maybe a few module-specific types elsewhere in the stdlib) document them explicitly.** 少数具有特殊属性的类型( OSErrorSyntaxError ,也许是stdlib中其他位置的一些模块特定的类型)明确地记录了它们。**

If we are using the except some_exception as e syntax, is doing a print e sufficient to get the exception printed out without calling its attributes 如果我们使用except some_exception as e语法,则执行print e足以在不调用其属性的情况下将异常打印出来

It's sufficient to get some useful form of the exception printed. 获得一些有用的异常打印形式就足够了。 Again, from the BaseException docs: 再次,从BaseException文档:

If str() or unicode() is called on an instance of this class, the representation of the argument(s) to the instance are returned or the empty string when there were no arguments. 如果在此类的实例上调用str()unicode() ,则在没有参数的情况下,将返回该实例的参数表示形式或空字符串。

In some cases, that's not what you want. 在某些情况下,这不是您想要的。 In particular, notice that it doesn't include the type of the exception. 特别要注意的是,它不包括异常类型 You can get that with repr , which gives you something that looks like a constructor call (eg, ValueError("invalid literal for int() with base 10: 'f'") ). 您可以使用repr获得该功能,这将为您提供类似于构造函数调用的内容(例如ValueError("invalid literal for int() with base 10: 'f'") )。

If you want the same output you get in a traceback, you have to put the type and the str or unicode together yourself—eg, '{}: {}'.format(type(e), e) . 如果要在回溯中获得相同的输出,则必须自己将typestrunicode放在一起,例如'{}: {}'.format(type(e), e)

If you want to get the actual information out of the exception, like that base 10 or that string 'f' —well, you can't,*** because that information has already been thrown away. 如果您想从异常中获取实际信息,例如以10底或字符串'f' ,那么您就不能,***,因为该信息已被丢弃。 You'll have to write your own code to keep track of it, like: 您必须编写自己的代码来跟踪它,例如:

try:
    i = int(s, 16)
except ValueError as e:
    print '{} is not a base-16 number'.format(s)

* It's as if BaseException.__init__(self, *args) were defined to do self.args = tuple(args); self.message = str(args[0]) if args else '' *就像BaseException.__init__(self, *args)被定义为self.args = tuple(args); self.message = str(args[0]) if args else '' self.args = tuple(args); self.message = str(args[0]) if args else '' . self.args = tuple(args); self.message = str(args[0]) if args else ''

** I believe in 2.5 there were a few exception types that had undocumented attributes as an implementation detail of CPython, but by 2.7 they're all either gone or documented. **我相信在2.5中,有一些异常类型具有未记录的属性作为CPython的实现细节,但是到2.7,它们全部消失或被记录了。

*** Well, you could parse the exception string, but needless to say, that's an implementation detail, not something guaranteed to be stable and portable. ***好吧,您可以解析异常字符串,但是不用说,这是实现细节,而不是保证稳定和可移植的内容。 It could be different in Jython, or on a Spanish-language system, or it may not quote strings the way you expect, etc. 在Jython中或在西班牙语系统中,它可能有所不同,或者可能未按您期望的方式引用字符串,等等。

Using a bare except is rarely a good idea, but this is one of those rare times -- primarily because you have a backup that you want to use if, for any reason, you cannot import the system sge : 使用裸except很少是个好主意,但是这是罕见的时刻之一-主要是因为你有你想,如果因为任何原因,你不能导入系统使用备份sge

try:
    import sge_execution_engine as execution_engine
except:
    print "Use local execution engine because SGE support is missing!"
    print sys.exc_info() # Print out the exception message
    import local_execution_engine as execution_engine

Notice you now only need one except clause. 注意,您现在只需要一个except子句。

The better way to handle getting that information to the user is probably to go ahead and print it out, and then also use the logging module to make a permanent record: 处理该信息给用户的更好方法可能是继续打印并打印出来,然后使用日志记录模块进行永久记录:

import logging
logger = logging.getLogger()
logger.setLevel(logging.WARNING)

and then in your except clause add: 然后在您的except子句中添加:

    logger.exception('unable to import sge')

and your message, along with the actual exception, will be saved in a log file. 并且您的消息以及实际的异常将保存在日志文件中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM