标签归档:exception

您如何捕获此异常?

问题:您如何捕获此异常?

这段代码在django / db / models / fields.py中。它创建/定义一个异常吗?

class ReverseSingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjectDescriptorMethods)):
    # This class provides the functionality that makes the related-object
    # managers available as attributes on a model class, for fields that have
    # a single "remote" value, on the class that defines the related field.
    # In the example "choice.poll", the poll attribute is a
    # ReverseSingleRelatedObjectDescriptor instance.
    def __init__(self, field_with_rel):
        self.field = field_with_rel
        self.cache_name = self.field.get_cache_name()

    @cached_property
    def RelatedObjectDoesNotExist(self):
        # The exception can't be created at initialization time since the
        # related model might not be resolved yet; `rel.to` might still be
        # a string model reference.
        return type(
            str('RelatedObjectDoesNotExist'),
            (self.field.rel.to.DoesNotExist, AttributeError),
            {}
        )

这在django / db / models / fields / related.py中,它在上面引发了上述异常:

def __get__(self, instance, instance_type=None):
    if instance is None:
        return self
    try:
        rel_obj = getattr(instance, self.cache_name)
    except AttributeError:
        val = self.field.get_local_related_value(instance)
        if None in val:
            rel_obj = None
        else:
            params = dict(
                (rh_field.attname, getattr(instance, lh_field.attname))
                for lh_field, rh_field in self.field.related_fields)
            qs = self.get_queryset(instance=instance)
            extra_filter = self.field.get_extra_descriptor_filter(instance)
            if isinstance(extra_filter, dict):
                params.update(extra_filter)
                qs = qs.filter(**params)
            else:
                qs = qs.filter(extra_filter, **params)
            # Assuming the database enforces foreign keys, this won't fail.
            rel_obj = qs.get()
            if not self.field.rel.multiple:
                setattr(rel_obj, self.field.related.get_cache_name(), instance)
        setattr(instance, self.cache_name, rel_obj)
    if rel_obj is None and not self.field.null:
        raise self.RelatedObjectDoesNotExist(
            "%s has no %s." % (self.field.model.__name__, self.field.name)
        )
    else:
        return rel_obj

问题是此代码:

    try:
        val = getattr(obj, attr_name)
    except related.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist:
        val = None  # Does not catch the thrown exception
    except Exception as foo:
        print type(foo)  # Catches here, not above

不会捕获该异常

>>>print type(foo)
<class 'django.db.models.fields.related.RelatedObjectDoesNotExist'>
>>>isinstance(foo, related.FieldDoesNotExist)
False

except related.RelatedObjectDoesNotExist:

提出一个 AttributeError: 'module' object has no attribute 'RelatedObjectDoesNotExist'

>>>isinstance(foo, related.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist)
Traceback (most recent call last):
  File "<string>", line 1, in <fragment>
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types

这可能是为什么。

This code is in django/db/models/fields.py It creates/defines an exception?

class ReverseSingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjectDescriptorMethods)):
    # This class provides the functionality that makes the related-object
    # managers available as attributes on a model class, for fields that have
    # a single "remote" value, on the class that defines the related field.
    # In the example "choice.poll", the poll attribute is a
    # ReverseSingleRelatedObjectDescriptor instance.
    def __init__(self, field_with_rel):
        self.field = field_with_rel
        self.cache_name = self.field.get_cache_name()

    @cached_property
    def RelatedObjectDoesNotExist(self):
        # The exception can't be created at initialization time since the
        # related model might not be resolved yet; `rel.to` might still be
        # a string model reference.
        return type(
            str('RelatedObjectDoesNotExist'),
            (self.field.rel.to.DoesNotExist, AttributeError),
            {}
        )

This is in django/db/models/fields/related.py it raises the said exception above:

def __get__(self, instance, instance_type=None):
    if instance is None:
        return self
    try:
        rel_obj = getattr(instance, self.cache_name)
    except AttributeError:
        val = self.field.get_local_related_value(instance)
        if None in val:
            rel_obj = None
        else:
            params = dict(
                (rh_field.attname, getattr(instance, lh_field.attname))
                for lh_field, rh_field in self.field.related_fields)
            qs = self.get_queryset(instance=instance)
            extra_filter = self.field.get_extra_descriptor_filter(instance)
            if isinstance(extra_filter, dict):
                params.update(extra_filter)
                qs = qs.filter(**params)
            else:
                qs = qs.filter(extra_filter, **params)
            # Assuming the database enforces foreign keys, this won't fail.
            rel_obj = qs.get()
            if not self.field.rel.multiple:
                setattr(rel_obj, self.field.related.get_cache_name(), instance)
        setattr(instance, self.cache_name, rel_obj)
    if rel_obj is None and not self.field.null:
        raise self.RelatedObjectDoesNotExist(
            "%s has no %s." % (self.field.model.__name__, self.field.name)
        )
    else:
        return rel_obj

The problem is that this code:

    try:
        val = getattr(obj, attr_name)
    except related.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist:
        val = None  # Does not catch the thrown exception
    except Exception as foo:
        print type(foo)  # Catches here, not above

won’t catch that exception

>>>print type(foo)
<class 'django.db.models.fields.related.RelatedObjectDoesNotExist'>
>>>isinstance(foo, related.FieldDoesNotExist)
False

and

except related.RelatedObjectDoesNotExist:

Raises an AttributeError: 'module' object has no attribute 'RelatedObjectDoesNotExist'

>>>isinstance(foo, related.ReverseSingleRelatedObjectDescriptor.RelatedObjectDoesNotExist)
Traceback (most recent call last):
  File "<string>", line 1, in <fragment>
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types

which is probably why.


回答 0

如果您的相关模型称为Foo,则可以执行以下操作:

except Foo.DoesNotExist:

Django令人惊叹,但并不可怕。RelatedObjectDoesNotExist是一个属性,该属性返回在运行时动态确定的类型。该类型self.field.rel.to.DoesNotExist用作基类。根据Django文档:

对象不存在和不存在

异常DowsNotExist

DoesNotExist时未找到查询的给定参数的对象异常。Django提供了DidNotExist 异常作为每个模型类的属性,以标识找不到的对象类,并允许您使用try/except捕获特定的模型类。

这就是使之成为现实的魔力。一旦建立了模型,该模型self.field.rel.to.DoesNotExist就是不存在的异常。

If your related model is called Foo you can just do:

except Foo.DoesNotExist:

Django is amazing when its not terrifying. RelatedObjectDoesNotExist is a property that returns a type that is figured out dynamically at runtime. That type uses self.field.rel.to.DoesNotExist as a base class. According to Django documentation:

ObjectDoesNotExist and DoesNotExist

exception DoesNotExist

The DoesNotExist exception is raised when an object is not found for the given parameters of a query. Django provides a DoesNotExist exception as an attribute of each model class to identify the class of object that could not be found and to allow you to catch a particular model class with try/except.

This is the magic that makes that happen. Once the model has been built up, self.field.rel.to.DoesNotExist is the does-not-exist exception for that model.


回答 1

如果您不想导入相关的模型类,则可以:

except MyModel.related_field.RelatedObjectDoesNotExist:

要么

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

哪里 related_field字段名称。

If you don’t want to import the related model class, you can:

except MyModel.related_field.RelatedObjectDoesNotExist:

or

except my_model_instance._meta.model.related_field.RelatedObjectDoesNotExist:

where related_field is the field name.


回答 2

要捕获此异常,通常可以

from django.core.exceptions import ObjectDoesNotExist

try:
    # Your code here
except ObjectDoesNotExist:
    # Handle exception

To catch this exception in general, you can do

from django.core.exceptions import ObjectDoesNotExist

try:
    # Your code here
except ObjectDoesNotExist:
    # Handle exception

回答 3

RelatedObjectDoesNotExist在运行时动态创建的exceptions。以下是ForwardManyToOneDescriptorReverseOneToOneDescriptor描述符的相关代码段:

@cached_property
def RelatedObjectDoesNotExist(self):
    # The exception can't be created at initialization time since the
    # related model might not be resolved yet; `self.field.model` might
    # still be a string model reference.
    return type(
        'RelatedObjectDoesNotExist',
        (self.field.remote_field.model.DoesNotExist, AttributeError),
        {}
    )

因此,异常继承自<model name>.DoesNotExistAttributeError。实际上,此异常类型的完整MRO为:

[<class 'django.db.models.fields.related_descriptors.RelatedObjectDoesNotExist'>, 
<class '<model module path>.DoesNotExist'>,
<class 'django.core.exceptions.ObjectDoesNotExist'>,
<class 'AttributeError'>,
<class 'Exception'>,
<class 'BaseException'>,
<class 'object'>]

基本的要点是您可以捕获<model name>.DoesNotExistObjectDoesNotExist(从导入django.core.exceptions)或AttributeError,在您的上下文中最有意义的。

The RelatedObjectDoesNotExist exception is created dynamically at runtime. Here is the relevant code snippet for the ForwardManyToOneDescriptor and ReverseOneToOneDescriptor descriptors:

@cached_property
def RelatedObjectDoesNotExist(self):
    # The exception can't be created at initialization time since the
    # related model might not be resolved yet; `self.field.model` might
    # still be a string model reference.
    return type(
        'RelatedObjectDoesNotExist',
        (self.field.remote_field.model.DoesNotExist, AttributeError),
        {}
    )

So the exception inherits from <model name>.DoesNotExist and AttributeError. In fact, the complete MRO for this exception type is:

[<class 'django.db.models.fields.related_descriptors.RelatedObjectDoesNotExist'>, 
<class '<model module path>.DoesNotExist'>,
<class 'django.core.exceptions.ObjectDoesNotExist'>,
<class 'AttributeError'>,
<class 'Exception'>,
<class 'BaseException'>,
<class 'object'>]

The basic takeaway is you can catch <model name>.DoesNotExist, ObjectDoesNotExist (import from django.core.exceptions) or AttributeError, whatever makes the most sense in your context.


回答 4

tdelaney的答案非常适合常规代码路径,但是如果您需要知道如何在测试中捕获此异常,则:

from django.core.exceptions import ObjectDoesNotExist

...

    def testCompanyRequired(self):
        with self.assertRaises(ObjectDoesNotExist):
            employee = Employee.objects.create()

tdelaney’s answer is great for regular code paths, but if you need to know how to catch this exception in tests:

from django.core.exceptions import ObjectDoesNotExist

...

    def testCompanyRequired(self):
        with self.assertRaises(ObjectDoesNotExist):
            employee = Employee.objects.create()

回答 5

有点晚了,但对其他人有帮助。

有两种处理方法。

第一:

当我们需要捕获异常时

>>> from django.core.exceptions import ObjectDoesNotExist
>>> try:
>>>     p2.restaurant
>>> except ObjectDoesNotExist:
>>>     print("There is no restaurant here.")
There is no restaurant here.

第二: 当不想处理异常时

>>> hasattr(p2, 'restaurant')
False

Little bit late but helpful for others.

2 ways to handle this.

1st :

When we need to catch exception

>>> from django.core.exceptions import ObjectDoesNotExist
>>> try:
>>>     p2.restaurant
>>> except ObjectDoesNotExist:
>>>     print("There is no restaurant here.")
There is no restaurant here.

2nd: When don’t want to handle exception

>>> hasattr(p2, 'restaurant')
False

带回溯的日志异常

问题:带回溯的日志异常

如何记录我的Python错误?

try:
    do_something()
except:
    # How can I log my exception here, complete with its traceback?

How can I log my Python errors?

try:
    do_something()
except:
    # How can I log my exception here, complete with its traceback?

回答 0

使用logging.exception从内except:处理/块与跟踪信息,与消息前缀一起记录当前异常。

import logging
LOG_FILENAME = '/tmp/logging_example.out'
logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG)

logging.debug('This message should go to the log file')

try:
    run_my_stuff()
except:
    logging.exception('Got exception on main handler')
    raise

现在查看日志文件/tmp/logging_example.out

DEBUG:root:This message should go to the log file
ERROR:root:Got exception on main handler
Traceback (most recent call last):
  File "/tmp/teste.py", line 9, in <module>
    run_my_stuff()
NameError: name 'run_my_stuff' is not defined

Use logging.exception from within the except: handler/block to log the current exception along with the trace information, prepended with a message.

import logging
LOG_FILENAME = '/tmp/logging_example.out'
logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG)

logging.debug('This message should go to the log file')

try:
    run_my_stuff()
except:
    logging.exception('Got exception on main handler')
    raise

Now looking at the log file, /tmp/logging_example.out:

DEBUG:root:This message should go to the log file
ERROR:root:Got exception on main handler
Traceback (most recent call last):
  File "/tmp/teste.py", line 9, in <module>
    run_my_stuff()
NameError: name 'run_my_stuff' is not defined

回答 1

使用exc_info选项可能更好,但仍会显示警告或错误标题:

try:
    # coode in here
except Exception as e:
    logging.error(e, exc_info=True)

Use exc_info options may be better, remains warning or error title:

try:
    # coode in here
except Exception as e:
    logging.error(e, exc_info=True)

回答 2

最近,我的工作要求我记录应用程序中的所有回溯/异常。我尝试了其他人在网上发布的许多技术,例如上面的一种,但选择了另一种方法。覆盖traceback.print_exception

我在http://www.bbarrows.com/上写了一篇文章,它很容易阅读,但我也会将其粘贴在这里。

当任务是记录我们的软件在野外可能遇到的所有异常时,我尝试了多种不同的技术来记录我们的python异常回溯。起初,我认为python系统异常挂钩sys.excepthook将是插入日志记录代码的理想场所。我正在尝试类似的东西:

import traceback
import StringIO
import logging
import os, sys

def my_excepthook(excType, excValue, traceback, logger=logger):
    logger.error("Logging an uncaught exception",
                 exc_info=(excType, excValue, traceback))

sys.excepthook = my_excepthook  

这适用于主线程,但是我很快发现我的sys.excepthook在进程启动的任何新线程中都不存在。这是一个很大的问题,因为大多数事情都发生在该项目的线程中。

仔细阅读并阅读大量文档后,我发现最有用的信息来自Python问题跟踪器。

线程的第一篇文章显示了一个sys.excepthook跨线程不持久的工作示例(如下所示)。显然,这是预期的行为。

import sys, threading

def log_exception(*args):
    print 'got exception %s' % (args,)
sys.excepthook = log_exception

def foo():
    a = 1 / 0

threading.Thread(target=foo).start()

该Python Issue线程上的消息确实导致了2条建议的hack。可以将子类Thread并将run方法包装在我们自己的tryexcept块中以捕获和记录异常,或者将Monkey补丁threading.Thread.run以您自己的tryexcept块中的方式运行,以阻止和记录异常。

Thread我看来,第一种子类化方法在您的代码中似乎不太优雅,因为您必须在Thread想要拥有日志记录线程的任何地方导入和使用自定义类。最终这很麻烦,因为我不得不搜索我们的整个代码库,并Threads用此自定义替换所有常规代码Thread。但是,很清楚这Thread是在做什么,如果自定义日志代码出了问题,则对于某人来说,诊断和调试将更容易。定制日志记录线程可能如下所示:

class TracebackLoggingThread(threading.Thread):
    def run(self):
        try:
            super(TracebackLoggingThread, self).run()
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception, e:
            logger = logging.getLogger('')
            logger.exception("Logging an uncaught exception")

Monkey修补的第二种方法threading.Thread.run很好,因为我可以立即运行一次,__main__并在所有异常中检测日志记录代码。Monkey修补可能会令人讨厌调试,因为它会更改某些功能的预期功能。来自Python问题跟踪器的建议补丁为:

def installThreadExcepthook():
    """
    Workaround for sys.excepthook thread bug
    From
http://spyced.blogspot.com/2007/06/workaround-for-sysexcepthook-bug.html

(https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1230540&group_id=5470).
    Call once from __main__ before creating any threads.
    If using psyco, call psyco.cannotcompile(threading.Thread.run)
    since this replaces a new-style class method.
    """
    init_old = threading.Thread.__init__
    def init(self, *args, **kwargs):
        init_old(self, *args, **kwargs)
        run_old = self.run
        def run_with_except_hook(*args, **kw):
            try:
                run_old(*args, **kw)
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                sys.excepthook(*sys.exc_info())
        self.run = run_with_except_hook
    threading.Thread.__init__ = init

直到我开始测试异常日志记录时,我才意识到自己在处理所有错误。

为了测试,我放置了一个

raise Exception("Test")

在我的代码中的某处。但是,包装一个称为该方法的方法是一种尝试,除了打印出回溯并吞没了异常的块。这非常令人沮丧,因为我看到回溯将打印输出到STDOUT,但是没有被记录下来。然后我决定,记录回溯的一种更简单的方法就是Monkey补丁所有Python代码用来打印回溯的方法traceback.print_exception。我最终得到了类似于以下内容的东西:

def add_custom_print_exception():
    old_print_exception = traceback.print_exception
    def custom_print_exception(etype, value, tb, limit=None, file=None):
        tb_output = StringIO.StringIO()
        traceback.print_tb(tb, limit, tb_output)
        logger = logging.getLogger('customLogger')
        logger.error(tb_output.getvalue())
        tb_output.close()
        old_print_exception(etype, value, tb, limit=None, file=None)
    traceback.print_exception = custom_print_exception

此代码将回溯写到字符串缓冲区,并将其记录到日志记录错误中。我有一个自定义日志记录处理程序,它设置了’customLogger’记录器,该记录器将使用ERROR级日志并将其发送回家进行分析。

My job recently tasked me with logging all the tracebacks/exceptions from our application. I tried numerous techniques that others had posted online such as the one above but settled on a different approach. Overriding traceback.print_exception.

I have a write up at http://www.bbarrows.com/ That would be much easier to read but Ill paste it in here as well.

When tasked with logging all the exceptions that our software might encounter in the wild I tried a number of different techniques to log our python exception tracebacks. At first I thought that the python system exception hook, sys.excepthook would be the perfect place to insert the logging code. I was trying something similar to:

import traceback
import StringIO
import logging
import os, sys

def my_excepthook(excType, excValue, traceback, logger=logger):
    logger.error("Logging an uncaught exception",
                 exc_info=(excType, excValue, traceback))

sys.excepthook = my_excepthook  

This worked for the main thread but I soon found that the my sys.excepthook would not exist across any new threads my process started. This is a huge issue because most everything happens in threads in this project.

