7

这是说明问题的代码:

from PyQt4 import QtGui
app = QtGui.QApplication([])
dialog = QtGui.QDialog()
button = QtGui.QPushButton('I crash')
layout = QtGui.QHBoxLayout()
layout.addWidget(button)
dialog.setLayout(layout)
def crash(): raise Exception('Crash!')
button.clicked.connect(crash)
button.click()
print 'I should not happen'

当我运行它时,PyQt4 会为我处理错误。我的控制台显示带有“崩溃!”的堆栈跟踪 等等,我看到“我不应该发生”。

这没有用,因为我已经收到了一个包含许多处理程序的大型应用程序,我需要将它们的所有错误都强加到我的脸上(以及我的 - 嗯 - 自动化测试)。每次我运行时,错误都会从我的网络中逃脱,它们需要在每个处理程序中使用过多且无用的 try:except 块,才能将它们全部捕获。

换句话说,我希望好的代码非常好,而坏的代码非常糟糕。没有粉饰。

抱歉,如果已经问过这个问题,但是当我进行电子搜索时,我自然会遇到成千上万的新手询问基本的错误处理问题(或者,更糟糕的是,我让新手询问如何关闭他们任性的异常!;)

如何覆盖 PyQt4 的默认错误处理,以便我自己传播或记录错误?也请不要回答 sys.excepthook - 它会捕获 PyQt4没有捕获的错误。

4

2 回答 2

3

这不是答案,你这个愚蠢的网站。停止强迫我们适应理想化线程模板的先入为主的概念。

答案是使用我的测试框架 setUp() 来挂钩异常处理程序:

def setUp(self):
    self.no_exceptions = True

    def testExceptionHook(type, value, tback):
        self.no_exceptions = False
        sys.__excepthook__(type, value, tback)

    sys.excepthook = testExceptionHook

如果说“self.no_exceptions = False”,我宁愿简单地说 self.fail('')。但是,由于 Python 的单元测试库坚持抛出异常只是为了注册测试失败,而且由于 PyQt 坚持捕获所有异常,所以我们陷入了死锁。

为了适应 unittest.TestCase 对理想化测试用例的愚蠢先入为主的概念,我必须改为设置一个变量,然后在拆解中检测它:

def tearDown(self):
    self.assertTrue(self.no_exceptions)

这仍然不理想,但至少它会迫使我花更多时间关注错误,而不是花时间在技术网站上抱怨它们。

根本问题:如何关闭 PyQt 的神奇错误处理程序?- 仍然没有答案...

于 2013-01-24T18:02:08.273 回答
2

我认为答案是,这不是 的“特征” PyQt,而是让信号/插槽工作的设计所固有的结果(请记住,信号/插槽通信也通过 c++ 层)。

这很难看,但是会解决您的问题

from PyQt4 import QtGui
import time
app = QtGui.QApplication([])

class exception_munger(object):
    def __init__(self):
        self.flag = True
        self.txt = ''
        self.type = None

    def indicate_fail(self,etype=None, txt=None):
        self.flag = False
        if txt is not None:
            self.txt = txt
        self.type = etype
    def reset(self):

        tmp_txt = self.txt
        tmp_type = self.type
        tmp_flag = self.flag
        self.flag = True
        self.txt = ''
        self.type = None
        return tmp_flag, tmp_type, tmp_txt

class e_manager():
    def __init__(self):
        self.old_hook = None

    def __enter__(self):
        em = exception_munger()
        def my_hook(type, value, tback):
            em.indicate_fail(type, value)
            sys.__excepthook__(type, value, tback) 
        self.old_hook = sys.excepthook
        sys.excepthook = my_hook
        self.em = em
        return self

    def __exit__(self,*args,**kwargs):
        sys.excepthook = self.old_hook

def mang_fac():
    return e_manager()

def assert_dec(original_fun):

    def new_fun(*args,**kwargs):
        with mang_fac() as mf:
            res = original_fun(*args, **kwargs)
            flag, etype, txt = mf.em.reset()
            if not flag:
                raise etype(txt)
            return res
    return new_fun


@assert_dec
def my_test_fun():
    dialog = QtGui.QDialog()
    button = QtGui.QPushButton('I crash')
    layout = QtGui.QHBoxLayout()
    layout.addWidget(button)
    dialog.setLayout(layout)
    def crash(): 
        time.sleep(1)
        raise Exception('Crash!')
    button.clicked.connect(crash)


    button.click()

my_test_fun()
print 'should not happen'

这不会打印“不应该发生”,并为您提供一些可以通过自动化测试捕获的东西(使用正确的异常类型)。

In [11]: Traceback (most recent call last):
  File "/tmp/ipython2-3426rwB.py", line 68, in crash
Exception: Crash!
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-11-6ef4090ab3de> in <module>()
----> 1 execfile(r'/tmp/ipython2-3426rwB.py') # PYTHON-MODE

/tmp/ipython2-3426rwB.py in <module>()

/tmp/ipython2-3426rwB.py in new_fun(*args, **kwargs)

Exception: Crash!

In [12]: 

堆栈跟踪被顶起,但您仍然可以读取打印出来的第一个。

于 2013-01-24T05:26:40.000 回答