[BUG] Console appears not to be a terminal in background thread
See original GitHub issueIโve set up a widget with a spinner as renderable. I instantiate using Live(spinner_widget)
and then call start()
on the Live
instance. However, the renderable doesnโt update, even though the thread is started and calls refresh()
on the Live
instance at intervals. It can be updated through e.g. an action, as per the code sample given below.
It appears that the proximate reason is that in this line https://github.com/Textualize/rich/blob/5f4e93efb159af99ed51f1fbfd8b793bb36448d9/rich/live.py#L238
self.console.is_terminal
is returning False
. That seems to be because in this line
https://github.com/Textualize/rich/blob/5f4e93efb159af99ed51f1fbfd8b793bb36448d9/rich/console.py#L954
isatty
is None
when called in the live refresh thread.
In an attempt to understand whatโs happening, I inserted some logging statements. Just after the start()
call, I see this in the log:
__main__ 22 MainThread <_RefreshThread(Thread-3, started daemon 140431866193664)>
rich.console 956 MainThread isatty: <bound method _WriterThread.isatty of <_WriterThread(Thread-1, started daemon 140431953118976)>>: True
rich.console 956 MainThread isatty: <bound method _WriterThread.isatty of <_WriterThread(Thread-1, started daemon 140431953118976)>>: True
rich.console 956 MainThread isatty: <bound method _WriterThread.isatty of <_WriterThread(Thread-1, started daemon 140431953118976)>>: True
... (more duplicate lines elided)
rich.console 956 MainThread isatty: <bound method _WriterThread.isatty of <_WriterThread(Thread-1, started daemon 140431953118976)>>: True
rich.console 956 Thread-3 isatty: None: False
rich.console 956 Thread-3 isatty: None: False
rich.live 223 Thread-3 Console: <console width=220 None> is terminal? False
Since in the thread the console appears to be what youโd expect, itโs not immediately clear why isatty
would be None
. This might be a misunderstanding on my part rather than an actual bug, but I think I followed the documentation to set things up and so expected it to work.
Example Code
The code I used:
import logging
import time
from rich.live import Live
from rich.spinner import Spinner
from textual.app import App, ComposeResult
from textual.widgets import Static, Footer
logger = logging.getLogger(__name__)
class SpinnerStatic(Static):
def __init__(self, spinner, **kwargs):
super().__init__(**kwargs)
self.spinner = spinner
self.live = Live(spinner)
def on_mount(self):
self.update(self.spinner)
self.live.start()
time.sleep(0.1)
logger.debug(self.live._refresh_thread)
class TestApp(App):
BINDINGS = [("r", "refresh", "Refresh")]
def compose(self) -> ComposeResult:
spinner = Spinner('moon', speed=1.0)
w = SpinnerStatic(spinner, id='spinner')
self.spinner = w
yield w
yield Footer()
def action_refresh(self):
self.spinner.refresh()
logging.basicConfig(level=logging.DEBUG,
filename='spinner.log',
filemode='w',
format='%(name)-12s %(lineno)4d %(threadName)-10s %(message)s')
app = TestApp()
app.run()
Platform
Click to expand
Linux (Ubuntu 64-bit), standard terminal
I may ask you to copy and paste the output of the following commands. It may save some time if you do it now.
If youโre using Rich in a terminal:
$ python -m rich.diagnose
โญโโโโโโโโโโโโโโโโโโโโโโโโโ <class 'rich.console.Console'> โโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ A high level console interface. โ
โ โ
โ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ โ
โ โ <console width=220 ColorSystem.TRUECOLOR> โ โ
โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ โ
โ โ
โ color_system = 'truecolor' โ
โ encoding = 'utf-8' โ
โ file = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'> โ
โ height = 24 โ
โ is_alt_screen = False โ
โ is_dumb_terminal = False โ
โ is_interactive = True โ
โ is_jupyter = False โ
โ is_terminal = True โ
โ legacy_windows = False โ
โ no_color = False โ
โ options = ConsoleOptions( โ
โ size=ConsoleDimensions(width=220, height=24), โ
โ legacy_windows=False, โ
โ min_width=1, โ
โ max_width=220, โ
โ is_terminal=True, โ
โ encoding='utf-8', โ
โ max_height=24, โ
โ justify=None, โ
โ overflow=None, โ
โ no_wrap=False, โ
โ highlight=None, โ
โ markup=None, โ
โ height=None โ
โ ) โ
โ quiet = False โ
โ record = False โ
โ safe_box = True โ
โ size = ConsoleDimensions(width=220, height=24) โ
โ soft_wrap = False โ
โ stderr = False โ
โ style = None โ
โ tab_size = 8 โ
โ width = 220 โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
โญโโโ <class 'rich._windows.WindowsConsoleFeatures'> โโโโโฎ
โ Windows features available. โ
โ โ
โ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ โ
โ โ WindowsConsoleFeatures(vt=False, truecolor=False) โ โ
โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ โ
โ โ
โ truecolor = False โ
โ vt = False โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
โญโโโโโโ Environment Variables โโโโโโโโฎ
โ { โ
โ 'TERM': 'xterm-256color', โ
โ 'COLORTERM': 'truecolor', โ
โ 'CLICOLOR': None, โ
โ 'NO_COLOR': None, โ
โ 'TERM_PROGRAM': None, โ
โ 'COLUMNS': None, โ
โ 'LINES': None, โ
โ 'JUPYTER_COLUMNS': None, โ
โ 'JUPYTER_LINES': None, โ
โ 'JPY_PARENT_PID': None, โ
โ 'VSCODE_VERBOSE_LOGGING': None โ
โ } โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
platform="Linux"
$ pip freeze | grep rich
rich==12.6.0
Issue Analytics
- State:
- Created 10 months ago
- Comments:12 (2 by maintainers)
I think we will have builtin widgets for spinner / progress eventually. So probably not worth adding those to docs right now.
However, it would make an excellent blog post!
Textual uses asyncio which is generally single threaded. So your thread approach is unlikely to work. Fortunately it is generally easier to implement such things with timers, as Rodrigoโs examples shows!