After googling and reading plenty of documentation the most helpful information I found was from the Python Issue tracker.

The first post on the thread shows a working example of the sys.excepthook NOT persisting across threads (as shown below). Apparently this is expected behavior.

import sys, threading

def log_exception(*args):
    print 'got exception %s' % (args,)
sys.excepthook = log_exception

def foo():
    a = 1 / 0

threading.Thread(target=foo).start()

The messages on this Python Issue thread really result in 2 suggested hacks. Either subclass Thread and wrap the run method in our own try except block in order to catch and log exceptions or monkey patch threading.Thread.run to run in your own try except block and log the exceptions.

The first method of subclassing Thread seems to me to be less elegant in your code as you would have to import and use your custom Thread class EVERYWHERE you wanted to have a logging thread. This ended up being a hassle because I had to search our entire code base and replace all normal Threads with this custom Thread. However, it was clear as to what this Thread was doing and would be easier for someone to diagnose and debug if something went wrong with the custom logging code. A custome logging thread might look like this:

class TracebackLoggingThread(threading.Thread):
    def run(self):
        try:
            super(TracebackLoggingThread, self).run()
        except (KeyboardInterrupt, SystemExit):
            raise
        except Exception, e:
            logger = logging.getLogger('')
            logger.exception("Logging an uncaught exception")

The second method of monkey patching threading.Thread.run is nice because I could just run it once right after __main__ and instrument my logging code in all exceptions. Monkey patching can be annoying to debug though as it changes the expected functionality of something. The suggested patch from the Python Issue tracker was:

def installThreadExcepthook():
    """
    Workaround for sys.excepthook thread bug
    From
http://spyced.blogspot.com/2007/06/workaround-for-sysexcepthook-bug.html

(https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1230540&group_id=5470).
    Call once from __main__ before creating any threads.
    If using psyco, call psyco.cannotcompile(threading.Thread.run)
    since this replaces a new-style class method.
    """
    init_old = threading.Thread.__init__
    def init(self, *args, **kwargs):
        init_old(self, *args, **kwargs)
        run_old = self.run
        def run_with_except_hook(*args, **kw):
            try:
                run_old(*args, **kw)
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                sys.excepthook(*sys.exc_info())
        self.run = run_with_except_hook
    threading.Thread.__init__ = init

It was not until I started testing my exception logging I realized that I was going about it all wrong.

To test I had placed a

raise Exception("Test")

somewhere in my code. However, wrapping a a method that called this method was a try except block that printed out the traceback and swallowed the exception. This was very frustrating because I saw the traceback bring printed to STDOUT but not being logged. It was I then decided that a much easier method of logging the tracebacks was just to monkey patch the method that all python code uses to print the tracebacks themselves, traceback.print_exception. I ended up with something similar to the following:

def add_custom_print_exception():
    old_print_exception = traceback.print_exception
    def custom_print_exception(etype, value, tb, limit=None, file=None):
        tb_output = StringIO.StringIO()
        traceback.print_tb(tb, limit, tb_output)
        logger = logging.getLogger('customLogger')
        logger.error(tb_output.getvalue())
        tb_output.close()
        old_print_exception(etype, value, tb, limit=None, file=None)
    traceback.print_exception = custom_print_exception

This code writes the traceback to a String Buffer and logs it to logging ERROR. I have a custom logging handler set up the ‘customLogger’ logger which takes the ERROR level logs and send them home for analysis.


回答 3

您可以通过将处理程序分配给来记录主线程上所有未捕获的异常sys.excepthook,也许使用exc_infoPython的记录函数参数

import sys
import logging

logging.basicConfig(filename='/tmp/foobar.log')

def exception_hook(exc_type, exc_value, exc_traceback):
    logging.error(
        "Uncaught exception",
        exc_info=(exc_type, exc_value, exc_traceback)
    )

sys.excepthook = exception_hook

raise Exception('Boom')

如果你的程序使用的线程,然而,然后记下创建的线程使用threading.Thread不会触发sys.excepthook时未捕获的异常在他们里面发生,在指出问题1230540 Python的问题跟踪器。已经有人提出了一些可以解决此限制的技巧,例如Monkey修补Thread.__init__程序self.run用另run一种方法覆盖,该方法将原始文件包装在一个try块中并sys.excepthook从该except块内部进行调用。或者,您可以手动将每个线程的入口点包装在try/ except自己中。

You can log all uncaught exceptions on the main thread by assigning a handler to sys.excepthook, perhaps using the exc_info parameter of Python’s logging functions:

import sys
import logging

logging.basicConfig(filename='/tmp/foobar.log')

def exception_hook(exc_type, exc_value, exc_traceback):
    logging.error(
        "Uncaught exception",
        exc_info=(exc_type, exc_value, exc_traceback)
    )

sys.excepthook = exception_hook

raise Exception('Boom')

If your program uses threads, however, then note that threads created using threading.Thread will not trigger sys.excepthook when an uncaught exception occurs inside them, as noted in Issue 1230540 on Python’s issue tracker. Some hacks have been suggested there to work around this limitation, like monkey-patching Thread.__init__ to overwrite self.run with an alternative run method that wraps the original in a try block and calls sys.excepthook from inside the except block. Alternatively, you could just manually wrap the entry point for each of your threads in try/except yourself.


回答 4

未捕获的异常消息将发送到STDERR,因此,您可以使用用于运行Python脚本的任何Shell将STDERR发送到文件,而不是在Python本身中实现日志记录。在Bash脚本中,您可以使用输出重定向来执行此操作,如BASH指南中所述

例子

将错误附加到文件,其他输出到终端:

./test.py 2>> mylog.log

用交错的STDOUT和STDERR输出覆盖文件:

./test.py &> mylog.log

Uncaught exception messages go to STDERR, so instead of implementing your logging in Python itself you could send STDERR to a file using whatever shell you’re using to run your Python script. In a Bash script, you can do this with output redirection, as described in the BASH guide.

Examples

Append errors to file, other output to the terminal:

./test.py 2>> mylog.log

Overwrite file with interleaved STDOUT and STDERR output:

./test.py &> mylog.log

回答 5

我在寻找什么:

import sys
import traceback

exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)

看到:

What I was looking for:

import sys
import traceback

exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)

See:


回答 6

您可以使用记录器在任何级别(调试,信息等)获取回溯。请注意,使用logging.exception,级别为ERROR。

# test_app.py
import sys
import logging

logging.basicConfig(level="DEBUG")

def do_something():
    raise ValueError(":(")

try:
    do_something()
except Exception:
    logging.debug("Something went wrong", exc_info=sys.exc_info())
DEBUG:root:Something went wrong
Traceback (most recent call last):
  File "test_app.py", line 10, in <module>
    do_something()
  File "test_app.py", line 7, in do_something
    raise ValueError(":(")
ValueError: :(

编辑:

这也可以工作(使用python 3.6)

logging.debug("Something went wrong", exc_info=True)

You can get the traceback using a logger, at any level (DEBUG, INFO, …). Note that using logging.exception, the level is ERROR.

# test_app.py
import sys
import logging

logging.basicConfig(level="DEBUG")

def do_something():
    raise ValueError(":(")

try:
    do_something()
except Exception:
    logging.debug("Something went wrong", exc_info=sys.exc_info())
DEBUG:root:Something went wrong
Traceback (most recent call last):
  File "test_app.py", line 10, in <module>
    do_something()
  File "test_app.py", line 7, in do_something
    raise ValueError(":(")
ValueError: :(

EDIT:

This works too (using python 3.6)

logging.debug("Something went wrong", exc_info=True)

回答 7

这是使用sys.excepthook的版本

import traceback
import sys

logger = logging.getLogger()

def handle_excepthook(type, message, stack):
     logger.error(f'An unhandled exception occured: {message}. Traceback: {traceback.format_tb(stack)}')

sys.excepthook = handle_excepthook

Here is a version that uses sys.excepthook

import traceback
import sys

logger = logging.getLogger()

def handle_excepthook(type, message, stack):
     logger.error(f'An unhandled exception occured: {message}. Traceback: {traceback.format_tb(stack)}')

sys.excepthook = handle_excepthook

回答 8

也许不那么时尚,但是更容易:

#!/bin/bash
log="/var/log/yourlog"
/path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)

maybe not as stylish, but easier:

#!/bin/bash
log="/var/log/yourlog"
/path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)

回答 9

这是取自python 2.6文档的一个简单示例:

import logging
LOG_FILENAME = '/tmp/logging_example.out'
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,)

logging.debug('This message should go to the log file')

Heres a simple example taken from the python 2.6 documentation:

import logging
LOG_FILENAME = '/tmp/logging_example.out'
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,)

logging.debug('This message should go to the log file')

Python中的“内部异常”(带有追溯)?

问题:Python中的“内部异常”(带有追溯)?

我的背景是C#,最近刚开始使用Python编程。当引发异常时,我通常希望将其包装在添加更多信息的另一个异常中,同时仍显示完整的堆栈跟踪。在C#中这很容易,但是如何在Python中做到呢?

例如。在C#中,我将执行以下操作:

try
{
  ProcessFile(filePath);
}
catch (Exception ex)
{
  throw new ApplicationException("Failed to process file " + filePath, ex);
}

在Python中,我可以执行类似的操作:

try:
  ProcessFile(filePath)
except Exception as e:
  raise Exception('Failed to process file ' + filePath, e)

…但是这丢失了对内部异常的追溯!

编辑:我想同时看到异常消息和堆栈跟踪,并将两者关联起来。也就是说,我想在输出中看到异常X在这里发生,然后异常Y在这里发生-与我在C#中一样。这在Python 2.6中可行吗?到目前为止,看来我能做的最好的(根据Glenn Maynard的回答)是:

try:
  ProcessFile(filePath)
except Exception as e:
  raise Exception('Failed to process file' + filePath, e), None, sys.exc_info()[2]

这既包括消息,也包括回溯,但是没有显示回溯中哪个异常发生。

My background is in C# and I’ve just recently started programming in Python. When an exception is thrown I typically want to wrap it in another exception that adds more information, while still showing the full stack trace. It’s quite easy in C#, but how do I do it in Python?

Eg. in C# I would do something like this:

try
{
  ProcessFile(filePath);
}
catch (Exception ex)
{
  throw new ApplicationException("Failed to process file " + filePath, ex);
}

In Python I can do something similar:

try:
  ProcessFile(filePath)
except Exception as e:
  raise Exception('Failed to process file ' + filePath, e)

…but this loses the traceback of the inner exception!

Edit: I’d like to see both exception messages and both stack traces and correlate the two. That is, I want to see in the output that exception X occurred here and then exception Y there – same as I would in C#. Is this possible in Python 2.6? Looks like the best I can do so far (based on Glenn Maynard’s answer) is:

try:
  ProcessFile(filePath)
except Exception as e:
  raise Exception('Failed to process file' + filePath, e), None, sys.exc_info()[2]

This includes both the messages and both the tracebacks, but it doesn’t show which exception occurred where in the traceback.


回答 0

Python 2

这很简单; 将回溯作为第三个引发的参数。

import sys
class MyException(Exception): pass

try:
    raise TypeError("test")
except TypeError, e:
    raise MyException(), None, sys.exc_info()[2]

捕获一个异常并重新引发另一个异常时,请始终执行此操作。

Python 2

It’s simple; pass the traceback as the third argument to raise.

import sys
class MyException(Exception): pass

try:
    raise TypeError("test")
except TypeError, e:
    raise MyException(), None, sys.exc_info()[2]

Always do this when catching one exception and re-raising another.


回答 1

Python 3

在python 3中,您可以执行以下操作:

try:
    raise MyExceptionToBeWrapped("I have twisted my ankle")

except MyExceptionToBeWrapped as e:

    raise MyWrapperException("I'm not in a good shape") from e

这将产生如下内容:

   Traceback (most recent call last):
   ...
   MyExceptionToBeWrapped: ("I have twisted my ankle")

The above exception was the direct cause of the following exception:

   Traceback (most recent call last):
   ...
   MyWrapperException: ("I'm not in a good shape")

Python 3

In python 3 you can do the following:

try:
    raise MyExceptionToBeWrapped("I have twisted my ankle")

except MyExceptionToBeWrapped as e:

    raise MyWrapperException("I'm not in a good shape") from e

This will produce something like this:

   Traceback (most recent call last):
   ...
   MyExceptionToBeWrapped: ("I have twisted my ankle")

The above exception was the direct cause of the following exception:

   Traceback (most recent call last):
   ...
   MyWrapperException: ("I'm not in a good shape")

回答 2

Python 3具有raisefrom子句以链接异常。Glenn的答案对于Python 2.7非常有用,但是它仅使用原始异常的回溯,并丢弃了错误消息和其他详细信息。以下是Python 2.7中的一些示例,这些示例将当前作用域的上下文信息添加到原始异常的错误消息中,而其他细节保持完整。

已知异常类型

try:
    sock_common = xmlrpclib.ServerProxy(rpc_url+'/common')
    self.user_id = sock_common.login(self.dbname, username, self.pwd)
except IOError:
    _, ex, traceback = sys.exc_info()
    message = "Connecting to '%s': %s." % (config['connection'],
                                           ex.strerror)
    raise IOError, (ex.errno, message), traceback

这种raise声明风格将异常类型作为第一个表达式,将元组中的异常类构造函数参数作为第二个表达式,并将回溯作为第三个表达式。如果您运行的版本早于Python 2.2,请参阅中的警告sys.exc_info()

任何异常类型

如果您不知道代码可能必须捕获哪种异常,这是另一个更通用的示例。缺点是它将丢失异常类型,而只会引发RuntimeError。您必须导入traceback模块。

except Exception:
    extype, ex, tb = sys.exc_info()
    formatted = traceback.format_exception_only(extype, ex)[-1]
    message = "Importing row %d, %s" % (rownum, formatted)
    raise RuntimeError, message, tb

修改讯息

如果异常类型允许您向其添加上下文,则这是另一种选择。您可以修改异常的消息,然后重新引发它。

import subprocess

try:
    final_args = ['lsx', '/home']
    s = subprocess.check_output(final_args)
except OSError as ex:
    ex.strerror += ' for command {}'.format(final_args)
    raise

生成以下堆栈跟踪:

Traceback (most recent call last):
  File "/mnt/data/don/workspace/scratch/scratch.py", line 5, in <module>
    s = subprocess.check_output(final_args)
  File "/usr/lib/python2.7/subprocess.py", line 566, in check_output
    process = Popen(stdout=PIPE, *popenargs, **kwargs)
  File "/usr/lib/python2.7/subprocess.py", line 710, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory for command ['lsx', '/home']

您可以看到它显示了check_output()被调用的行,但是异常消息现在包括命令行。

Python 3 has the raisefrom clause to chain exceptions. Glenn’s answer is great for Python 2.7, but it only uses the original exception’s traceback and throws away the error message and other details. Here are some examples in Python 2.7 that add context information from the current scope into the original exception’s error message, but keep other details intact.

Known Exception Type

try:
    sock_common = xmlrpclib.ServerProxy(rpc_url+'/common')
    self.user_id = sock_common.login(self.dbname, username, self.pwd)
except IOError:
    _, ex, traceback = sys.exc_info()
    message = "Connecting to '%s': %s." % (config['connection'],
                                           ex.strerror)
    raise IOError, (ex.errno, message), traceback

That flavour of raise statement takes the exception type as the first expression, the exception class constructor arguments in a tuple as the second expression, and the traceback as the third expression. If you’re running earlier than Python 2.2, see the warnings on sys.exc_info().

Any Exception Type

Here’s another example that’s more general purpose if you don’t know what kind of exceptions your code might have to catch. The downside is that it loses the exception type and just raises a RuntimeError. You have to import the traceback module.

except Exception:
    extype, ex, tb = sys.exc_info()
    formatted = traceback.format_exception_only(extype, ex)[-1]
    message = "Importing row %d, %s" % (rownum, formatted)
    raise RuntimeError, message, tb

Modify the Message

Here’s another option if the exception type will let you add context to it. You can modify the exception’s message and then reraise it.

import subprocess

try:
    final_args = ['lsx', '/home']
    s = subprocess.check_output(final_args)
except OSError as ex:
    ex.strerror += ' for command {}'.format(final_args)
    raise

That generates the following stack trace:

Traceback (most recent call last):
  File "/mnt/data/don/workspace/scratch/scratch.py", line 5, in <module>
    s = subprocess.check_output(final_args)
  File "/usr/lib/python2.7/subprocess.py", line 566, in check_output
    process = Popen(stdout=PIPE, *popenargs, **kwargs)
  File "/usr/lib/python2.7/subprocess.py", line 710, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory for command ['lsx', '/home']

You can see that it shows the line where check_output() was called, but the exception message now includes the command line.


回答 3

Python 3.x中

raise Exception('Failed to process file ' + filePath).with_traceback(e.__traceback__)

简单地

except Exception:
    raise MyException()

它将传播,MyException但如果不处理,则会打印两个异常。

Python 2.x中

raise Exception, 'Failed to process file ' + filePath, e

