rich
rich copied to clipboard
[BUG] `AttributeError` using `Console.capture` when run with `pythonw`
Describe the bug
This code, when run using pythonw
:
table = Table(box=DOUBLE_EDGE)
printer = Console(force_terminal=False)
with printer.capture() as capture:
printer.print(table)
fails with this stack trace:
Traceback (most recent call last):
File "<MY CODE>", line ...
with printer.capture() as capture:
File "rich\console.py", line 336, in __exit__
self._result = self._console.end_capture()
File "rich\console.py", line 852, in end_capture
self._exit_buffer()
File "rich\console.py", line 796, in _exit_buffer
self._check_buffer()
File "rich\console.py", line 1979, in _check_buffer
self.file.fileno() in _STD_STREAMS_OUTPUT
AttributeError: 'NoneType' object has no attribute 'fileno'
My guess is that pythonw
causes Console
to get some attributes that are usually not null, but in this case, are.
I can probably immediately work around this by instantiating the Console
like so:
table = Table(box=DOUBLE_EDGE)
capture = StringIO()
printer = Console(file=capture)
printer.print(table)
but Rich could probably cater for the Console
instance having no STDOUT
in some cases (such as above).
Platform
- Windows 10
- Python v3.10.0
I've avoided including details generated by python -m rich.diagnose
because more diagnostic information would be provided using pythonw ...
—but of course, since it disables STDOUT
, there's no obvious way to grab that info for you. 🤣
I don't have a Windows box handy. What happens if you run a script with pythonw that does print("hello world")
?
Hi Will. There is no output. I somewhat expected this as I believe pythonw
nullifies its STDOUT
handle.
And is that the behaviour you would expect from Rich? To silently ignore the error?
Could you paste the full traceback?
To frame my expected behaviour in a slightly alternative manner:
- When I instantiate a
Console
purely for the purpose ofcapture
-ing its output to a variable, I would expect Rich not to fail whenSTDOUT
is redirected to null (which is whatpythonw
appears to do)—particularly since I would expectSTDOUT
not to be getting used as part of that capturing operation.
Directly answering your question though: yes, I believe there should be no error.
The traceback above is complete except for my calling code, which is no different from the example given.
I see. So the error occurs regardless of if you are printing to stdout or not.
You didn't include the full traceback btw. The error message would help.
Yes, the error is occurring despite no intent to print to STDOUT
.
Sorry, you're absolutely right: I did fail to paste the actual error message. I've edited that in to the earlier message now, but here's the entire thing from my logging anyway for good measure, in case any of the other bits do actually help:
2022-07-17 15:35:48,860 ERROR [ClientWorkerThread/ductus.client.gui.eventloopthread.ThreadedEventLoop] exception in asyncio event loop
Traceback (most recent call last):
File "C:\Users\kate\AppData\Local\Programs\Ductus\ductus\client\gui\eventloopthread.py", line 54, in __thread_main
asyncio.run(self.__run_coro_cancellable(), debug=True)
File "C:\Program Files\Python310\lib\asyncio\runners.py", line 44, in run
return loop.run_until_complete(main)
File "C:\Program Files\Python310\lib\asyncio\base_events.py", line 641, in run_until_complete
return future.result()
File "C:\Users\kate\AppData\Local\Programs\Ductus\ductus\client\gui\eventloopthread.py", line 76, in __run_coro_cancellable
await await_task
File "C:\Users\kate\AppData\Local\Programs\Ductus\ductus\client\gui\worker.py", line 85, in __loop_tasks
await asyncio.gather(
File "C:\Users\kate\AppData\Local\Programs\Ductus\ductus\client\gui\worker.py", line 128, in __watch_for_auth_requests
await self.client.google_auth()
File "C:\Users\kate\AppData\Local\Programs\Ductus\ductus\client\client.py", line 119, in google_auth
creds = await asyncio.to_thread(google_auth_flow)
File "C:\Program Files\Python310\lib\asyncio\threads.py", line 25, in to_thread
return await loop.run_in_executor(None, func_call)
File "C:\Program Files\Python310\lib\concurrent\futures\thread.py", line 52, in run
result = self.fn(*self.args, **self.kwargs)
File "C:\Users\kate\AppData\Local\Programs\Ductus\ductus\client\googleauth.py", line 43, in google_auth_flow
success_message=_browser_finish_message(),
File "C:\Users\kate\AppData\Local\Programs\Ductus\ductus\client\googleauth.py", line 75, in _browser_finish_message
with printer.capture() as capture:
File "...\rich\console.py", line 336, in __exit__
self._result = self._console.end_capture()
File "...\rich\console.py", line 852, in end_capture
self._exit_buffer()
File "...\rich\console.py", line 796, in _exit_buffer
self._check_buffer()
File "...\rich\console.py", line 1979, in _check_buffer
self.file.fileno() in _STD_STREAMS_OUTPUT
AttributeError: 'NoneType' object has no attribute 'fileno'
In particular, Console
is treating its file
attribute as if it will never be None. When run under pythonw
however, that's exactly what it's defaulting to. Even so though, in my example, there's no obvious reason for self.file
to be getting called.