ОБНОВЛЕНИЕ: это, кажется, характерно для необработанных исключений внутри функции, вызываемой потоком слотов / сигналов PyQt5 (PyQt 5.11 работает в Python 3.7.0). См. ОБНОВЛЕНИЕ ниже этого текста вопроса.
Общая цель состоит в том, чтобы выводить stdout и stderr программы на python на экран и в один и тот же файл на компьютере с Windows 10; Я бы предпочел сделать это перенаправление на уровне операционной системы (то есть оболочка, которая вызывает Python - в данном случае Windows); сделать это чисто изнутри программы на python не получилось.
Проверьте сломанные линии перенаправления stderr ниже.
radiolog.py - большая программа на Python, которая не должна быть здесь уместной; внутри него я запускаю исключение, чтобы проверить этот рабочий процесс регистрации. radiolog_log.ps1 - это оболочка powershell для radiolog.py:
python -u radiolog.py 2>&1 | % ToString | Tee-Object log.txt
Запуск radiolog_log.ps1 из терминала powershell:
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 на ширину консоли ... однако, похоже, что это разделение строк каждые несколько символов, и каждый раз это не одно и то же.
| % 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>
Еще одна попытка - запустить файл .bat из терминала powershell, а затем выполнить его в 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 написан в юникоде, т.е. не читается человеком как простой текстовый файл. Microsoft так много говорит в документации Tee здесь - не знаю, почему ...? Нашел вопрос о том, как преобразовать его в ascii на лету, но, похоже, вы не можете иметь один и тот же файл и вывод в реальном времени, что в первую очередь сводит на нет значительную часть рассуждений о тройнике.
Есть какие-нибудь идеи о том, как заставить все играть просто так, как в 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-обработчика в соответствии с самым ранним ответом на этот вопрос (не требуется наблюдать прерывистые линии, но он делает более чистый вывод без заголовка-исключения powershell, а также делает простой текстовый файл журнала), затем выполните следующее:
python -u r.py *>&1 | STD-Handler log.txt
Нажмите кнопку в графическом интерфейсе, и вы получите это:
[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, который я скоро соберу и приведу здесь перекрестную ссылку - хотя помощь по этой теме все равно будет отличной!