您可以通过杀死该__context__属性来防止同时打印两个异常。在这里,我编写了一个上下文管理器,使用它来快速捕获和更改您的异常:(有关其工作原理的详细信息,请参见http://docs.python.org/3.1/library/stdtypes.html

try: # Wrap the whole program into the block that will kill __context__.

    class Catcher(Exception):
        '''This context manager reraises an exception under a different name.'''

        def __init__(self, name):
            super().__init__('Failed to process code in {!r}'.format(name))

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_type is not None:
                self.__traceback__ = exc_tb
                raise self

    ...


    with Catcher('class definition'):
        class a:
            def spam(self):
                # not really pass, but you get the idea
                pass

            lut = [1,
                   3,
                   17,
                   [12,34],
                   5,
                   _spam]


        assert a().lut[-1] == a.spam

    ...


except Catcher as e:
    e.__context__ = None
    raise

In Python 3.x:

raise Exception('Failed to process file ' + filePath).with_traceback(e.__traceback__)

or simply

except Exception:
    raise MyException()

which will propagate MyException but print both exceptions if it will not be handled.

In Python 2.x:

raise Exception, 'Failed to process file ' + filePath, e

You can prevent printing both exceptions by killing the __context__ attribute. Here I write a context manager using that to catch and change your exception on the fly: (see http://docs.python.org/3.1/library/stdtypes.html for expanation of how they work)

try: # Wrap the whole program into the block that will kill __context__.

    class Catcher(Exception):
        '''This context manager reraises an exception under a different name.'''

        def __init__(self, name):
            super().__init__('Failed to process code in {!r}'.format(name))

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_type is not None:
                self.__traceback__ = exc_tb
                raise self

    ...


    with Catcher('class definition'):
        class a:
            def spam(self):
                # not really pass, but you get the idea
                pass

            lut = [1,
                   3,
                   17,
                   [12,34],
                   5,
                   _spam]


        assert a().lut[-1] == a.spam

    ...


except Catcher as e:
    e.__context__ = None
    raise

回答 4

我认为您无法在Python 2.x中执行此操作,但是与该功能相似的功能是Python 3的一部分。来自PEP 3134

在当今的Python实现中,异常由三部分组成:类型,值和回溯。’sys’模块以三个并行变量exc_type,exc_value和exc_traceback公开当前异常,sys.exc_info()函数返回这三个部分的元组,并且’raise’语句具有接受三个参数的形式这三个部分。处理异常通常需要并行传递这三件事,这可能是乏味且容易出错的。此外,“ except”语句只能提供对值的访问,而不能提供对追溯的访问。将’ traceback ‘属性添加到异常值可以使所有异常信息都可以从一个位置访问。

与C#的比较:

C#中的异常包含一个只读的’InnerException’属性,该属性可能指向另一个异常。它的文档[10]说:“当由于先前的异常Y的直接结果而引发异常X时,X的InnerException属性应包含对Y的引用。” VM不会自动设置此属性。相反,所有异常构造函数都使用可选的“ innerException”参数来对其进行显式设置。在“ 事业 ”属性满足同样的目的的InnerException,但这个PEP提出的“提高”,而不是一直延伸异常的构造函数的新形式。C#还提供了一个GetBaseException方法,该方法直接跳转到InnerException链的末尾。

还要注意,Java,Ruby和Perl 5也不支持这种类型的东西。再次报价:

与其他语言一样,当“ catch” /“ rescue”或“ finally” /“ ensure”子句中发生另一个异常时,Java和Ruby都将丢弃原始异常。Perl 5缺少内置的结构化异常处理。对于Perl 6,RFC 88 [9]提出了一种异常机制,该机制隐式地将链式异常保留在名为@@的数组中。

I don’t think you can do this in Python 2.x, but something similar to this functionality is part of Python 3. From PEP 3134:

In today’s Python implementation, exceptions are composed of three parts: the type, the value, and the traceback. The ‘sys’ module, exposes the current exception in three parallel variables, exc_type, exc_value, and exc_traceback, the sys.exc_info() function returns a tuple of these three parts, and the ‘raise’ statement has a three-argument form accepting these three parts. Manipulating exceptions often requires passing these three things in parallel, which can be tedious and error-prone. Additionally, the ‘except’ statement can only provide access to the value, not the traceback. Adding the ‘traceback‘ attribute to exception values makes all the exception information accessible from a single place.

Comparison to C#:

Exceptions in C# contain a read-only ‘InnerException’ property that may point to another exception. Its documentation [10] says that “When an exception X is thrown as a direct result of a previous exception Y, the InnerException property of X should contain a reference to Y.” This property is not set by the VM automatically; rather, all exception constructors take an optional ‘innerException’ argument to set it explicitly. The ‘cause‘ attribute fulfills the same purpose as InnerException, but this PEP proposes a new form of ‘raise’ rather than extending the constructors of all exceptions. C# also provides a GetBaseException method that jumps directly to the end of the InnerException chain; this PEP proposes no analog.

Note also that Java, Ruby and Perl 5 don’t support this type of thing either. Quoting again:

As for other languages, Java and Ruby both discard the original exception when another exception occurs in a ‘catch’/’rescue’ or ‘finally’/’ensure’ clause. Perl 5 lacks built-in structured exception handling. For Perl 6, RFC number 88 [9] proposes an exception mechanism that implicitly retains chained exceptions in an array named @@.


回答 5

为了最大程度地兼容Python 2和3,可以raise_from在该six库中使用。 https://six.readthedocs.io/#six.raise_from。这是您的示例(为清晰起见,对其进行了稍微修改):

import six

try:
  ProcessFile(filePath)
except Exception as e:
  six.raise_from(IOError('Failed to process file ' + repr(filePath)), e)

For maximum compatibility between Python 2 and 3, you can use raise_from in the six library. https://six.readthedocs.io/#six.raise_from . Here is your example (slightly modified for clarity):

import six

try:
  ProcessFile(filePath)
except Exception as e:
  six.raise_from(IOError('Failed to process file ' + repr(filePath)), e)

回答 6

您可以使用我的CausedException类在Python 2.x中链接异常(甚至在Python 3中,如果要将多个捕获的异常作为新引发的异常的原因,它也可能很有用)。也许可以帮到您。

You could use my CausedException class to chain exceptions in Python 2.x (and even in Python 3 it can be useful in case you want to give more than one caught exception as cause to a newly raised exception). Maybe it can help you.


回答 7

也许您可以获取相关信息并将其传递出去?我在想类似的东西:

import traceback
import sys
import StringIO

class ApplicationError:
    def __init__(self, value, e):
        s = StringIO.StringIO()
        traceback.print_exc(file=s)
        self.value = (value, s.getvalue())

    def __str__(self):
        return repr(self.value)

try:
    try:
        a = 1/0
    except Exception, e:
        raise ApplicationError("Failed to process file", e)
except Exception, e:
    print e

Maybe you could grab the relevant information and pass it up? I’m thinking something like:

import traceback
import sys
import StringIO

class ApplicationError:
    def __init__(self, value, e):
        s = StringIO.StringIO()
        traceback.print_exc(file=s)
        self.value = (value, s.getvalue())

    def __str__(self):
        return repr(self.value)

try:
    try:
        a = 1/0
    except Exception, e:
        raise ApplicationError("Failed to process file", e)
except Exception, e:
    print e

回答 8

假设:

  • 您需要一个适用于Python 2的解决方案(有关纯Python 3,请参见raise ... from解决方案)
  • 只是想丰富错误消息,例如提供一些其他上下文
  • 需要完整的堆栈跟踪

您可以使用docs https://docs.python.org/3/tutorial/errors.html#raising-exceptions中的简单解决方案:

try:
    raise NameError('HiThere')
except NameError:
    print 'An exception flew by!' # print or log, provide details about context
    raise # reraise the original exception, keeping full stack trace

输出:

An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere

看起来关键是简化的“ raise”关键字,它独立存在。这将重新引发except块中的Exception。

Assuming:

  • you need a solution, which works for Python 2 (for pure Python 3 see raise ... from solution)
  • just want to enrich the error message, e.g. providing some additional context
  • need the full stack trace

you can use a simple solution from the docs https://docs.python.org/3/tutorial/errors.html#raising-exceptions:

try:
    raise NameError('HiThere')
except NameError:
    print 'An exception flew by!' # print or log, provide details about context
    raise # reraise the original exception, keeping full stack trace

The output:

An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere

It looks like the key piece is the simplified ‘raise’ keyword that stands alone. That will re-raise the Exception in the except block.


如何在Python中使用自定义消息引发相同的Exception?

问题:如何在Python中使用自定义消息引发相同的Exception?

try我的代码中包含以下代码块:

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise ValueError(errmsg)

严格来说,我实际上是提出了另一个问题 ValueError,而不是ValueError抛出do_something...()err在这种情况下称为。如何将自定义消息附加到err?我尝试下面的代码,但失败,因为err,一个ValueError 实例,不是赎回:

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise err(errmsg)

I have this try block in my code:

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise ValueError(errmsg)

Strictly speaking, I am actually raising another ValueError, not the ValueError thrown by do_something...(), which is referred to as err in this case. How do I attach a custom message to err? I try the following code but fails due to err, a ValueError instance, not being callable:

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise err(errmsg)

回答 0

更新:对于Python 3,请检查Ben的答案


将消息附加到当前异常并重新引发它:(外部try / except只是为了显示效果)

对于python 2.x,其中x> = 6:

try:
    try:
      raise ValueError  # something bad...
    except ValueError as err:
      err.message=err.message+" hello"
      raise              # re-raise current exception
except ValueError as e:
    print(" got error of type "+ str(type(e))+" with message " +e.message)

这也将做正确的事情,如果err衍生ValueError。例如UnicodeDecodeError

请注意,您可以添加任何内容err。例如err.problematic_array=[1,2,3]


编辑: @Ducan在注释中指出以上内容不适用于python 3,因为.message它不是的成员ValueError。相反,您可以使用此代码(有效的python 2.6或更高版本或3.x):

try:
    try:
      raise ValueError
    except ValueError as err:
       if not err.args: 
           err.args=('',)
       err.args = err.args + ("hello",)
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e.args))

编辑2:

根据目的,您还可以选择在自己的变量名下添加额外的信息。对于python2和python3:

try:
    try:
      raise ValueError
    except ValueError as err:
       err.extra_info = "hello"
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e))
    if 'extra_info' in dir(e):
       print e.extra_info

Update: For Python 3, check Ben’s answer


To attach a message to the current exception and re-raise it: (the outer try/except is just to show the effect)

For python 2.x where x>=6:

try:
    try:
      raise ValueError  # something bad...
    except ValueError as err:
      err.message=err.message+" hello"
      raise              # re-raise current exception
except ValueError as e:
    print(" got error of type "+ str(type(e))+" with message " +e.message)

This will also do the right thing if err is derived from ValueError. For example UnicodeDecodeError.

Note that you can add whatever you like to err. For example err.problematic_array=[1,2,3].


Edit: @Ducan points in a comment the above does not work with python 3 since .message is not a member of ValueError. Instead you could use this (valid python 2.6 or later or 3.x):

try:
    try:
      raise ValueError
    except ValueError as err:
       if not err.args: 
           err.args=('',)
       err.args = err.args + ("hello",)
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e.args))

Edit2:

Depending on what the purpose is, you can also opt for adding the extra information under your own variable name. For both python2 and python3:

try:
    try:
      raise ValueError
    except ValueError as err:
       err.extra_info = "hello"
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e))
    if 'extra_info' in dir(e):
       print e.extra_info

回答 1

如果您有幸仅支持python 3.x,那么这真的很美:)

从…提高

我们可以使用raise from链接异常。

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks') from e

在这种情况下,您的调用方法将捕获的异常具有我们提出异常的地方的行号。

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks') from e
Exception: Smelly socks

请注意,底部异常仅包含引发异常的堆栈跟踪。您的调用者仍然可以通过访问__cause__他们捕获的异常的属性来获取原始异常。

with_traceback

或者,您可以使用with_traceback

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks').with_traceback(e.__traceback__)

使用此表单,您的调用者将捕获的异常具有追溯到原始错误发生的位置。

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks').with_traceback(e.__traceback__)
  File "test.py", line 2, in <module>
    1 / 0
Exception: Smelly socks

请注意,底部异常包含执行无效除法的行以及重新引发异常的行。

If you’re lucky enough to only support python 3.x, this really becomes a thing of beauty :)

raise from

We can chain the exceptions using raise from.

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks') from e

In this case, the exception your caller would catch has the line number of the place where we raise our exception.

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks') from e
Exception: Smelly socks

Notice the bottom exception only has the stacktrace from where we raised our exception. Your caller could still get the original exception by accessing the __cause__ attribute of the exception they catch.

with_traceback

Or you can use with_traceback.

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks').with_traceback(e.__traceback__)

Using this form, the exception your caller would catch has the traceback from where the original error occurred.

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks').with_traceback(e.__traceback__)
  File "test.py", line 2, in <module>
    1 / 0
Exception: Smelly socks

Notice the bottom exception has the line where we performed the invalid division as well as the line where we reraise the exception.


回答 2

try:
    try:
        int('a')
    except ValueError as e:
        raise ValueError('There is a problem: {0}'.format(e))
except ValueError as err:
    print err

印刷品:

There is a problem: invalid literal for int() with base 10: 'a'
try:
    try:
        int('a')
    except ValueError as e:
        raise ValueError('There is a problem: {0}'.format(e))
except ValueError as err:
    print err

prints:

There is a problem: invalid literal for int() with base 10: 'a'

回答 3

似乎所有答案都将信息添加到e.args [0],从而更改了现有的错误消息。扩展args元组有不利之处吗?我认为可能的好处是,在需要解析该字符串的情况下,您可以不考虑原始错误消息。如果您的自定义错误处理产生了多个消息或错误代码,并且可以通过编程方式(例如通过系统监视工具)解析回溯,则可以在元组中添加多个元素。

## Approach #1, if the exception may not be derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args = (e.args if e.args else tuple()) + ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

要么

## Approach #2, if the exception is always derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args += ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

您能看到这种方法的缺点吗?

It seems all the answers are adding info to e.args[0], thereby altering the existing error message. Is there a downside to extending the args tuple instead? I think the possible upside is, you can leave the original error message alone for cases where parsing that string is needed; and you could add multiple elements to the tuple if your custom error handling produced several messages or error codes, for cases where the traceback would be parsed programmatically (like via a system monitoring tool).

## Approach #1, if the exception may not be derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args = (e.args if e.args else tuple()) + ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

or

## Approach #2, if the exception is always derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args += ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

Can you see a downside to this approach?


回答 4

此代码模板应允许您使用自定义消息引发异常。

try:
     raise ValueError
except ValueError as err:
    raise type(err)("my message")

This code template should allow you to raise an exception with a custom message.

try:
     raise ValueError
except ValueError as err:
    raise type(err)("my message")

回答 5

使用以下错误消息引发新异常

raise Exception('your error message')

要么

raise ValueError('your error message')

在您要引发它的地方或使用’from’将错误消息附加(替换)到当前异常中(仅支持Python 3.x):

except ValueError as e:
  raise ValueError('your message') from e

Either raise the new exception with your error message using

raise Exception('your error message')

or

raise ValueError('your error message')

within the place where you want to raise it OR attach (replace) error message into current exception using ‘from’ (Python 3.x supported only):

except ValueError as e:
  raise ValueError('your message') from e

回答 6

这是我用来在保留原始回溯的同时修改Python 2.7和3.x中的异常消息的功能。这个需要six

def reraise_modify(caught_exc, append_msg, prepend=False):
    """Append message to exception while preserving attributes.

    Preserves exception class, and exception traceback.

    Note:
        This function needs to be called inside an except because
        `sys.exc_info()` requires the exception context.

    Args:
        caught_exc(Exception): The caught exception object
        append_msg(str): The message to append to the caught exception
        prepend(bool): If True prepend the message to args instead of appending

    Returns:
        None

    Side Effects:
        Re-raises the exception with the preserved data / trace but
        modified message
    """
    ExceptClass = type(caught_exc)
    # Keep old traceback
    traceback = sys.exc_info()[2]
    if not caught_exc.args:
        # If no args, create our own tuple
        arg_list = [append_msg]
    else:
        # Take the last arg
        # If it is a string
        # append your message.
        # Otherwise append it to the
        # arg list(Not as pretty)
        arg_list = list(caught_exc.args[:-1])
        last_arg = caught_exc.args[-1]
        if isinstance(last_arg, str):
            if prepend:
                arg_list.append(append_msg + last_arg)
            else:
                arg_list.append(last_arg + append_msg)
        else:
            arg_list += [last_arg, append_msg]
    caught_exc.args = tuple(arg_list)
    six.reraise(ExceptClass,
                caught_exc,
                traceback)

This is the function I use to modify the exception message in Python 2.7 and 3.x while preserving the original traceback. It requires six

def reraise_modify(caught_exc, append_msg, prepend=False):
    """Append message to exception while preserving attributes.

    Preserves exception class, and exception traceback.

    Note:
        This function needs to be called inside an except because
        `sys.exc_info()` requires the exception context.

    Args:
        caught_exc(Exception): The caught exception object
        append_msg(str): The message to append to the caught exception
        prepend(bool): If True prepend the message to args instead of appending

    Returns:
        None

    Side Effects:
        Re-raises the exception with the preserved data / trace but
        modified message
    """
    ExceptClass = type(caught_exc)
    # Keep old traceback
    traceback = sys.exc_info()[2]
    if not caught_exc.args:
        # If no args, create our own tuple
        arg_list = [append_msg]
    else:
        # Take the last arg
        # If it is a string
        # append your message.
        # Otherwise append it to the
        # arg list(Not as pretty)
        arg_list = list(caught_exc.args[:-1])
        last_arg = caught_exc.args[-1]
        if isinstance(last_arg, str):
            if prepend:
                arg_list.append(append_msg + last_arg)
            else:
                arg_list.append(last_arg + append_msg)
        else:
            arg_list += [last_arg, append_msg]
    caught_exc.args = tuple(arg_list)
    six.reraise(ExceptClass,
                caught_exc,
                traceback)

回答 7

Python 3内置异常具有以下strerror字段:

except ValueError as err:
  err.strerror = "New error message"
  raise err

Python 3 built-in exceptions have the strerror field:

except ValueError as err:
  err.strerror = "New error message"
  raise err

回答 8

当前的答案对我没有用,如果未重新捕获异常,则不会显示附加的消息。

但是,无论是否重新捕获异常,下面的操作都将保留跟踪并显示附加的消息。

try:
  raise ValueError("Original message")
except ValueError as err:
  t, v, tb = sys.exc_info()
  raise t, ValueError(err.message + " Appended Info"), tb

(我使用的是Python 2.7,还没有在Python 3中尝试过)

The current answer did not work good for me, if the exception is not re-caught the appended message is not shown.

But doing like below both keeps the trace and shows the appended message regardless if the exception is re-caught or not.

try:
  raise ValueError("Original message")
except ValueError as err:
  t, v, tb = sys.exc_info()
  raise t, ValueError(err.message + " Appended Info"), tb

( I used Python 2.7, have not tried it in Python 3 )


回答 9

以上解决方案均未达到我想要的目的,即向错误消息的第一部分添加了一些信息,即我希望用户首先看到我的自定义消息。

这为我工作:

exception_raised = False
try:
    do_something_that_might_raise_an_exception()
except ValueError as e:
    message = str(e)
    exception_raised = True

if exception_raised:
    message_to_prepend = "Custom text"
    raise ValueError(message_to_prepend + message)

