更新:这似乎特定于PyQt5的插槽/信号流(在Python 3.7.0中运行的PyQt 5.11)调用的函数中未捕获的异常。请参阅此问题文本的更深处的更新。
总体目标是在Windows 10计算机上准备python程序的stdout和stderr进行筛选并查找到相同的文件;我宁愿在操作系统级别进行此重定向(即,调用python的shell-在这种情况下为Windows);从python程序中干净地完成它并没有成功。
查看下面断开的stderr重定向行。
radiolog.py是一个大型python程序,在这里不应该涉及;在其中触发一个异常,以测试此日志记录工作流程。 radiolog_log.ps1是围绕radiolog.py的Powershell包装:
python -u radiolog.py 2>&1 | % ToString | Tee-Object log.txt
从powershell终端内部运行radiolog_log.ps1:
PS C:\Users\caver\Documents\GitHub\radiolog> .\radiolog_log.ps1
6319:Operating system is Windows.
6319:PowerShell.exe is in the path.
... (omitting a bunch of irrelevant radiolog transcript output)
6329:Accepted2
Traceback (most recent call last):
File "
radiolog.py", line 3796, in keyPress
Eve
n
t
sel
f.accept
(
)
File "
r
adio
l
og.
py"
,
line 3
9
13, in accept
rprint(1/0)
ZeroD
ivi
sionError: division by zero
PS C:\Users\caver\Documents\GitHub\radiolog>
在控制台的宽度上有一些关于Powershell stderr重定向分割线的好帖子和答案...但是,这似乎是每隔几个字符就分割线,而且每次都不一样。< / p>
%ToString摆脱了Powershell异常处理的冗余层(在这种情况下);没有| %ToString看起来像这样(以'python:Traceback'开头的所有内容都是红色文本):
6851:Accepted2
python : Traceback (most recent call last):
At line:1 char:1
+ python -u radiolog.py 2>&1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Traceback (most recent call last)::String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
F
ile "radiolog.py", line 3796, in keyPressEvent
self.accept()
File "ra
diol
o
g.p
y",
lin
e 39
13, in accept
rp
r
int(1/0)
Ze
r
oDivisio
n
Error:
div
i
sion by zero
PS C:\Users\caver\Documents\GitHub\radiolog>
另一种尝试是从powershell终端运行.bat文件,然后在powershell中进行发球。
radiolog_log.bat:
python -u radiolog.py 2>&1
在powershell终端中:
PS C:\Users\caver\Documents\GitHub\radiolog> .\radiolog_log.bat | % ToString | Tee-Object log.dat
这将导致以下终端显示-一切正常:
C:\Users\caver\Documents\GitHub\radiolog>python -u radiolog.py 2>&1
8314:Operating system is Windows.
8314:PowerShell.exe is in the path.
...
8327:Accepted2
Traceback (most recent call last):
File "radiolog.py", line 3796, in keyPressEvent
self.accept()
File "radiolog.py", line 3913, in accept
rprint(1/0)
ZeroDivisionError: division by zero
PS C:\Users\caver\Documents\GitHub\radiolog>
但是log.dat是用unicode编写的,即,作为纯文本文件不是人类可读的。 Microsoft在Tee文档here中说了很多-不知道为什么...?找到了一个有关即时将其转换为ascii的问题,但是,看起来好像您不能拥有相同的实时文件和终端输出,因此一开始就否定了tee的大部分理由。
关于如何使一切像在Linux中一样好玩的任何想法吗?
更新:PyQt5插槽/信号流似乎正在触发此行为。示例代码,从原始radiolog.py大量缩减:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
self.pushButton = QPushButton(Dialog)
self.pushButton.setObjectName("pushButton")
self.pushButton.setText(" Add Entry ")
self.horizontalLayout = QHBoxLayout(Dialog)
self.horizontalLayout.addWidget(self.pushButton)
class MyWindow(QDialog,Ui_Dialog):
def __init__(self,parent):
QDialog.__init__(self)
self.ui=Ui_Dialog()
self.ui.setupUi(self)
# click the button to see that stderr inserts line breaks every few
# characters (different on each occurrance) when openNewEntry is called
# from the slot/signal flow:
self.ui.pushButton.clicked.connect(self.openNewEntry)
# uncomment the following line to see that stderr is line-buffered when
# openNewEntry is called from code:
# self.openNewEntry()
def openNewEntry(self):
print(1/0)
def main():
app = QApplication(sys.argv)
w = MyWindow(app)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
在powershell终端中,按照对此问题的最早答案定义了STD-Handler(不需要观察虚线,但是,它确实可以在没有powershell-exception标头的情况下提供更清晰的输出,并且可以使-文本日志文件),然后执行以下操作:
python -u r.py *>&1 | STD-Handler log.txt
单击GUI中的按钮,您将获得以下信息:
[636765635572699130]: Traceback (most recent call last):
[636765635572808866]:
[636765635572918571]: File "r.py", line 33,
[636765635573028268]:
[636765635573118026]: in open
[636765635573207784]: New
[636765635573307659]: E
[636765635573407558]: ntry
print(1/0)
ZeroDivisionError: division by z
[636765635573506983]: ero
现在按照注释取消注释python代码中的行,以便从代码而不是从用户交互中调用异常;在powershell终端上运行同一行,您会得到以下提示:
[636765639350787977]: Traceback (most recent call last):
[636765639350877842]: File "r.py", line 42, in <module>
[636765639350997857]:
[636765639351077838]: main()
File "r.py", line 37, in main
[636765639351187538]:
[636765639351282570]: w = MyWindow(app)
File "r.py", line 30, in __init__
[636765639351372787]:
[636765639351467301]: self.openNewEntry()
File "r.py", line 33, in openNewEntry
[636765639351554768]:
[636765639351660016]: print(1/0)
ZeroDivisionError: division by zero
所以,这可能给PyQt社区带来了一个新问题,我将在不久的将来将其汇总起来并在此处进行交叉引用-尽管对该线程的帮助仍然非常棒!
答案 0 :(得分:0)
我试图用一些虚拟脚本复制此问题,但是在两种情况下(ps和bat)都得到了正确的文件和终端输出。我想您可以只使用bat脚本,然后最后修复文件编码。如果您确实需要实时更新文件,则可以采用以下解决方法。基本上,您可以创建一些自定义“ Tee对象”并处理标准输出。 $ _是一个来自管道(stdout)的对象,即使它损坏了,您也可以修复编码或删除Process块内的换行符
function STD-Handler
{ param ($outFile)
Begin { $null > $outFile }
Process {
$line = "[" + (get-date).Ticks + "]: " + $_
Write-Host $line
$line >> $outFile
}
End { <# can do smth with the file at the end#>}
}
用法:
python -u C:\ stack \ stdout.py *>&1 | STD处理程序“ C:\ temp \ log.txt”
答案 1 :(得分:0)
部分宾果游戏-其他一些问题涉及PyQt5处理未捕获的异常的方式。不确定PyQt的stderr处理与powershell的stderr处理之间的相互作用,但是,在python中重写sys.excepthook使其具有“旧”行为可以解决问题。这是新的整个r.py python代码-更新后的问题代码的唯一变化是'if name '子句中的'defexception_hook'和'sys.excepthook = except_hook':
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
self.pushButton = QPushButton(Dialog)
self.pushButton.setObjectName("pushButton")
self.pushButton.setText(" Add Entry ")
self.horizontalLayout = QHBoxLayout(Dialog)
self.horizontalLayout.addWidget(self.pushButton)
class MyWindow(QDialog,Ui_Dialog):
def __init__(self,parent):
QDialog.__init__(self)
self.ui=Ui_Dialog()
self.ui.setupUi(self)
# click the button to see that stderr inserts line breaks every few
# characters (different on each occurrance) when openNewEntry is called
# from the slot/signal flow:
self.ui.pushButton.clicked.connect(self.openNewEntry)
# uncomment the following line to see that stderr is line-buffered when
# openNewEntry is called from code:
# self.openNewEntry()
def openNewEntry(self):
print(1/0)
def except_hook(cls, exception, traceback):
sys.__excepthook__(cls, exception, traceback)
exit(-1)
def main():
app = QApplication(sys.argv)
w = MyWindow(app)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
sys.excepthook = except_hook
main()
现在,单击按钮将产生以下输出:
[636766143015640817]: Traceback (most recent call last):
[636766143015760489]: File "r.py", line 33, in openNewEntry
[636766143015911442]:
[636766143016031102]: print(1/0)
ZeroDivisionError: division by zero
有时候(不能重复),它在标点符号处有点破裂,但是,这样更易于管理:
[636766144719906619]: Traceback (most recent call last):
[636766144720006506]: File "r.py", line 47, in <module>
[636766144720096055]:
[636766144720195662]: main()
File "r.py", line 41, in main
[636766144720275744]:
[636766144720375114]: w = MyWindow(app)
[636766144720469728]: File "r.py", line 30, in __init__
[636766144720565688]:
[636766144720657318]: self.openNewEntry()
File "r.py", line 33, in openNewEntry
[636766144720757052]:
[636766144720852021]: print(1/0)
ZeroDivisionError
[636766144720946788]: :
[636766144721046802]: division by zero
[636766144721132731]:
这个表现更好的输出也发生在整个程序中(radiolog.py)。
因此,如果有人能进一步阐明为什么仍然发生间歇性局部断裂,那将是很好的,但是,我认为这是一个可行的解决方案。谢谢