None of the above solutions did exactly what I wanted, which was to add some information to the first part of the error message i.e. I wanted my users to see my custom message first.

This worked for me:

exception_raised = False
try:
    do_something_that_might_raise_an_exception()
except ValueError as e:
    message = str(e)
    exception_raised = True

if exception_raised:
    message_to_prepend = "Custom text"
    raise ValueError(message_to_prepend + message)

回答 10

这仅适用于Python 3。您可以修改异常的原始参数并添加自己的参数。

异常会记住创建它的参数。我认为这是为了您可以修改异常。

在函数中,reraise我们在异常的原始参数之前添加了所需的任何新参数(例如消息)。最后,我们在保留追溯历史的同时重新引发异常。

def reraise(e, *args):
  '''re-raise an exception with extra arguments
  :param e: The exception to reraise
  :param args: Extra args to add to the exception
  '''

  # e.args is a tuple of arguments that the exception with instantiated with.
  #
  e.args = args + e.args

  # Recreate the expection and preserve the traceback info so thta we can see 
  # where this exception originated.
  #
  raise e.with_traceback(e.__traceback__)   


def bad():
  raise ValueError('bad')

def very():
  try:
    bad()
  except Exception as e:
    reraise(e, 'very')

def very_very():
  try:
    very()
  except Exception as e:
    reraise(e, 'very')

very_very()

输出

Traceback (most recent call last):
  File "main.py", line 35, in <module>
    very_very()
  File "main.py", line 30, in very_very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 28, in very_very
    very()
  File "main.py", line 24, in very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 22, in very
    bad()
  File "main.py", line 18, in bad
    raise ValueError('bad')
ValueError: ('very', 'very', 'bad')

This only works with Python 3. You can modify the exception’s original arguments and add your own arguments.

An exception remembers the args it was created with. I presume this is so that you can modify the exception.

In the function reraise we prepend the exception’s original arguments with any new arguments that we want (like a message). Finally we re-raise the exception while preserving the trace-back history.

def reraise(e, *args):
  '''re-raise an exception with extra arguments
  :param e: The exception to reraise
  :param args: Extra args to add to the exception
  '''

  # e.args is a tuple of arguments that the exception with instantiated with.
  #
  e.args = args + e.args

  # Recreate the expection and preserve the traceback info so thta we can see 
  # where this exception originated.
  #
  raise e.with_traceback(e.__traceback__)   


def bad():
  raise ValueError('bad')

def very():
  try:
    bad()
  except Exception as e:
    reraise(e, 'very')

def very_very():
  try:
    very()
  except Exception as e:
    reraise(e, 'very')

very_very()

output

Traceback (most recent call last):
  File "main.py", line 35, in <module>
    very_very()
  File "main.py", line 30, in very_very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 28, in very_very
    very()
  File "main.py", line 24, in very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 22, in very
    bad()
  File "main.py", line 18, in bad
    raise ValueError('bad')
ValueError: ('very', 'very', 'bad')

回答 11

如果要自定义错误类型,您可以做的一件简单的事情就是基于ValueError定义一个错误类。

if you want to custom the error type, a simple thing you can do is to define an error class based on ValueError.


向异常添加信息?

问题:向异常添加信息?

我想实现以下目标:

def foo():
   try:
       raise IOError('Stuff ')
   except:
       raise

def bar(arg1):
    try:
       foo()
    except Exception as e:
       e.message = e.message + 'happens at %s' % arg1
       raise

bar('arg1')
Traceback...
  IOError('Stuff Happens at arg1')

但是我得到的是:

Traceback..
  IOError('Stuff')

关于如何实现这一目标的任何线索?如何在Python 2和3中做到这一点?

I want to achieve something like this:

def foo():
   try:
       raise IOError('Stuff ')
   except:
       raise

def bar(arg1):
    try:
       foo()
    except Exception as e:
       e.message = e.message + 'happens at %s' % arg1
       raise

bar('arg1')
Traceback...
  IOError('Stuff Happens at arg1')

But what I get is:

Traceback..
  IOError('Stuff')

Any clues as to how to achieve this? How to do it both in Python 2 and 3?


回答 0

我会这样做,因此更改它的类型foo()将不需要也将其更改bar()

def foo():
    try:
        raise IOError('Stuff')
    except:
        raise

def bar(arg1):
    try:
        foo()
    except Exception as e:
        raise type(e)(e.message + ' happens at %s' % arg1)

bar('arg1')

Traceback (most recent call last):
  File "test.py", line 13, in <module>
    bar('arg1')
  File "test.py", line 11, in bar
    raise type(e)(e.message + ' happens at %s' % arg1)
IOError: Stuff happens at arg1

更新1

这是保留原始回溯的略微修改:

...
def bar(arg1):
    try:
        foo()
    except Exception as e:
        import sys
        raise type(e), type(e)(e.message +
                               ' happens at %s' % arg1), sys.exc_info()[2]

bar('arg1')

Traceback (most recent call last):
  File "test.py", line 16, in <module>
    bar('arg1')
  File "test.py", line 11, in bar
    foo()
  File "test.py", line 5, in foo
    raise IOError('Stuff')
IOError: Stuff happens at arg1

更新2

对于Python 3.x,我的第一次更新中的代码在语法上是不正确的,并且在message2012 BaseException年5 月16日对PEP 352的更改收回了启用属性的想法(我的第一次更新发布于2012-03-12) 。因此,当前,无论如何,在Python 3.5.2中,您都需要按照以下步骤做一些事情以保留回溯,而不是硬编码function中的异常类型bar()。另请注意,将出现以下行:

During handling of the above exception, another exception occurred:

在显示的回溯消息中。

# for Python 3.x
...
def bar(arg1):
    try:
        foo()
    except Exception as e:
        import sys
        raise type(e)(str(e) +
                      ' happens at %s' % arg1).with_traceback(sys.exc_info()[2])

bar('arg1')

更新3

一个评论者询问是否有会在两个Python 2和3。工作虽然答案可能似乎是“不”,因为语法不同的方式,还有就是周围的一种方法,通过使用一个辅助函数一样reraise()six添加-在模块上。因此,如果您出于某种原因不愿使用该库,则下面是简化的独立版本。

还要注意,由于异常是在reraise()函数中引发的,因此它将在引发任何回溯的情况下出现,但最终结果是您想要的。

import sys

if sys.version_info.major < 3:  # Python 2?
    # Using exec avoids a SyntaxError in Python 3.
    exec("""def reraise(exc_type, exc_value, exc_traceback=None):
                raise exc_type, exc_value, exc_traceback""")
else:
    def reraise(exc_type, exc_value, exc_traceback=None):
        if exc_value is None:
            exc_value = exc_type()
        if exc_value.__traceback__ is not exc_traceback:
            raise exc_value.with_traceback(exc_traceback)
        raise exc_value

def foo():
    try:
        raise IOError('Stuff')
    except:
        raise

def bar(arg1):
    try:
       foo()
    except Exception as e:
        reraise(type(e), type(e)(str(e) +
                                 ' happens at %s' % arg1), sys.exc_info()[2])

bar('arg1')

I’d do it like this so changing its type in foo() won’t require also changing it in bar().

def foo():
    try:
        raise IOError('Stuff')
    except:
        raise

def bar(arg1):
    try:
        foo()
    except Exception as e:
        raise type(e)(e.message + ' happens at %s' % arg1)

bar('arg1')

Traceback (most recent call last):
  File "test.py", line 13, in <module>
    bar('arg1')
  File "test.py", line 11, in bar
    raise type(e)(e.message + ' happens at %s' % arg1)
IOError: Stuff happens at arg1

Update 1

Here’s a slight modification that preserves the original traceback:

...
def bar(arg1):
    try:
        foo()
    except Exception as e:
        import sys
        raise type(e), type(e)(e.message +
                               ' happens at %s' % arg1), sys.exc_info()[2]

bar('arg1')

Traceback (most recent call last):
  File "test.py", line 16, in <module>
    bar('arg1')
  File "test.py", line 11, in bar
    foo()
  File "test.py", line 5, in foo
    raise IOError('Stuff')
IOError: Stuff happens at arg1

Update 2

For Python 3.x, the code in my first update is syntactically incorrect plus the idea of having a message attribute on BaseException was retracted in a change to PEP 352 on 2012-05-16 (my first update was posted on 2012-03-12). So currently, in Python 3.5.2 anyway, you’d need to do something along these lines to preserve the traceback and not hardcode the type of exception in function bar(). Also note that there will be the line:

During handling of the above exception, another exception occurred:

in the traceback messages displayed.

# for Python 3.x
...
def bar(arg1):
    try:
        foo()
    except Exception as e:
        import sys
        raise type(e)(str(e) +
                      ' happens at %s' % arg1).with_traceback(sys.exc_info()[2])

bar('arg1')

Update 3

A commenter asked if there was a way that would work in both Python 2 and 3. Although the answer might seem to be “No” due to the syntax differences, there is a way around that by using a helper function like reraise() in the six add-on module. So, if you’d rather not use the library for some reason, below is a simplified standalone version.

Note too, that since the exception is reraised within the reraise() function, that will appear in whatever traceback is raised, but the final result is what you want.

import sys

if sys.version_info.major < 3:  # Python 2?
    # Using exec avoids a SyntaxError in Python 3.
    exec("""def reraise(exc_type, exc_value, exc_traceback=None):
                raise exc_type, exc_value, exc_traceback""")
else:
    def reraise(exc_type, exc_value, exc_traceback=None):
        if exc_value is None:
            exc_value = exc_type()
        if exc_value.__traceback__ is not exc_traceback:
            raise exc_value.with_traceback(exc_traceback)
        raise exc_value

def foo():
    try:
        raise IOError('Stuff')
    except:
        raise

def bar(arg1):
    try:
       foo()
    except Exception as e:
        reraise(type(e), type(e)(str(e) +
                                 ' happens at %s' % arg1), sys.exc_info()[2])

bar('arg1')

回答 1

如果您是来这里寻找Python 3解决方案的,该手册会 说:

当引发一个新的异常时(而不是使用裸机raise重新引发当前正在处理的异常),可以通过使用from并加引发来为隐式异常上下文添加显式原因:

raise new_exc from original_exc

例:

try:
    return [permission() for permission in self.permission_classes]
except TypeError as e:
    raise TypeError("Make sure your view's 'permission_classes' are iterable. "
                    "If you use '()' to generate a set with a single element "
                    "make sure that there is a comma behind the one (element,).") from e

最终看起来像这样:

2017-09-06 16:50:14,797 [ERROR] django.request: Internal Server Error: /v1/sendEmail/
Traceback (most recent call last):
File "venv/lib/python3.4/site-packages/rest_framework/views.py", line 275, in get_permissions
    return [permission() for permission in self.permission_classes]
TypeError: 'type' object is not iterable 

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
    # Traceback removed...
TypeError: Make sure your view's Permission_classes are iterable. If 
     you use parens () to generate a set with a single element make 
     sure that there is a (comma,) behind the one element.

TypeError不弄乱原始Exception的情况下,将完全没有描述的信息变成带有解决方案提示的好消息。

In case you came here searching for a solution for Python 3 the manual says:

When raising a new exception (rather than using a bare raise to re-raise the exception currently being handled), the implicit exception context can be supplemented with an explicit cause by using from with raise:

raise new_exc from original_exc

Example:

try:
    return [permission() for permission in self.permission_classes]
except TypeError as e:
    raise TypeError("Make sure your view's 'permission_classes' are iterable. "
                    "If you use '()' to generate a set with a single element "
                    "make sure that there is a comma behind the one (element,).") from e

Which looks like this in the end:

2017-09-06 16:50:14,797 [ERROR] django.request: Internal Server Error: /v1/sendEmail/
Traceback (most recent call last):
File "venv/lib/python3.4/site-packages/rest_framework/views.py", line 275, in get_permissions
    return [permission() for permission in self.permission_classes]
TypeError: 'type' object is not iterable 

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
    # Traceback removed...
TypeError: Make sure your view's Permission_classes are iterable. If 
     you use parens () to generate a set with a single element make 
     sure that there is a (comma,) behind the one element.

Turning a totally nondescript TypeError into a nice message with hints towards a solution without messing up the original Exception.


回答 2

假设您不想或无法修改foo(),可以执行以下操作:

try:
    raise IOError('stuff')
except Exception as e:
    if len(e.args) >= 1:
        e.args = (e.args[0] + ' happens',) + e.args[1:]
    raise

实际上,这确实是解决Python 3中问题的唯一解决方案,而不会出现丑陋且令人困惑的“在处理上述异常期间,发生了另一个异常”消息。

万一将重新抬高的行添加到堆栈跟踪中,则写raise e而不是写raise就可以了。

Assuming you don’t want to or can’t modify foo(), you can do this:

try:
    raise IOError('stuff')
except Exception as e:
    if len(e.args) >= 1:
        e.args = (e.args[0] + ' happens',) + e.args[1:]
    raise

This is indeed the only solution here that solves the problem in Python 3 without an ugly and confusing “During handling of the above exception, another exception occurred” message.

In case the re-raising line should be added to the stack trace, writing raise e instead of raise will do the trick.


回答 3

到目前为止,我不喜欢所有给出的答案。他们仍然太冗长,恕我直言。在代码和消息输出中。

我要拥有的只是指向源异常的stacktrace,中间没有异常的东西,因此不创建新的异常,只需重新引发具有所有相关堆栈框架状态的原始异常,就可以了。

史蒂夫·霍华德Steve Howard)给出了一个很好的答案,我想将其扩展为不,仅限于Python 3。

except Exception as e:
    e.args = ("Some failure state", *e.args)
    raise

唯一的新功能是参数扩展/解压缩,它使它小巧易用。

试试吧:

foo = None

try:
    try:
        state = "bar"
        foo.append(state)

    except Exception as e:
        e.args = ("Appending '"+state+"' failed", *e.args)
        raise

    print(foo[0]) # would raise too

except Exception as e:
    e.args = ("print(foo) failed: " + str(foo), *e.args)
    raise

这将为您提供:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    foo.append(state)
AttributeError: ('print(foo) failed: None', "Appending 'bar' failed", "'NoneType' object has no attribute 'append'")

简单的漂亮印刷可能像

print("\n".join( "-"*i+" "+j for i,j in enumerate(e.args)))

I don’t like all the given answers so far. They are still too verbose imho. In either code and message output.

All i want to have is the stacktrace pointing to the source exception, no exception stuff in between, so no creation of new exceptions, just re-raising the original with all the relevant stack frame states in it, that led there.

Steve Howard gave a nice answer which i want to extend, no, reduce … to python 3 only.

except Exception as e:
    e.args = ("Some failure state", *e.args)
    raise

The only new thing is the parameter expansion/unpacking which makes it small and easy enough for me to use.

Try it:

foo = None

try:
    try:
        state = "bar"
        foo.append(state)

    except Exception as e:
        e.args = ("Appending '"+state+"' failed", *e.args)
        raise

    print(foo[0]) # would raise too

except Exception as e:
    e.args = ("print(foo) failed: " + str(foo), *e.args)
    raise

This will give you:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    foo.append(state)
AttributeError: ('print(foo) failed: None', "Appending 'bar' failed", "'NoneType' object has no attribute 'append'")

A simple pretty-print could be something like

print("\n".join( "-"*i+" "+j for i,j in enumerate(e.args)))

回答 4

我使用的一种便捷方法是使用类属性作为详细信息的存储,因为可以从类对象和类实例访问类属性:

class CustomError(Exception):
    def __init__(self, details: Dict):
        self.details = details

然后在您的代码中:

raise CustomError({'data': 5})

当发现错误时:

except CustomError as e:
    # Do whatever you want with the exception instance
    print(e.details)

One handy approach that I used is to use class attribute as storage for details, as class attribute is accessible both from class object and class instance:

class CustomError(Exception):
    def __init__(self, details: Dict):
        self.details = details

Then in your code:

raise CustomError({'data': 5})

And when catching an error:

except CustomError as e:
    # Do whatever you want with the exception instance
    print(e.details)

回答 5

与先前的答案不同,这在面对非常糟糕的异常时有效__str__。但是,它确实修改了类型,以排除无用的__str__实现。

我仍然想找到一种不会修改类型的其他改进。

from contextlib import contextmanager
@contextmanager
def helpful_info():
    try:
        yield
    except Exception as e:
        class CloneException(Exception): pass
        CloneException.__name__ = type(e).__name__
        CloneException.__module___ = type(e).__module__
        helpful_message = '%s\n\nhelpful info!' % e
        import sys
        raise CloneException, helpful_message, sys.exc_traceback


class BadException(Exception):
    def __str__(self):
        return 'wat.'

with helpful_info():
    raise BadException('fooooo')

原始的追溯和类型(名称)被保留。

Traceback (most recent call last):
  File "re_raise.py", line 20, in <module>
    raise BadException('fooooo')
  File "/usr/lib64/python2.6/contextlib.py", line 34, in __exit__
    self.gen.throw(type, value, traceback)
  File "re_raise.py", line 5, in helpful_info
    yield
  File "re_raise.py", line 20, in <module>
    raise BadException('fooooo')
__main__.BadException: wat.

helpful info!

Unlike previous answers, this works in the face of exceptions with really bad __str__. It does modify the type however, in order to factor out unhelpful __str__ implementations.

I’d still like to find an additional improvement that doesn’t modify the type.

from contextlib import contextmanager
@contextmanager
def helpful_info():
    try:
        yield
    except Exception as e:
        class CloneException(Exception): pass
        CloneException.__name__ = type(e).__name__
        CloneException.__module___ = type(e).__module__
        helpful_message = '%s\n\nhelpful info!' % e
        import sys
        raise CloneException, helpful_message, sys.exc_traceback


class BadException(Exception):
    def __str__(self):
        return 'wat.'

with helpful_info():
    raise BadException('fooooo')

The original traceback and type (name) are preserved.

Traceback (most recent call last):
  File "re_raise.py", line 20, in <module>
    raise BadException('fooooo')
  File "/usr/lib64/python2.6/contextlib.py", line 34, in __exit__
    self.gen.throw(type, value, traceback)
  File "re_raise.py", line 5, in helpful_info
    yield
  File "re_raise.py", line 20, in <module>
    raise BadException('fooooo')
__main__.BadException: wat.

helpful info!

回答 6

每当我想向异常添加额外的信息时,我都会提供我经常使用的代码片段。我在Python 2.7和3.6中都可以工作。

import sys
import traceback

try:
    a = 1
    b = 1j

    # The line below raises an exception because
    # we cannot compare int to complex.
    m = max(a, b)  

except Exception as ex:
    # I create my  informational message for debugging:
    msg = "a=%r, b=%r" % (a, b)

    # Gather the information from the original exception:
    exc_type, exc_value, exc_traceback = sys.exc_info()

    # Format the original exception for a nice printout:
    traceback_string = ''.join(traceback.format_exception(
        exc_type, exc_value, exc_traceback))

    # Re-raise a new exception of the same class as the original one, 
    # using my custom message and the original traceback:
    raise type(ex)("%s\n\nORIGINAL TRACEBACK:\n\n%s\n" % (msg, traceback_string))

上面的代码产生以下输出:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-09b74752c60d> in <module>()
     14     raise type(ex)(
     15         "%s\n\nORIGINAL TRACEBACK:\n\n%s\n" %
---> 16         (msg, traceback_string))

TypeError: a=1, b=1j

ORIGINAL TRACEBACK:

Traceback (most recent call last):
  File "<ipython-input-6-09b74752c60d>", line 7, in <module>
    m = max(a, b)  # Cannot compare int to complex
TypeError: no ordering relation is defined for complex numbers


我知道这与问题中提供的示例有些出入,但是我还是希望有人觉得它有用。

I will provide a snippet of code that I use often whenever I want to add extra info to an exception. I works both in Python 2.7 and 3.6.

import sys
import traceback

try:
    a = 1
    b = 1j

    # The line below raises an exception because
    # we cannot compare int to complex.
    m = max(a, b)  

except Exception as ex:
    # I create my  informational message for debugging:
    msg = "a=%r, b=%r" % (a, b)

    # Gather the information from the original exception:
    exc_type, exc_value, exc_traceback = sys.exc_info()

    # Format the original exception for a nice printout:
    traceback_string = ''.join(traceback.format_exception(
        exc_type, exc_value, exc_traceback))

    # Re-raise a new exception of the same class as the original one, 
    # using my custom message and the original traceback:
    raise type(ex)("%s\n\nORIGINAL TRACEBACK:\n\n%s\n" % (msg, traceback_string))

The code above results in the following output:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-09b74752c60d> in <module>()
     14     raise type(ex)(
     15         "%s\n\nORIGINAL TRACEBACK:\n\n%s\n" %
---> 16         (msg, traceback_string))

TypeError: a=1, b=1j

ORIGINAL TRACEBACK:

Traceback (most recent call last):
  File "<ipython-input-6-09b74752c60d>", line 7, in <module>
    m = max(a, b)  # Cannot compare int to complex
TypeError: no ordering relation is defined for complex numbers


I know this deviates a little from the example provided in the question, but nevertheless I hope someone finds it useful.


回答 7

您可以定义自己的从另一个继承的异常,并创建它自己的构造函数来设置值。

例如:

class MyError(Exception):
   def __init__(self, value):
     self.value = value
     Exception.__init__(self)

   def __str__(self):
     return repr(self.value)

You can define your own exception that inherits from another and create it’s own constructor to set value.

For example:

class MyError(Exception):
   def __init__(self, value):
     self.value = value
     Exception.__init__(self)

   def __str__(self):
     return repr(self.value)

回答 8

也许

except Exception as e:
    raise IOError(e.message + 'happens at %s'%arg1)

Maybe

except Exception as e:
    raise IOError(e.message + 'happens at %s'%arg1)

相当于e.printStackTrace的python

问题:相当于e.printStackTrace的python

我知道print(e)(其中e是一个异常)会打印发生的异常,但是,我试图找到与Java等效的python,e.printStackTrace()它可以将异常确切地跟踪到发生的那一行,并打印出整个轨迹。

谁能告诉我e.printStackTrace()Python 的等效功能吗?

I know that print(e) (where e is an Exception) prints the occurred exception but, I was trying to find the python equivalent of Java’s e.printStackTrace() that exactly traces the exception to what line it occurred and prints the entire trace of it.

Could anyone please tell me the equivalent of e.printStackTrace() in Python?


回答 0

import traceback
traceback.print_exc()

在一个except ...:块内执行此操作时,它将自动使用当前异常。有关更多信息,请参见http://docs.python.org/library/traceback.html

import traceback
traceback.print_exc()

When doing this inside an except ...: block it will automatically use the current exception. See http://docs.python.org/library/traceback.html for more information.


回答 1

也有logging.exception

import logging

...

try:
    g()
except Exception as ex:
    logging.exception("Something awful happened!")
    # will print this message followed by traceback

输出:

ERROR 2007-09-18 23:30:19,913 error 1294 Something awful happened!
Traceback (most recent call last):
  File "b.py", line 22, in f
    g()
  File "b.py", line 14, in g
    1/0
ZeroDivisionError: integer division or modulo by zero

(摘自http://blog.tplus1.com/index.php/2007/09/28/the-python-logging-module-is-much-better-than-print-statements/通过如何打印完整的回溯而无需暂停程序?

There is also logging.exception.

import logging

...

try:
    g()
except Exception as ex:
    logging.exception("Something awful happened!")
    # will print this message followed by traceback

Output:

ERROR 2007-09-18 23:30:19,913 error 1294 Something awful happened!
Traceback (most recent call last):
  File "b.py", line 22, in f
    g()
  File "b.py", line 14, in g
    1/0
ZeroDivisionError: integer division or modulo by zero

(From http://blog.tplus1.com/index.php/2007/09/28/the-python-logging-module-is-much-better-than-print-statements/ via How to print the full traceback without halting the program?)


回答 2

相当于e.printStackTrace的python

在Java中,这将执行以下操作(docs):

public void printStackTrace()

将这个throwable及其回溯打印到标准错误流中。

这样使用:

try
{ 
// code that may raise an error
}
catch (IOException e)
{
// exception handling
e.printStackTrace();
}

在Java中,标准错误流没有缓冲,因此输出立即到达。

Python 2中的相同语义是:

import traceback
import sys
try: # code that may raise an error
    pass 
except IOError as e: # exception handling
    # in Python 2, stderr is also unbuffered
    print >> sys.stderr, traceback.format_exc()
    # in Python 2, you can also from __future__ import print_function
    print(traceback.format_exc(), file=sys.stderr)
    # or as the top answer here demonstrates, use:
    traceback.print_exc()
    # which also uses stderr.

Python 3

在Python 3中,我们可以直接从异常对象获取回溯(对于线程代码,其行为可能更好)。另外,stderr是行缓冲的,但是print函数获取flush参数,因此可以立即将其打印到stderr:

    print(traceback.format_exception(None, # <- type(e) by docs, but ignored 
                                     e, e.__traceback__),
          file=sys.stderr, flush=True)

结论:

因此,在Python 3中,traceback.print_exc()尽管sys.stderr 默认使用,但会缓冲输出,您可能会丢失它。因此,为了获得尽可能相等的语义,请在Python 3中print与一起使用flush=True

e.printStackTrace equivalent in python

In Java, this does the following (docs):

public void printStackTrace()

Prints this throwable and its backtrace to the standard error stream…

This is used like this:

try
{ 
// code that may raise an error
}
catch (IOException e)
{
// exception handling
e.printStackTrace();
}

In Java, the Standard Error stream is unbuffered so that output arrives immediately.

The same semantics in Python 2 are:

import traceback
import sys
try: # code that may raise an error
    pass 
except IOError as e: # exception handling
    # in Python 2, stderr is also unbuffered
    print >> sys.stderr, traceback.format_exc()
    # in Python 2, you can also from __future__ import print_function
    print(traceback.format_exc(), file=sys.stderr)
    # or as the top answer here demonstrates, use:
    traceback.print_exc()
    # which also uses stderr.

Python 3

In Python 3, we can get the traceback directly from the exception object (which likely behaves better for threaded code). Also, stderr is line-buffered, but the print function gets a flush argument, so this would be immediately printed to stderr:

    print(traceback.format_exception(None, # <- type(e) by docs, but ignored 
                                     e, e.__traceback__),
          file=sys.stderr, flush=True)

Conclusion:

In Python 3, therefore, traceback.print_exc(), although it uses sys.stderr by default, would buffer the output, and you may possibly lose it. So to get as equivalent semantics as possible, in Python 3, use print with flush=True.


回答 3

添加到其他伟大的答案,我们可以使用Python logging库的debug()info()warning()error(),和critical()方法。引用Python 3.7.4的文档,

在kwargs中检查了三个关键字参数:exc_info,如果不将其评估为false,则会将异常信息添加到日志消息中。

这意味着,您可以使用Python logging库输出debug()或其他类型的消息,并且该logging库将在其输出中包括堆栈跟踪。考虑到这一点,我们可以执行以下操作:

import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

def f():
    a = { 'foo': None }
    # the following line will raise KeyError
    b = a['bar']

def g():
    f()

try:
    g()
except Exception as e:
    logger.error(str(e), exc_info=True)

它将输出:

'bar'
Traceback (most recent call last):
  File "<ipython-input-2-8ae09e08766b>", line 18, in <module>
    g()
  File "<ipython-input-2-8ae09e08766b>", line 14, in g
    f()
  File "<ipython-input-2-8ae09e08766b>", line 10, in f
    b = a['bar']
KeyError: 'bar'

Adding to the other great answers, we can use the Python logging library’s debug(), info(), warning(), error(), and critical() methods. Quoting from the docs for Python 3.7.4,

There are three keyword arguments in kwargs which are inspected: exc_info which, if it does not evaluate as false, causes exception information to be added to the logging message.

What this means is, you can use the Python logging library to output a debug(), or other type of message, and the logging library will include the stack trace in its output. With this in mind, we can do the following:

import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

def f():
    a = { 'foo': None }
    # the following line will raise KeyError
    b = a['bar']

def g():
    f()

try:
    g()
except Exception as e:
    logger.error(str(e), exc_info=True)

And it will output:

'bar'
Traceback (most recent call last):
  File "<ipython-input-2-8ae09e08766b>", line 18, in <module>
    g()
  File "<ipython-input-2-8ae09e08766b>", line 14, in g
    f()
  File "<ipython-input-2-8ae09e08766b>", line 10, in f
    b = a['bar']
KeyError: 'bar'

在Python的调用者线程中捕获线程的异常

问题:在Python的调用者线程中捕获线程的异常

我对Python和多线程编程非常陌生。基本上,我有一个脚本可以将文件复制到另一个位置。我希望将其放置在另一个线程中,以便可以输出....以指示脚本仍在运行。

我遇到的问题是,如果无法复制文件,它将引发异常。如果在主线程中运行,这没关系;但是,具有以下代码不起作用:

try:
    threadClass = TheThread(param1, param2, etc.)
    threadClass.start()   ##### **Exception takes place here**
except:
    print "Caught an exception"

在线程类本身中,我试图重新引发异常,但是它不起作用。我已经看到这里的人问类似的问题,但是他们似乎都在做比我想做的事情更具体的事情(而且我不太了解所提供的解决方案)。我见过有人提到的用法sys.exc_info(),但是我不知道在哪里或如何使用它。

非常感谢所有帮助!

编辑:线程类的代码如下:

class TheThread(threading.Thread):
    def __init__(self, sourceFolder, destFolder):
        threading.Thread.__init__(self)
        self.sourceFolder = sourceFolder
        self.destFolder = destFolder

    def run(self):
        try:
           shul.copytree(self.sourceFolder, self.destFolder)
        except:
           raise

I’m very new to Python and multithreaded programming in general. Basically, I have a script that will copy files to another location. I would like this to be placed in another thread so I can output .... to indicate that the script is still running.

The problem that I am having is that if the files cannot be copied it will throw an exception. This is ok if running in the main thread; however, having the following code does not work:

try:
    threadClass = TheThread(param1, param2, etc.)
    threadClass.start()   ##### **Exception takes place here**
except:
    print "Caught an exception"

In the thread class itself, I tried to re-throw the exception, but it does not work. I have seen people on here ask similar questions, but they all seem to be doing something more specific than what I am trying to do (and I don’t quite understand the solutions offered). I have seen people mention the usage of sys.exc_info(), however I do not know where or how to use it.

All help is greatly appreciated!

EDIT: The code for the thread class is below:

class TheThread(threading.Thread):
    def __init__(self, sourceFolder, destFolder):
        threading.Thread.__init__(self)
        self.sourceFolder = sourceFolder
        self.destFolder = destFolder

    def run(self):
        try:
           shul.copytree(self.sourceFolder, self.destFolder)
        except:
           raise

回答 0

问题是thread_obj.start()立即返回。您产生的子线程在其自己的上下文中执行,并带有自己的堆栈。在那里发生的任何异常都在子线程的上下文中,并且在其自己的堆栈中。我现在可以想到的一种将此信息传达给父线程的方法是使用某种消息传递,因此您可能会对此进行研究。

尝试以下尺寸:

import sys
import threading
import Queue


class ExcThread(threading.Thread):

    def __init__(self, bucket):
        threading.Thread.__init__(self)
        self.bucket = bucket

    def run(self):
        try:
            raise Exception('An error occured here.')
        except Exception:
            self.bucket.put(sys.exc_info())


def main():
    bucket = Queue.Queue()
    thread_obj = ExcThread(bucket)
    thread_obj.start()

    while True:
        try:
            exc = bucket.get(block=False)
        except Queue.Empty:
            pass
        else:
            exc_type, exc_obj, exc_trace = exc
            # deal with the exception
            print exc_type, exc_obj
            print exc_trace

        thread_obj.join(0.1)
        if thread_obj.isAlive():
            continue
        else:
            break


if __name__ == '__main__':
    main()

The problem is that thread_obj.start() returns immediately. The child thread that you spawned executes in its own context, with its own stack. Any exception that occurs there is in the context of the child thread, and it is in its own stack. One way I can think of right now to communicate this information to the parent thread is by using some sort of message passing, so you might look into that.

Try this on for size:

import sys
import threading
import Queue


class ExcThread(threading.Thread):

    def __init__(self, bucket):
        threading.Thread.__init__(self)
        self.bucket = bucket

    def run(self):
        try:
            raise Exception('An error occured here.')
        except Exception:
            self.bucket.put(sys.exc_info())


def main():
    bucket = Queue.Queue()
    thread_obj = ExcThread(bucket)
    thread_obj.start()

    while True:
        try:
            exc = bucket.get(block=False)
        except Queue.Empty:
            pass
        else:
            exc_type, exc_obj, exc_trace = exc
            # deal with the exception
            print exc_type, exc_obj
            print exc_trace

        thread_obj.join(0.1)
        if thread_obj.isAlive():
            continue
        else:
            break


if __name__ == '__main__':
    main()

回答 1

通过该concurrent.futures模块,可以轻松地在单独的线程(或进程)中进行工作并处理任何导致的异常:

import concurrent.futures
import shutil

def copytree_with_dots(src_path, dst_path):
    with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
        # Execute the copy on a separate thread,
        # creating a future object to track progress.
        future = executor.submit(shutil.copytree, src_path, dst_path)

        while future.running():
            # Print pretty dots here.
            pass

        # Return the value returned by shutil.copytree(), None.
        # Raise any exceptions raised during the copy process.
        return future.result()

concurrent.futures包含在Python 3.2中,并且可以作为早期版本的向后移植futures模块

The concurrent.futures module makes it simple to do work in separate threads (or processes) and handle any resulting exceptions:

import concurrent.futures
import shutil

def copytree_with_dots(src_path, dst_path):
    with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
        # Execute the copy on a separate thread,
        # creating a future object to track progress.
        future = executor.submit(shutil.copytree, src_path, dst_path)

        while future.running():
            # Print pretty dots here.
            pass

        # Return the value returned by shutil.copytree(), None.
        # Raise any exceptions raised during the copy process.
        return future.result()

concurrent.futures is included with Python 3.2, and is available as the backported futures module for earlier versions.


回答 2

这个问题有很多非常复杂的答案。我是否对此简化了,因为这对我来说大多数事情似乎已经足够。

from threading import Thread

class PropagatingThread(Thread):
    def run(self):
        self.exc = None
        try:
            if hasattr(self, '_Thread__target'):
                # Thread uses name mangling prior to Python 3.
                self.ret = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
            else:
                self.ret = self._target(*self._args, **self._kwargs)
        except BaseException as e:
            self.exc = e

    def join(self):
        super(PropagatingThread, self).join()
        if self.exc:
            raise self.exc
        return self.ret

如果确定只能在一个或另一个版本的Python上运行,则可以将该run()方法缩减为仅损坏的版本(如果仅在3之前的Python版本上运行),或者只是干净的版本(如果您只能在以3开头的Python版本上运行)。

用法示例:

def f(*args, **kwargs):
    print(args)
    print(kwargs)
    raise Exception('I suck at this')

t = PropagatingThread(target=f, args=(5,), kwargs={'hello':'world'})
t.start()
t.join()

当您加入时,您将看到另一个线程引发的异常。

如果仅使用sixPython 3或仅在Python 3上使用,则可以改善重新引发异常时所获得的堆栈跟踪信息。您可以将内部异常包装在新的外部异常中,而不是仅在连接时使用堆栈,并使用

six.raise_from(RuntimeError('Exception in thread'),self.exc)

要么

raise RuntimeError('Exception in thread') from self.exc

There are a lot of really weirdly complicated answers to this question. Am I oversimplifying this, because this seems sufficient for most things to me.

from threading import Thread

class PropagatingThread(Thread):
    def run(self):
        self.exc = None
        try:
            if hasattr(self, '_Thread__target'):
                # Thread uses name mangling prior to Python 3.
                self.ret = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
            else:
                self.ret = self._target(*self._args, **self._kwargs)
        except BaseException as e:
            self.exc = e

    def join(self):
        super(PropagatingThread, self).join()
        if self.exc:
            raise self.exc
        return self.ret

If you’re certain you’ll only ever be running on one or the other version of Python, you could reduce the run() method down to just the mangled version (if you’ll only be running on versions of Python before 3), or just the clean version (if you’ll only be running on versions of Python starting with 3).

Example usage:

def f(*args, **kwargs):
    print(args)
    print(kwargs)
    raise Exception('I suck at this')

t = PropagatingThread(target=f, args=(5,), kwargs={'hello':'world'})
t.start()
t.join()

And you’ll see the exception raised on the other thread when you join.

If you are using six or on Python 3 only, you can improve the stack trace information you get when the exception is re-raised. Instead of only the stack at the point of the join, you can wrap the inner exception in a new outer exception, and get both stack traces with

six.raise_from(RuntimeError('Exception in thread'),self.exc)

or

raise RuntimeError('Exception in thread') from self.exc

回答 3

尽管不可能直接捕获在不同线程中引发的异常,但是这里的代码可以透明地获取与该功能非常接近的内容。在等待线程完成其工作时,您的子线程必须ExThread代替该类的子类,threading.Thread并且父线程必须调用该child_thread.join_with_exception()方法,而不是调用该方法child_thread.join()

此实现的技术细节:当子线程引发异常时,它将通过a传递给Queue父线程,然后再次在父线程中引发。请注意,这种方法无需等待。

#!/usr/bin/env python

import sys
import threading
import Queue

class ExThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.__status_queue = Queue.Queue()

    def run_with_exception(self):
        """This method should be overriden."""
        raise NotImplementedError

    def run(self):
        """This method should NOT be overriden."""
        try:
            self.run_with_exception()
        except BaseException:
            self.__status_queue.put(sys.exc_info())
        self.__status_queue.put(None)

    def wait_for_exc_info(self):
        return self.__status_queue.get()

    def join_with_exception(self):
        ex_info = self.wait_for_exc_info()
        if ex_info is None:
            return
        else:
            raise ex_info[1]

class MyException(Exception):
    pass

class MyThread(ExThread):
    def __init__(self):
        ExThread.__init__(self)

    def run_with_exception(self):
        thread_name = threading.current_thread().name
        raise MyException("An error in thread '{}'.".format(thread_name))

def main():
    t = MyThread()
    t.start()
    try:
        t.join_with_exception()
    except MyException as ex:
        thread_name = threading.current_thread().name
        print "Caught a MyException in thread '{}': {}".format(thread_name, ex)

if __name__ == '__main__':
    main()

Although it is not possible to directly catch an exception thrown in a different thread, here’s a code to quite transparently obtain something very close to this functionality. Your child thread must subclass the ExThread class instead of threading.Thread and the parent thread must call the child_thread.join_with_exception() method instead of child_thread.join() when waiting for the thread to finish its job.

Technical details of this implementation: when the child thread throws an exception, it is passed to the parent through a Queue and thrown again in the parent thread. Notice that there’s no busy waiting in this approach .

#!/usr/bin/env python

import sys
import threading
import Queue

class ExThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.__status_queue = Queue.Queue()

    def run_with_exception(self):
        """This method should be overriden."""
        raise NotImplementedError

    def run(self):
        """This method should NOT be overriden."""
        try:
            self.run_with_exception()
        except BaseException:
            self.__status_queue.put(sys.exc_info())
        self.__status_queue.put(None)

    def wait_for_exc_info(self):
        return self.__status_queue.get()

    def join_with_exception(self):
        ex_info = self.wait_for_exc_info()
        if ex_info is None:
            return
        else:
            raise ex_info[1]

class MyException(Exception):
    pass

class MyThread(ExThread):
    def __init__(self):
        ExThread.__init__(self)

    def run_with_exception(self):
        thread_name = threading.current_thread().name
        raise MyException("An error in thread '{}'.".format(thread_name))

def main():
    t = MyThread()
    t.start()
    try:
        t.join_with_exception()
    except MyException as ex:
        thread_name = threading.current_thread().name
        print "Caught a MyException in thread '{}': {}".format(thread_name, ex)

if __name__ == '__main__':
    main()

回答 4

如果线程中发生异常,最好的方法是在期间在调用者线程中重新引发它join。您可以使用该sys.exc_info()函数获取有关当前正在处理的异常的信息。该信息可以简单地存储为线程对象的属性,直到join被调用为止,此时可以重新引发该信息。

请注意,Queue.Queue在这种简单的情况下,线程最多引发1个异常,在引发异常后立即完成,因此不需要(如其他答案中所建议的)。我们只需等待线程完成即可避免出现竞争情况。

例如,扩展ExcThread(在下面),覆盖excRun(而不是run)。

Python 2.x:

import threading

class ExcThread(threading.Thread):
  def excRun(self):
    pass

  def run(self):
    self.exc = None
    try:
      # Possibly throws an exception
      self.excRun()
    except:
      import sys
      self.exc = sys.exc_info()
      # Save details of the exception thrown but don't rethrow,
      # just complete the function

  def join(self):
    threading.Thread.join(self)
    if self.exc:
      msg = "Thread '%s' threw an exception: %s" % (self.getName(), self.exc[1])
      new_exc = Exception(msg)
      raise new_exc.__class__, new_exc, self.exc[2]

Python 3.x:

的3参数形式raise在Python 3中消失了,因此将最后一行更改为:

raise new_exc.with_traceback(self.exc[2])

If an exception occurs in a thread, the best way is to re-raise it in the caller thread during join. You can get information about the exception currently being handled using the sys.exc_info() function. This information can simply be stored as a property of the thread object until join is called, at which point it can be re-raised.

Note that a Queue.Queue (as suggested in other answers) is not necessary in this simple case where the thread throws at most 1 exception and completes right after throwing an exception. We avoid race conditions by simply waiting for the thread to complete.

For example, extend ExcThread (below), overriding excRun (instead of run).

Python 2.x:

import threading

class ExcThread(threading.Thread):
  def excRun(self):
    pass

  def run(self):
    self.exc = None
    try:
      # Possibly throws an exception
      self.excRun()
    except:
      import sys
      self.exc = sys.exc_info()
      # Save details of the exception thrown but don't rethrow,
      # just complete the function

  def join(self):
    threading.Thread.join(self)
    if self.exc:
      msg = "Thread '%s' threw an exception: %s" % (self.getName(), self.exc[1])
      new_exc = Exception(msg)
      raise new_exc.__class__, new_exc, self.exc[2]

Python 3.x:

The 3 argument form for raise is gone in Python 3, so change the last line to:

raise new_exc.with_traceback(self.exc[2])

回答 5

concurrent.futures.as_completed

https://docs.python.org/3.7/library/concurrent.futures.html#concurrent.futures.as_completed

以下解决方法:

  • 调用异常后立即返回主线程
  • 不需要额外的用户定义类,因为它不需要:
    • 明确的 Queue
    • 在工作线程周围添加一个else

资源:

#!/usr/bin/env python3

import concurrent.futures
import time

def func_that_raises(do_raise):
    for i in range(3):
        print(i)
        time.sleep(0.1)
    if do_raise:
        raise Exception()
    for i in range(3):
        print(i)
        time.sleep(0.1)

with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
    futures = []
    futures.append(executor.submit(func_that_raises, False))
    futures.append(executor.submit(func_that_raises, True))
    for future in concurrent.futures.as_completed(futures):
        print(repr(future.exception()))

可能的输出:

0
0
1
1
2
2
0
Exception()
1
2
None

不幸的是,由于一个交易失败,不可能杀死期货以取消其他交易:

如果您执行以下操作:

for future in concurrent.futures.as_completed(futures):
    if future.exception() is not None:
        raise future.exception()

然后with捕获它,并等待第二个线程完成后再继续。以下行为类似:

for future in concurrent.futures.as_completed(futures):
    future.result()

因为future.result()如果发生一个异常,则会重新引发异常。

如果您想退出整个Python过程,则可以使用os._exit(0),但是这可能意味着您需要重构。

具有完美异常语义的自定义类

我最终在以下方面为自己编写了一个完美的接口:限制一次运行的最大线程数的正确方法?部分“带有错误处理的队列示例”。该类旨在既方便,又使您可以完全控制提交和结果/错误处理。

在Python 3.6.7,Ubuntu 18.04上进行了测试。

concurrent.futures.as_completed

https://docs.python.org/3.7/library/concurrent.futures.html#concurrent.futures.as_completed

The following solution:

  • returns to the main thread immediately when an exception is called
  • requires no extra user defined classes because it does not need:
    • an explicit Queue
    • to add an except else around your work thread

Source:

#!/usr/bin/env python3

import concurrent.futures
import time

def func_that_raises(do_raise):
    for i in range(3):
        print(i)
        time.sleep(0.1)
    if do_raise:
        raise Exception()
    for i in range(3):
        print(i)
        time.sleep(0.1)

with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
    futures = []
    futures.append(executor.submit(func_that_raises, False))
    futures.append(executor.submit(func_that_raises, True))
    for future in concurrent.futures.as_completed(futures):
        print(repr(future.exception()))

Possible output:

0
0
1
1
2
2
0
Exception()
1
2
None

It is unfortunately not possible to kill futures to cancel the others as one fails:

If you do something like:

for future in concurrent.futures.as_completed(futures):
    if future.exception() is not None:
        raise future.exception()

then the with catches it, and waits for the second thread to finish before continuing. The following behaves similarly:

for future in concurrent.futures.as_completed(futures):
    future.result()

since future.result() re-raises the exception if one occurred.

If you want to quit the entire Python process, you might get away with os._exit(0), but this likely means you need a refactor.

Custom class with perfect exception semantics

I ended up coding the perfect interface for myself at: The right way to limit maximum number of threads running at once? section “Queue example with error handling”. That class aims to be both convenient, and give you total control over submission and result / error handling.

Tested on Python 3.6.7, Ubuntu 18.04.


回答 6

这是一个令人讨厌的小问题,我想提出自己的解决方案。我发现的其他一些解决方案(例如async.io)看起来很有希望,但也有一些黑匣子。队列/事件循环方法将您与特定实现联系在一起。但是,并发的期货源代码大约只有1000行,并且很容易理解。它使我可以轻松地解决我的问题:无需进行过多设置即可创建临时工作线程,并能够捕获主线程中的异常。

我的解决方案使用并发的期货API和线程API。它允许您创建一个工作线程,为您提供线程和未来。这样,您可以加入线程以等待结果:

worker = Worker(test)
thread = worker.start()
thread.join()
print(worker.future.result())

…或者您可以让工作人员在完成后仅发送回调:

worker = Worker(test)
thread = worker.start(lambda x: print('callback', x))

…或者您可以循环播放直到事件结束:

worker = Worker(test)
thread = worker.start()

while True:
    print("waiting")
    if worker.future.done():
        exc = worker.future.exception()
        print('exception?', exc)
        result = worker.future.result()
        print('result', result)           
        break
    time.sleep(0.25)

这是代码:

from concurrent.futures import Future
import threading
import time

class Worker(object):
    def __init__(self, fn, args=()):
        self.future = Future()
        self._fn = fn
        self._args = args

    def start(self, cb=None):
        self._cb = cb
        self.future.set_running_or_notify_cancel()
        thread = threading.Thread(target=self.run, args=())
        thread.daemon = True #this will continue thread execution after the main thread runs out of code - you can still ctrl + c or kill the process
        thread.start()
        return thread

    def run(self):
        try:
            self.future.set_result(self._fn(*self._args))
        except BaseException as e:
            self.future.set_exception(e)

        if(self._cb):
            self._cb(self.future.result())

…以及测试功能:

def test(*args):
    print('args are', args)
    time.sleep(2)
    raise Exception('foo')

This was a nasty little problem, and I’d like to throw my solution in. Some other solutions I found (async.io for example) looked promising but also presented a bit of a black box. The queue / event loop approach sort of ties you to a certain implementation. The concurrent futures source code, however, is around only 1000 lines, and easy to comprehend. It allowed me to easily solve my problem: create ad-hoc worker threads without much setup, and to be able to catch exceptions in the main thread.

My solution uses the concurrent futures API and threading API. It allows you to create a worker which gives you both the thread and the future. That way, you can join the thread to wait for the result:

worker = Worker(test)
thread = worker.start()
thread.join()
print(worker.future.result())

…or you can let the worker just send a callback when done:

worker = Worker(test)
thread = worker.start(lambda x: print('callback', x))

…or you can loop until the event completes:

worker = Worker(test)
thread = worker.start()

while True:
    print("waiting")
    if worker.future.done():
        exc = worker.future.exception()
        print('exception?', exc)
        result = worker.future.result()
        print('result', result)           
        break
    time.sleep(0.25)

Here’s the code:

from concurrent.futures import Future
import threading
import time

class Worker(object):
    def __init__(self, fn, args=()):
        self.future = Future()
        self._fn = fn
        self._args = args

    def start(self, cb=None):
        self._cb = cb
        self.future.set_running_or_notify_cancel()
        thread = threading.Thread(target=self.run, args=())
        thread.daemon = True #this will continue thread execution after the main thread runs out of code - you can still ctrl + c or kill the process
        thread.start()
        return thread

    def run(self):
        try:
            self.future.set_result(self._fn(*self._args))
        except BaseException as e:
            self.future.set_exception(e)

        if(self._cb):
            self._cb(self.future.result())

…and the test function:

def test(*args):
    print('args are', args)
    time.sleep(2)
    raise Exception('foo')

回答 7

作为Threading的入门者,我花了很长时间了解如何实现Mateusz Kobos的代码(上述)。这是一个澄清的版本,可帮助您了解如何使用它。

#!/usr/bin/env python

import sys
import threading
import Queue

class ExThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.__status_queue = Queue.Queue()

    def run_with_exception(self):
        """This method should be overriden."""
        raise NotImplementedError

    def run(self):
        """This method should NOT be overriden."""
        try:
            self.run_with_exception()
        except Exception:
            self.__status_queue.put(sys.exc_info())
        self.__status_queue.put(None)

    def wait_for_exc_info(self):
        return self.__status_queue.get()

    def join_with_exception(self):
        ex_info = self.wait_for_exc_info()
        if ex_info is None:
            return
        else:
            raise ex_info[1]

class MyException(Exception):
    pass

class MyThread(ExThread):
    def __init__(self):
        ExThread.__init__(self)

    # This overrides the "run_with_exception" from class "ExThread"
    # Note, this is where the actual thread to be run lives. The thread
    # to be run could also call a method or be passed in as an object
    def run_with_exception(self):
        # Code will function until the int
        print "sleeping 5 seconds"
        import time
        for i in 1, 2, 3, 4, 5:
            print i
            time.sleep(1) 
        # Thread should break here
        int("str")
# I'm honestly not sure why these appear here? So, I removed them. 
# Perhaps Mateusz can clarify?        
#         thread_name = threading.current_thread().name
#         raise MyException("An error in thread '{}'.".format(thread_name))

if __name__ == '__main__':
    # The code lives in MyThread in this example. So creating the MyThread 
    # object set the code to be run (but does not start it yet)
    t = MyThread()
    # This actually starts the thread
    t.start()
    print
    print ("Notice 't.start()' is considered to have completed, although" 
           " the countdown continues in its new thread. So you code "
           "can tinue into new processing.")
    # Now that the thread is running, the join allows for monitoring of it
    try:
        t.join_with_exception()
    # should be able to be replace "Exception" with specific error (untested)
    except Exception, e: 
        print
        print "Exceptioon was caught and control passed back to the main thread"
        print "Do some handling here...or raise a custom exception "
        thread_name = threading.current_thread().name
        e = ("Caught a MyException in thread: '" + 
             str(thread_name) + 
             "' [" + str(e) + "]")
        raise Exception(e) # Or custom class of exception, such as MyException

As a noobie to Threading, it took me a long time to understand how to implement Mateusz Kobos’s code (above). Here’s a clarified version to help understand how to use it.

#!/usr/bin/env python

import sys
import threading
import Queue

class ExThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.__status_queue = Queue.Queue()

    def run_with_exception(self):
        """This method should be overriden."""
        raise NotImplementedError

    def run(self):
        """This method should NOT be overriden."""
        try:
            self.run_with_exception()
        except Exception:
            self.__status_queue.put(sys.exc_info())
        self.__status_queue.put(None)

    def wait_for_exc_info(self):
        return self.__status_queue.get()

    def join_with_exception(self):
        ex_info = self.wait_for_exc_info()
        if ex_info is None:
            return
        else:
            raise ex_info[1]

class MyException(Exception):
    pass

class MyThread(ExThread):
    def __init__(self):
        ExThread.__init__(self)

    # This overrides the "run_with_exception" from class "ExThread"
    # Note, this is where the actual thread to be run lives. The thread
    # to be run could also call a method or be passed in as an object
    def run_with_exception(self):
        # Code will function until the int
        print "sleeping 5 seconds"
        import time
        for i in 1, 2, 3, 4, 5:
            print i
            time.sleep(1) 
        # Thread should break here
        int("str")
# I'm honestly not sure why these appear here? So, I removed them. 
# Perhaps Mateusz can clarify?        
#         thread_name = threading.current_thread().name
#         raise MyException("An error in thread '{}'.".format(thread_name))

if __name__ == '__main__':
    # The code lives in MyThread in this example. So creating the MyThread 
    # object set the code to be run (but does not start it yet)
    t = MyThread()
    # This actually starts the thread
    t.start()
    print
    print ("Notice 't.start()' is considered to have completed, although" 
           " the countdown continues in its new thread. So you code "
           "can tinue into new processing.")
    # Now that the thread is running, the join allows for monitoring of it
    try:
        t.join_with_exception()
    # should be able to be replace "Exception" with specific error (untested)
    except Exception, e: 
        print
        print "Exceptioon was caught and control passed back to the main thread"
        print "Do some handling here...or raise a custom exception "
        thread_name = threading.current_thread().name
        e = ("Caught a MyException in thread: '" + 
             str(thread_name) + 
             "' [" + str(e) + "]")
        raise Exception(e) # Or custom class of exception, such as MyException

回答 8

类似于RickardSjogren的没有Queue,sys等的方式,但是也没有一些信号侦听器:直接执行与except块相对应的异常处理程序。

#!/usr/bin/env python3

import threading

class ExceptionThread(threading.Thread):

    def __init__(self, callback=None, *args, **kwargs):
        """
        Redirect exceptions of thread to an exception handler.

        :param callback: function to handle occured exception
        :type callback: function(thread, exception)
        :param args: arguments for threading.Thread()
        :type args: tuple
        :param kwargs: keyword arguments for threading.Thread()
        :type kwargs: dict
        """
        self._callback = callback
        super().__init__(*args, **kwargs)

    def run(self):
        try:
            if self._target:
                self._target(*self._args, **self._kwargs)
        except BaseException as e:
            if self._callback is None:
                raise e
            else:
                self._callback(self, e)
        finally:
            # Avoid a refcycle if the thread is running a function with
            # an argument that has a member that points to the thread.
            del self._target, self._args, self._kwargs, self._callback

仅self._callback和run()中的except块是普通threading.Thread的附加项。

Similar way like RickardSjogren’s without Queue, sys etc. but also without some listeners to signals: execute directly an exception handler which corresponds to an except block.

#!/usr/bin/env python3

import threading

class ExceptionThread(threading.Thread):

    def __init__(self, callback=None, *args, **kwargs):
        """
        Redirect exceptions of thread to an exception handler.

        :param callback: function to handle occured exception
        :type callback: function(thread, exception)
        :param args: arguments for threading.Thread()
        :type args: tuple
        :param kwargs: keyword arguments for threading.Thread()
        :type kwargs: dict
        """
        self._callback = callback
        super().__init__(*args, **kwargs)

    def run(self):
        try:
            if self._target:
                self._target(*self._args, **self._kwargs)
        except BaseException as e:
            if self._callback is None:
                raise e
            else:
                self._callback(self, e)
        finally:
            # Avoid a refcycle if the thread is running a function with
            # an argument that has a member that points to the thread.
            del self._target, self._args, self._kwargs, self._callback

Only self._callback and the except block in run() is additional to normal threading.Thread.


回答 9

我知道我在这里参加聚会有点晚了,但是我遇到了一个非常类似的问题,但是它包括使用tkinter作为GUI,并且mainloop使得无法使用任何依赖.join()的解决方案。因此,我改编了原始问题的EDIT中给出的解决方案,但使其变得更笼统,以使其他人易于理解。

这是正在使用的新线程类:

import threading
import traceback
import logging


class ExceptionThread(threading.Thread):
    def __init__(self, *args, **kwargs):
        threading.Thread.__init__(self, *args, **kwargs)

    def run(self):
        try:
            if self._target:
                self._target(*self._args, **self._kwargs)
        except Exception:
            logging.error(traceback.format_exc())


def test_function_1(input):
    raise IndexError(input)


if __name__ == "__main__":
    input = 'useful'

    t1 = ExceptionThread(target=test_function_1, args=[input])
    t1.start()

当然,从日志记录中,您总是可以用其他方法来处理异常,例如将其打印出来或将其输出到控制台。

这使您可以像完全使用Thread类一样使用ExceptionThread类,而无需进行任何特殊修改。

I know I’m a bit late to the party here but I was having a very similar problem but it included using tkinter as a GUI, and the mainloop made it impossible to use any of the solutions that depend on .join(). Therefore I adapted the solution given in the EDIT of the original question, but made it more general to make it easier to understand for others.

Here is the new thread class in action:

import threading
import traceback
import logging


class ExceptionThread(threading.Thread):
    def __init__(self, *args, **kwargs):
        threading.Thread.__init__(self, *args, **kwargs)

    def run(self):
        try:
            if self._target:
                self._target(*self._args, **self._kwargs)
        except Exception:
            logging.error(traceback.format_exc())


def test_function_1(input):
    raise IndexError(input)


if __name__ == "__main__":
    input = 'useful'

    t1 = ExceptionThread(target=test_function_1, args=[input])
    t1.start()

Of course you can always have it handle the exception some other way from logging, such as printing it out, or having it output to the console.

This allows you to use the ExceptionThread class exactly like you would the Thread class, without any special modifications.


回答 10

我喜欢的一种方法是基于观察者模式。我定义了一个信号类,我的线程使用该信号类向侦听器发出异常。它也可以用于从线程返回值。例:

import threading

class Signal:
    def __init__(self):
        self._subscribers = list()

    def emit(self, *args, **kwargs):
        for func in self._subscribers:
            func(*args, **kwargs)

    def connect(self, func):
        self._subscribers.append(func)

    def disconnect(self, func):
        try:
            self._subscribers.remove(func)
        except ValueError:
            raise ValueError('Function {0} not removed from {1}'.format(func, self))


class WorkerThread(threading.Thread):

    def __init__(self, *args, **kwargs):
        super(WorkerThread, self).__init__(*args, **kwargs)
        self.Exception = Signal()
        self.Result = Signal()

    def run(self):
        if self._Thread__target is not None:
            try:
                self._return_value = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
            except Exception as e:
                self.Exception.emit(e)
            else:
                self.Result.emit(self._return_value)

if __name__ == '__main__':
    import time

    def handle_exception(exc):
        print exc.message

    def handle_result(res):
        print res

    def a():
        time.sleep(1)
        raise IOError('a failed')

    def b():
        time.sleep(2)
        return 'b returns'

    t = WorkerThread(target=a)
    t2 = WorkerThread(target=b)
    t.Exception.connect(handle_exception)
    t2.Result.connect(handle_result)
    t.start()
    t2.start()

    print 'Threads started'

    t.join()
    t2.join()
    print 'Done'

我没有使用线程的足够经验来宣称这是一种完全安全的方法。但这对我有用,我喜欢这种灵活性。

One method I am fond of is based on the observer pattern. I define a signal class which my thread uses to emit exceptions to listeners. It can also be used to return values from threads. Example:

import threading

class Signal:
    def __init__(self):
        self._subscribers = list()

    def emit(self, *args, **kwargs):
        for func in self._subscribers:
            func(*args, **kwargs)

    def connect(self, func):
        self._subscribers.append(func)

    def disconnect(self, func):
        try:
            self._subscribers.remove(func)
        except ValueError:
            raise ValueError('Function {0} not removed from {1}'.format(func, self))


class WorkerThread(threading.Thread):

    def __init__(self, *args, **kwargs):
        super(WorkerThread, self).__init__(*args, **kwargs)
        self.Exception = Signal()
        self.Result = Signal()

    def run(self):
        if self._Thread__target is not None:
            try:
                self._return_value = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
            except Exception as e:
                self.Exception.emit(e)
            else:
                self.Result.emit(self._return_value)

if __name__ == '__main__':
    import time

    def handle_exception(exc):
        print exc.message

    def handle_result(res):
        print res

    def a():
        time.sleep(1)
        raise IOError('a failed')

    def b():
        time.sleep(2)
        return 'b returns'

    t = WorkerThread(target=a)
    t2 = WorkerThread(target=b)
    t.Exception.connect(handle_exception)
    t2.Result.connect(handle_result)
    t.start()
    t2.start()

    print 'Threads started'

    t.join()
    t2.join()
    print 'Done'

I do not have enough experience of working with threads to claim that this is a completely safe method. But it has worked for me and I like the flexibility.


回答 11

使用裸露的exceptions不是一个好习惯,因为您通常会收获比讨价还价更多的东西。

我建议修改,except使其仅捕获您要处理的异常。我认为提高它并没有达到预期的效果,因为当您TheThread在外部实例化时try,如果它引发异常,则分配将永远不会发生。

相反,您可能只想提醒它并继续前进,例如:

def run(self):
    try:
       shul.copytree(self.sourceFolder, self.destFolder)
    except OSError, err:
       print err

然后,当该异常被捕获时,您可以在那里处理它。然后,当外部程序try从中捕获到异常时TheThread,您就会知道它不会是您已经处理过的异常,它将帮助您隔离处理流程。

Using naked excepts is not a good practice because you usually catch more than you bargain for.

I would suggest modifying the except to catch ONLY the exception that you would like to handle. I don’t think that raising it has the desired effect, because when you go to instantiate TheThread in the outer try, if it raises an exception, the assignment is never going to happen.

Instead you might want to just alert on it and move on, such as:

def run(self):
    try:
       shul.copytree(self.sourceFolder, self.destFolder)
    except OSError, err:
       print err

Then when that exception is caught, you can handle it there. Then when the outer try catches an exception from TheThread, you know it won’t be the one you already handled, and will help you isolate your process flow.


回答 12

捕获线程异常并将其传递回调用方方法的一种简单方法是将字典或列表传递给worker方法。

示例(将字典传递给worker方法):

import threading

def my_method(throw_me):
    raise Exception(throw_me)

def worker(shared_obj, *args, **kwargs):
    try:
        shared_obj['target'](*args, **kwargs)
    except Exception as err:
        shared_obj['err'] = err

shared_obj = {'err':'', 'target': my_method}
throw_me = "Test"

th = threading.Thread(target=worker, args=(shared_obj, throw_me), kwargs={})
th.start()
th.join()

if shared_obj['err']:
    print(">>%s" % shared_obj['err'])

A simple way of catching thread’s exception and communicating back to the caller method could be by passing dictionary or a list to worker method.

Example (passing dictionary to worker method):

import threading

def my_method(throw_me):
    raise Exception(throw_me)

def worker(shared_obj, *args, **kwargs):
    try:
        shared_obj['target'](*args, **kwargs)
    except Exception as err:
        shared_obj['err'] = err

shared_obj = {'err':'', 'target': my_method}
throw_me = "Test"

th = threading.Thread(target=worker, args=(shared_obj, throw_me), kwargs={})
th.start()
th.join()

if shared_obj['err']:
    print(">>%s" % shared_obj['err'])

回答 13

用异常存储包装线程。

import threading
import sys
class ExcThread(threading.Thread):

    def __init__(self, target, args = None):
        self.args = args if args else []
        self.target = target
        self.exc = None
        threading.Thread.__init__(self)

    def run(self):
        try:
            self.target(*self.args)
            raise Exception('An error occured here.')
        except Exception:
            self.exc=sys.exc_info()

def main():
    def hello(name):
        print(!"Hello, {name}!")
    thread_obj = ExcThread(target=hello, args=("Jack"))
    thread_obj.start()

    thread_obj.join()
    exc = thread_obj.exc
    if exc:
        exc_type, exc_obj, exc_trace = exc
        print(exc_type, ':',exc_obj, ":", exc_trace)

main()

Wrap Thread with exception storage.

import threading
import sys
class ExcThread(threading.Thread):

    def __init__(self, target, args = None):
        self.args = args if args else []
        self.target = target
        self.exc = None
        threading.Thread.__init__(self)

    def run(self):
        try:
            self.target(*self.args)
            raise Exception('An error occured here.')
        except Exception:
            self.exc=sys.exc_info()

def main():
    def hello(name):
        print(!"Hello, {name}!")
    thread_obj = ExcThread(target=hello, args=("Jack"))
    thread_obj.start()

    thread_obj.join()
    exc = thread_obj.exc
    if exc:
        exc_type, exc_obj, exc_trace = exc
        print(exc_type, ':',exc_obj, ":", exc_trace)

main()

回答 14

pygolang提供sync.WorkGroup,尤其是将异常从生成的工作线程传播到主线程。例如:

#!/usr/bin/env python
"""This program demostrates how with sync.WorkGroup an exception raised in
spawned thread is propagated into main thread which spawned the worker."""

from __future__ import print_function
from golang import sync, context

def T1(ctx, *argv):
    print('T1: run ... %r' % (argv,))
    raise RuntimeError('T1: problem')

def T2(ctx):
    print('T2: ran ok')

def main():
    wg = sync.WorkGroup(context.background())
    wg.go(T1, [1,2,3])
    wg.go(T2)

    try:
        wg.wait()
    except Exception as e:
        print('Tmain: caught exception: %r\n' %e)
        # reraising to see full traceback
        raise

if __name__ == '__main__':
    main()

运行时给出以下内容:

T1: run ... ([1, 2, 3],)
T2: ran ok
Tmain: caught exception: RuntimeError('T1: problem',)

Traceback (most recent call last):
  File "./x.py", line 28, in <module>
    main()
  File "./x.py", line 21, in main
    wg.wait()
  File "golang/_sync.pyx", line 198, in golang._sync.PyWorkGroup.wait
    pyerr_reraise(pyerr)
  File "golang/_sync.pyx", line 178, in golang._sync.PyWorkGroup.go.pyrunf
    f(pywg._pyctx, *argv, **kw)
  File "./x.py", line 10, in T1
    raise RuntimeError('T1: problem')
RuntimeError: T1: problem

该问题的原始代码就是:

    wg = sync.WorkGroup(context.background())

    def _(ctx):
        shul.copytree(sourceFolder, destFolder)
    wg.go(_)

    # waits for spawned worker to complete and, on error, reraises
    # its exception on the main thread.
    wg.wait()

pygolang provides sync.WorkGroup which, in particular, propagates exception from spawned worker threads to the main thread. For example:

#!/usr/bin/env python
"""This program demostrates how with sync.WorkGroup an exception raised in
spawned thread is propagated into main thread which spawned the worker."""

from __future__ import print_function
from golang import sync, context

def T1(ctx, *argv):
    print('T1: run ... %r' % (argv,))
    raise RuntimeError('T1: problem')

def T2(ctx):
    print('T2: ran ok')

def main():
    wg = sync.WorkGroup(context.background())
    wg.go(T1, [1,2,3])
    wg.go(T2)

    try:
        wg.wait()
    except Exception as e:
        print('Tmain: caught exception: %r\n' %e)
        # reraising to see full traceback
        raise

if __name__ == '__main__':
    main()

gives the following when run:

T1: run ... ([1, 2, 3],)
T2: ran ok
Tmain: caught exception: RuntimeError('T1: problem',)

Traceback (most recent call last):
  File "./x.py", line 28, in <module>
    main()
  File "./x.py", line 21, in main
    wg.wait()
  File "golang/_sync.pyx", line 198, in golang._sync.PyWorkGroup.wait
    pyerr_reraise(pyerr)
  File "golang/_sync.pyx", line 178, in golang._sync.PyWorkGroup.go.pyrunf
    f(pywg._pyctx, *argv, **kw)
  File "./x.py", line 10, in T1
    raise RuntimeError('T1: problem')
RuntimeError: T1: problem

The original code from the question would be just:

    wg = sync.WorkGroup(context.background())

    def _(ctx):
        shul.copytree(sourceFolder, destFolder)
    wg.go(_)

    # waits for spawned worker to complete and, on error, reraises
    # its exception on the main thread.
    wg.wait()

为什么此迭代列表增长代码使IndexError:列表分配索引超出范围?

问题:为什么此迭代列表增长代码使IndexError:列表分配索引超出范围?

请考虑以下代码:

i = [1, 2, 3, 5, 8, 13]
j = []
k = 0

for l in i:
    j[k] = l
    k += 1

print j

输出(Win 7 32位上的Python 2.6.6)为:

> Traceback (most recent call last): 
>     j[k] = l IndexError: list assignment index out of range

我想这很简单,我不明白。有人可以清理吗?

Please consider the following code:

i = [1, 2, 3, 5, 8, 13]
j = []
k = 0

for l in i:
    j[k] = l
    k += 1

print j

The output (Python 2.6.6 on Win 7 32-bit) is:

> Traceback (most recent call last): 
>     j[k] = l IndexError: list assignment index out of range

I guess it’s something simple I don’t understand. Can someone clear it up?


回答 0

j是一个空列表,但您正在尝试[0]在第一次迭代中写入元素,但该迭代尚不存在。

请尝试以下操作,以将新元素添加到列表的末尾:

for l in i:
    j.append(l)

当然,如果您只想复制一个现有列表,则您永远都不会这样做。您只需:

j = list(i)

另外,如果您想像其他语言的数组一样使用Python列表,则可以预先创建一个列表,并将其元素设置为空值(None在下面的示例中),然后在特定位置覆盖值:

i = [1, 2, 3, 5, 8, 13]
j = [None] * len(i)
#j == [None, None, None, None, None, None]
k = 0

for l in i:
   j[k] = l
   k += 1

要实现的事情是,list对象将不允许您将值分配给不存在的索引。

j is an empty list, but you’re attempting to write to element [0] in the first iteration, which doesn’t exist yet.

Try the following instead, to add a new element to the end of the list:

for l in i:
    j.append(l)

Of course, you’d never do this in practice if all you wanted to do was to copy an existing list. You’d just do:

j = list(i)

Alternatively, if you wanted to use the Python list like an array in other languages, then you could pre-create a list with its elements set to a null value (None in the example below), and later, overwrite the values in specific positions:

i = [1, 2, 3, 5, 8, 13]
j = [None] * len(i)
#j == [None, None, None, None, None, None]
k = 0

for l in i:
   j[k] = l
   k += 1

The thing to realise is that a list object will not allow you to assign a value to an index that doesn’t exist.


回答 1

您的另一个选择是初始化j

j = [None] * len(i)

Your other option is to initialize j:

j = [None] * len(i)

回答 2

j.append(l)而不是j[k] = l避免k

Do j.append(l) instead of j[k] = l and avoid k at all.


回答 3

您还可以使用列表理解:

j = [l for l in i]

或使用以下语句进行复制:

j = i[:]

You could also use a list comprehension:

j = [l for l in i]

or make a copy of it using the statement:

j = i[:]

回答 4

j.append(l)

还要避免使用小写的“ L”,因为它们很容易与1混淆。

j.append(l)

Also avoid using lower-case “L’s” because it is easy for them to be confused with 1’s


回答 5

我认为您正在寻找Python方法插入项

在位置i处插入元素x。list.insert(i,x)

array = [1,2,3,4,5]

array.insert(1,20)

print(array)

# prints [1,2,20,3,4,5]

I think the Python method insert is what you’re looking for:

Inserts element x at position i. list.insert(i,x)

array = [1,2,3,4,5]

array.insert(1,20)

print(array)

# prints [1,2,20,3,4,5]

回答 6

您可以为j使用字典(类似于关联数组)

i = [1, 2, 3, 5, 8, 13]
j = {} #initiate as dictionary
k = 0

for l in i:
    j[k] = l
    k += 1

print j

将打印:

{0: 1, 1: 2, 2: 3, 3: 5, 4: 8, 5: 13}

You could use a dictionary (similar to an associative array) for j

i = [1, 2, 3, 5, 8, 13]
j = {} #initiate as dictionary
k = 0

for l in i:
    j[k] = l
    k += 1

print j

will print :

{0: 1, 1: 2, 2: 3, 3: 5, 4: 8, 5: 13}

回答 7

另一种方式:

j=i[0]
for k in range(1,len(i)):
    j = numpy.vstack([j,i[k]])

在这种情况下j将是一个numpy数组

One more way:

j=i[0]
for k in range(1,len(i)):
    j = numpy.vstack([j,i[k]])

In this case j will be a numpy array


回答 8

也许你需要extend()

i=[1,3,5,7]
j=[]
j.extend(i)

Maybe you need extend()

i=[1,3,5,7]
j=[]
j.extend(i)

Python 2.6中不推荐使用BaseException.message

问题:Python 2.6中不推荐使用BaseException.message

当我使用以下用户定义的异常时,我收到一条警告:Python 2.6中不推荐使用BaseException.message:

class MyException(Exception):

    def __init__(self, message):
        self.message = message

    def __str__(self):
        return repr(self.message)

这是警告:

DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
self.message = message

这怎么了 我要更改什么以摆脱过时警告?

I get a warning that BaseException.message is deprecated in Python 2.6 when I use the following user-defined exception:

class MyException(Exception):

    def __init__(self, message):
        self.message = message

    def __str__(self):
        return repr(self.message)

This is the warning:

DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
self.message = message

What’s wrong with this? What do I have to change to get rid of the deprecation warning?


回答 0

解决方案-几乎不需要编码

只是继承您的异常类,Exception并将消息作为第一个参数传递给构造函数

例:

class MyException(Exception):
    """My documentation"""

try:
    raise MyException('my detailed description')
except MyException as my:
    print my # outputs 'my detailed description'

您可以使用str(my)或(不太优雅)my.args[0]来访问自定义消息。

背景

在较新的Python版本(从2.6开始)中,我们应该从Exception(从Python 2.5开始)继承自BaseException的自定义异常类中继承。在PEP 352中详细描述了背景。

class BaseException(object):

    """Superclass representing the base of the exception hierarchy.
    Provides an 'args' attribute that contains all arguments passed
    to the constructor.  Suggested practice, though, is that only a
    single string argument be passed to the constructor."""

__str__并且__repr__已经以有意义的方式实现,尤其是对于仅一个arg(可用作消息)的情况。

您无需重复__str____init__实施或创建_get_message其他人建议的内容。

Solution – almost no coding needed

Just inherit your exception class from Exception and pass the message as the first parameter to the constructor

Example:

class MyException(Exception):
    """My documentation"""

try:
    raise MyException('my detailed description')
except MyException as my:
    print my # outputs 'my detailed description'

You can use str(my) or (less elegant) my.args[0] to access the custom message.

Background

In the newer versions of Python (from 2.6) we are supposed to inherit our custom exception classes from Exception which (starting from Python 2.5) inherits from BaseException. The background is described in detail in PEP 352.

class BaseException(object):

    """Superclass representing the base of the exception hierarchy.
    Provides an 'args' attribute that contains all arguments passed
    to the constructor.  Suggested practice, though, is that only a
    single string argument be passed to the constructor."""

__str__ and __repr__ are already implemented in a meaningful way, especially for the case of only one arg (that can be used as message).

You do not need to repeat __str__ or __init__ implementation or create _get_message as suggested by others.


回答 1

是的,它在Python 2.6中已弃用,因为它在Python 3.0中已经不存在了

BaseException类不再提供存储错误消息的方法。您必须自己实施。您可以使用一个子类来执行此操作,该子类使用用于存储消息的属性。

class MyException(Exception):
    def _get_message(self): 
        return self._message
    def _set_message(self, message): 
        self._message = message
    message = property(_get_message, _set_message)

希望这可以帮助

Yes, it’s deprecated in Python 2.6 because it’s going away in Python 3.0

BaseException class does not provide a way to store error message anymore. You’ll have to implement it yourself. You can do this with a subclass that uses a property for storing the message.

class MyException(Exception):
    def _get_message(self): 
        return self._message
    def _set_message(self, message): 
        self._message = message
    message = property(_get_message, _set_message)

Hope this helps


回答 2

class MyException(Exception):

    def __str__(self):
        return repr(self.args[0])

e = MyException('asdf')
print e

这是您使用Python2.6样式的类。新的异常采用任意数量的参数。

class MyException(Exception):

    def __str__(self):
        return repr(self.args[0])

e = MyException('asdf')
print e

This is your class in Python2.6 style. The new exception takes an arbitrary number of arguments.


回答 3

如何复制警告

让我澄清一下问题,因为您无法使用问题的示例代码来复制此问题,如果您启用了警告(通过-WflagPYTHONWARNINGSenvironment变量或warnings模块),则会在Python 2.6和2.7中复制警告:

>>> error = Exception('foobarbaz')
>>> error.message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foobarbaz'

停止使用 .message

我更喜欢使用repr(error),它返回一个字符串,其中包含错误类型的名称,消息的repr(如果有的话)以及其余参数的repr。

>>> repr(error)
"Exception('foobarbaz',)"

仍在使用时消除警告 .message

你得到的方式干掉DeprecationWarning是继承一个内置的exceptions,因为Python的设计意图:

class MyException(Exception):

    def __init__(self, message, *args):
        self.message = message
        # delegate the rest of initialization to parent
        super(MyException, self).__init__(message, *args)

>>> myexception = MyException('my message')
>>> myexception.message
'my message'
>>> str(myexception)
'my message'
>>> repr(myexception)
"MyException('my message',)"

只获得.message属性而没有error.message

如果您知道要向Exception发送一个参数,即一条消息,这就是您想要的,那么最好避免使用message属性,而只处理str错误。说一个子类Exception

class MyException(Exception):
    '''demo straight subclass'''

和用法:

>>> myexception = MyException('my message')
>>> str(myexception)
'my message'

另请参阅以下答案:

在现代Python中声明自定义异常的正确方法?

How to replicate the warning

Let me clarify the problem, as one cannot replicate this with the question’s sample code, this will replicate the warning in Python 2.6 and 2.7, if you have warnings turned on (via the -W flag, the PYTHONWARNINGS environment variable, or the warnings module):

>>> error = Exception('foobarbaz')
>>> error.message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foobarbaz'

Stop using .message

I prefer repr(error), which returns a string that contains the name of the error type, the repr of the message, if there is one, and the repr of the remaining arguments.

>>> repr(error)
"Exception('foobarbaz',)"

Eliminating the warning while still using .message

And the way you get rid of the DeprecationWarning is to subclass a builtin exception as the Python designers intended:

class MyException(Exception):

    def __init__(self, message, *args):
        self.message = message
        # delegate the rest of initialization to parent
        super(MyException, self).__init__(message, *args)

>>> myexception = MyException('my message')
>>> myexception.message
'my message'
>>> str(myexception)
'my message'
>>> repr(myexception)
"MyException('my message',)"

getting just the .message attribute without error.message

If you know there was one argument, a message, to the Exception and that’s what you want, it is preferable to avoid the message attribute and just take the str of the error. Say for a subclassed Exception:

class MyException(Exception):
    '''demo straight subclass'''

And usage:

>>> myexception = MyException('my message')
>>> str(myexception)
'my message'

See also this answer:

Proper way to declare custom exceptions in modern Python?


回答 4

据我所知,仅对message属性使用其他名称可以避免与基类发生冲突,从而停止弃用警告:

class MyException(Exception):

def __init__(self, message):
    self.msg = message

def __str__(self):
    return repr(self.msg)

在我看来就像是骇客。

也许有人可以解释为什么即使子类显式定义了message属性,也会发出警告。如果基类不再具有此属性,则应该没有问题。

As far as I can tell, simply using a different name for the message attribute avoids the conflict with the base class, and thus stops the deprecation warning:

class MyException(Exception):

def __init__(self, message):
    self.msg = message

def __str__(self):
    return repr(self.msg)

Seems like a hack to me.

Maybe someone can explain why the warning is issued even when the subclass defines a message attribute explicitly. If the base class no longer has this attribute, there shouldn’t be a problem.


回答 5

继续从geekQ的答案开始,首选的代码替换取决于您需要执行的操作:

### Problem
class MyException(Exception):
    """My documentation"""

try:
    raise MyException('my detailed description')
except MyException as my:
    ### Solution 1, fails in Python 2.x if MyException contains 🔥
    # with UnicodeEncodeError: 'ascii' codec can't encode characters in position 24-25: ordinal not in range(128)
    print(my)  # outputs 'my detailed description'

### Solution 2
# Works in Python 2.x if exception only has ASCII characters,
# should always work in Python 3.x
str(my)

### Solution 3
# Required in Python 2.x if you need to handle non-ASCII characters,
# such as δσφφδσ (as pointed out by jjc) or emoji 🔥 💕 🎁 💯 🌹
# but does not work in Python 3.x
unicode(my)

有时,异常包含多个参数,因此my.args[0]不能保证提供所有相关信息。

例如:

# Python 2.7
try:
    u'\u12345'.encode('utf-8').encode('utf-8')
except UnicodeDecodeError as e:
    print e.args[0]
    print e.args
    print str(e)

打印为输出:

ascii
('ascii', '\xe1\x88\xb45', 0, 1, 'ordinal not in range(128)')
'ascii' codec can't decode byte 0xe1 in position 0: ordinal not in range(128)

但是,这是上下文相关的折衷,因为例如:

# Python 2.7
>>> str(SyntaxError())
'None'
# 'None' compares True which might not be expected

Continuing on from geekQ’s answer, the preferred code replacement depends on what you need to do:

### Problem
class MyException(Exception):
    """My documentation"""

try:
    raise MyException('my detailed description')
except MyException as my:
    ### Solution 1, fails in Python 2.x if MyException contains 🔥
    # with UnicodeEncodeError: 'ascii' codec can't encode characters in position 24-25: ordinal not in range(128)
    print(my)  # outputs 'my detailed description'

### Solution 2
# Works in Python 2.x if exception only has ASCII characters,
# should always work in Python 3.x
str(my)

### Solution 3
# Required in Python 2.x if you need to handle non-ASCII characters,
# such as δσφφδσ (as pointed out by jjc) or emoji 🔥 💕 🎁 💯 🌹
# but does not work in Python 3.x
unicode(my)

Sometimes exceptions have more than one argument, so my.args[0] is not guaranteed to provide all the relevant information.

For instance:

# Python 2.7
try:
    u'\u12345'.encode('utf-8').encode('utf-8')
except UnicodeDecodeError as e:
    print e.args[0]
    print e.args
    print str(e)

Prints as output:

ascii
('ascii', '\xe1\x88\xb45', 0, 1, 'ordinal not in range(128)')
'ascii' codec can't decode byte 0xe1 in position 0: ordinal not in range(128)

However it’s a context sensitive trade off, because for instance:

# Python 2.7
>>> str(SyntaxError())
'None'
# 'None' compares True which might not be expected

回答 6

使用str(myexception)的建议会导致python 2.7中的unicode问题,例如:

str(Exception(u'δσφφδσ'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

:(

unicode(Exception(u'δσφφδσ')) 

可以按预期工作,并且在错误字符串的某些内容包括用户输入的情况下更可取

The advice to use str(myexception) leads to unicode problems in python 2.7, e.g.:

str(Exception(u'δσφφδσ'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

:(

unicode(Exception(u'δσφφδσ')) 

works as expected, and is preferred in cases where some of the content of the error string includes user input


回答 7

pzrq的帖子说使用:

str(e)

这正是我所需要的。

(如果您在unicode环境中,则似乎:

unicode(e)

可以正常工作,并且在非Unicode环境中似乎可以正常工作)

Pzrq说了很多其他好东西,但是由于所有好东西,我几乎都错过了他们的答案。由于我没有50分,因此我无法就他们的答案发表评论,以引起人们对有效的简单解决方案的关注;由于我没有15分,因此我无法投票赞成该答案,但是我可以发表评论(感觉是倒退的,但是哦,好)-我在这里发布-可能会因此而失去积分…

由于我的意思是要引起人们对pzrq答案的关注,请不要着迷并在下面的所有内容中忽略它。这篇文章的前几行是最重要的。

我的故事:

我来到这里的问题是,如果您想从无法控制的类中捕获异常,那么该怎么办?我当然不会继承我的代码使用的所有可能的类,以试图从所有可能的异常中获取消息!

我正在使用:

except Exception as e:
   print '%s (%s)' % (e.message,type(e))

正如我们现在所知道的那样,它给出了警告OP的警告(这将我带到了这里),而这就是pzrq给出的一种解决方法:

except Exception as e:
   print '%s (%s)' % (str(e),type(e))

没有。

我不是在unicode环境中,但是jjc的回答让我感到疑惑,因此我不得不尝试一下。在这种情况下,它将变为:

except Exception as e:
   print '%s (%s)' % (unicode(e),type(e))

令我惊讶的是,它的工作原理与str(e)完全一样-所以现在这就是我正在使用的。

不知道’str(e)/ unicode(e)’是否是’认可的Python方式’,我很可能会发现为什么当我达到3.0时这不好,但是有人希望能够处理不会死的意外异常(*)永远不会消失…

(*)嗯。“意外异常”-我想我口吃了!

pzrq’s post says to use:

str(e)

This was exactly what I needed.

(If you are in a unicode environment, it appears that:

unicode(e)

will work, and it appears to work fine in a non-unicode environment)

Pzrq said a lot of other good stuff, but I almost missed their answer due to all the good stuff. Since I don’t have 50 points I cannot comment on their answer to attempt to draw attention to the simple solution that works, and since I don’t have 15 I cannot vote that answer up, but I can post (feels backward, but oh well) – so here I am posting – probably lose points for that…

Since my point is to draw attention to pzrq’s answer, please don’t glaze over and miss it in all the below. the first few lines of this post are the most important.

My story:

The problem I came here for was if you want to catch an exception from a class that you have no control over – what then??? I’m certainly not going to subclass all possible classes my code uses in an attempt to be able to get a message out of all possible exceptions!

I was using:

except Exception as e:
   print '%s (%s)' % (e.message,type(e))

which, as we all now know, gives the warning OP asked about (which brought me here), and this, which pzrq gives as a way to do it:

except Exception as e:
   print '%s (%s)' % (str(e),type(e))

did not.

I’m not in a unicode environment, but jjc’s answer made me wonder, so I had to try it. In this context this becomes:

except Exception as e:
   print '%s (%s)' % (unicode(e),type(e))

which, to my surprise, worked exactly like str(e) – so now that’s what I’m using.

Don’t know if ‘str(e)/unicode(e)’ is the ‘approved Python way’, and I’ll probably find out why that’s not good when I get to 3.0, but one hopes that the ability to handle an unexpected exception (*) without dying and still get some information from it won’t ever go away…

(*) Hmm. “unexpected exception” – I think I just stuttered!


在Python中发出警告而不会中断程序

问题:在Python中发出警告而不会中断程序

我正在尝试在Python中引发警告,而不会导致程序崩溃/停止/中断。

我使用以下简单函数检查用户是否向其传递了非零数字。如果是这样,程序应警告它们,但按正常方式继续。它应该像下面的代码一样工作,但是应该使用class Warning()Error()或者Exception()代替手动打印警告。

def is_zero(i):
   if i != 0:
     print "OK"
   else:
     print "WARNING: the input is 0!"
   return i

如果我使用下面的代码并将0传递给该函数,则程序将崩溃,并且永远不会返回该值。相反,我希望程序继续正常运行,只是通知用户他已将0传递给该函数。

def is_zero(i):
   if i != 0:
     print "OK"
   else:
     raise Warning("the input is 0!")
   return i

我希望能够测试通过unittest对其进行了警告的警告。如果仅将消息打印出来,则无法在unittest中使用assertRaises对其进行测试。

I am trying to raise a Warning in Python without making the program crash / stop / interrupt.

I use the following simple function to check if the user passed a non-zero number to it. If so, the program should warn them, but continue as per normal. It should work like the code below, but should use class Warning(), Error() or Exception() instead of printing the warning out manually.

def is_zero(i):
   if i != 0:
     print "OK"
   else:
     print "WARNING: the input is 0!"
   return i

If I use the code below and pass 0 to the function, the program crashes and the value is never returned. Instead, I want the program to continue normally and just inform the user that he passed 0 to the function.

def is_zero(i):
   if i != 0:
     print "OK"
   else:
     raise Warning("the input is 0!")
   return i

I want to be able to test that a warning has been thrown testing it by unittest. If I simply print the message out, I am not able to test it with assertRaises in unittest.


回答 0

您不应该raise发出警告,应该使用warnings模块。通过提高它,您正在生成错误,而不是警告。

You shouldn’t raise the warning, you should be using warnings module. By raising it you’re generating error, rather than warning.


回答 1

import warnings
warnings.warn("Warning...........Message")

请参阅python文档:此处

import warnings
warnings.warn("Warning...........Message")

See the python documentation: here


回答 2

默认情况下,与异常不同,警告不会中断。

在之后import warnings,可以在生成警告时指定警告类。如果未指定,则UserWarning默认为字面值。

>>> warnings.warn('This is a default warning.')
<string>:1: UserWarning: This is a default warning.

要简单地使用预先存在的类代替,例如DeprecationWarning

>>> warnings.warn('This is a particular warning.', DeprecationWarning)
<string>:1: DeprecationWarning: This is a particular warning.

创建自定义警告类类似于创建自定义异常类:

>>> class MyCustomWarning(UserWarning):
...     pass
... 
... warnings.warn('This is my custom warning.', MyCustomWarning)

<string>:1: MyCustomWarning: This is my custom warning.

要进行测试,请考虑assertWarnsassertWarnsRegex


作为替代方法,尤其是对于独立应用程序,请考虑使用该logging模块。它可以记录级别为debuginfowarningerror等的消息。缺省情况下,级别为warning或更高级别的日志消息被打印到stderr。

By default, unlike an exception, a warning doesn’t interrupt.

After import warnings, it is possible to specify a Warnings class when generating a warning. If one is not specified, it is literally UserWarning by default.

>>> warnings.warn('This is a default warning.')
<string>:1: UserWarning: This is a default warning.

To simply use a preexisting class instead, e.g. DeprecationWarning:

>>> warnings.warn('This is a particular warning.', DeprecationWarning)
<string>:1: DeprecationWarning: This is a particular warning.

Creating a custom warning class is similar to creating a custom exception class:

>>> class MyCustomWarning(UserWarning):
...     pass
... 
... warnings.warn('This is my custom warning.', MyCustomWarning)

<string>:1: MyCustomWarning: This is my custom warning.

For testing, consider assertWarns or assertWarnsRegex.


As an alternative, especially for standalone applications, consider the logging module. It can log messages having a level of debug, info, warning, error, etc. Log messages having a level of warning or higher are by default printed to stderr.