mirror of https://github.com/Textualize/rich.git
fix for race condition
This commit is contained in:
parent
0758f2eff0
commit
23497ed230
|
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [10.0.1] - 2021-03-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed race condition that duplicated lines in progress
|
||||
|
||||
## [10.0.0] - 2021-03-27
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -44,7 +44,7 @@ The Text class has a number of parameters you can set on the constructor to modi
|
|||
- ``no_wrap`` prevents wrapping if the text is longer then the available width.
|
||||
- ``tab_size`` Sets the number of characters in a tab.
|
||||
|
||||
A Text instance may be used in place of a plain string virtually everywhere in the Rich API, which gives you a lot of control in how text renders within other Rich renderables. For instance, the following example right aligns text within a :class:`rich.panel.Panel`::
|
||||
A Text instance may be used in place of a plain string virtually everywhere in the Rich API, which gives you a lot of control in how text renders within other Rich renderables. For instance, the following example right aligns text within a :class:`~rich.panel.Panel`::
|
||||
|
||||
from rich import print
|
||||
from rich.panel import Panel
|
||||
|
|
|
@ -1159,33 +1159,34 @@ class Console:
|
|||
Returns:
|
||||
List[List[Segment]]: A list of lines, where a line is a list of Segment objects.
|
||||
"""
|
||||
render_options = options or self.options
|
||||
_rendered = self.render(renderable, render_options)
|
||||
if style:
|
||||
_rendered = Segment.apply_style(_rendered, style)
|
||||
lines = list(
|
||||
islice(
|
||||
Segment.split_and_crop_lines(
|
||||
_rendered,
|
||||
render_options.max_width,
|
||||
include_new_lines=new_lines,
|
||||
pad=pad,
|
||||
),
|
||||
None,
|
||||
render_options.height,
|
||||
with self._lock:
|
||||
render_options = options or self.options
|
||||
_rendered = self.render(renderable, render_options)
|
||||
if style:
|
||||
_rendered = Segment.apply_style(_rendered, style)
|
||||
lines = list(
|
||||
islice(
|
||||
Segment.split_and_crop_lines(
|
||||
_rendered,
|
||||
render_options.max_width,
|
||||
include_new_lines=new_lines,
|
||||
pad=pad,
|
||||
),
|
||||
None,
|
||||
render_options.height,
|
||||
)
|
||||
)
|
||||
)
|
||||
if render_options.height is not None:
|
||||
extra_lines = render_options.height - len(lines)
|
||||
if extra_lines > 0:
|
||||
pad_line = [
|
||||
[Segment(" " * render_options.max_width, style), Segment("\n")]
|
||||
if new_lines
|
||||
else [Segment(" " * render_options.max_width, style)]
|
||||
]
|
||||
lines.extend(pad_line * extra_lines)
|
||||
if render_options.height is not None:
|
||||
extra_lines = render_options.height - len(lines)
|
||||
if extra_lines > 0:
|
||||
pad_line = [
|
||||
[Segment(" " * render_options.max_width, style), Segment("\n")]
|
||||
if new_lines
|
||||
else [Segment(" " * render_options.max_width, style)]
|
||||
]
|
||||
lines.extend(pad_line * extra_lines)
|
||||
|
||||
return lines
|
||||
return lines
|
||||
|
||||
def render_str(
|
||||
self,
|
||||
|
|
|
@ -153,7 +153,8 @@ class Control:
|
|||
def __rich_console__(
|
||||
self, console: "Console", options: "ConsoleOptions"
|
||||
) -> "RenderResult":
|
||||
yield self.segment
|
||||
if self.segment.text:
|
||||
yield self.segment
|
||||
|
||||
|
||||
def strip_control_codes(text: str, _translate_table=_CONTROL_TRANSLATE) -> str:
|
||||
|
|
42
rich/live.py
42
rich/live.py
|
@ -86,7 +86,11 @@ class Live(JupyterMixin, RenderHook):
|
|||
self._live_render = LiveRender(
|
||||
self.get_renderable(), vertical_overflow=vertical_overflow
|
||||
)
|
||||
# cant store just clear_control as the live_render shape is lazily computed on render
|
||||
|
||||
@property
|
||||
def is_started(self) -> bool:
|
||||
"""Check if live display has been started."""
|
||||
return self._started
|
||||
|
||||
def get_renderable(self) -> RenderableType:
|
||||
renderable = (
|
||||
|
@ -205,17 +209,17 @@ class Live(JupyterMixin, RenderHook):
|
|||
|
||||
def refresh(self) -> None:
|
||||
"""Update the display of the Live Render."""
|
||||
self._live_render.set_renderable(self.renderable)
|
||||
if self.console.is_jupyter: # pragma: no cover
|
||||
try:
|
||||
from IPython.display import display
|
||||
from ipywidgets import Output
|
||||
except ImportError:
|
||||
import warnings
|
||||
with self._lock:
|
||||
self._live_render.set_renderable(self.renderable)
|
||||
if self.console.is_jupyter: # pragma: no cover
|
||||
try:
|
||||
from IPython.display import display
|
||||
from ipywidgets import Output
|
||||
except ImportError:
|
||||
import warnings
|
||||
|
||||
warnings.warn('install "ipywidgets" for Jupyter support')
|
||||
else:
|
||||
with self._lock:
|
||||
warnings.warn('install "ipywidgets" for Jupyter support')
|
||||
else:
|
||||
if self.ipy_widget is None:
|
||||
self.ipy_widget = Output()
|
||||
display(self.ipy_widget)
|
||||
|
@ -223,14 +227,14 @@ class Live(JupyterMixin, RenderHook):
|
|||
with self.ipy_widget:
|
||||
self.ipy_widget.clear_output(wait=True)
|
||||
self.console.print(self._live_render.renderable)
|
||||
elif self.console.is_terminal and not self.console.is_dumb_terminal:
|
||||
with self._lock, self.console:
|
||||
self.console.print(Control())
|
||||
elif (
|
||||
not self._started and not self.transient
|
||||
): # if it is finished allow files or dumb-terminals to see final result
|
||||
with self.console:
|
||||
self.console.print(Control())
|
||||
elif self.console.is_terminal and not self.console.is_dumb_terminal:
|
||||
with self.console:
|
||||
self.console.print(Control())
|
||||
elif (
|
||||
not self._started and not self.transient
|
||||
): # if it is finished allow files or dumb-terminals to see final result
|
||||
with self.console:
|
||||
self.console.print(Control())
|
||||
|
||||
def process_renderables(
|
||||
self, renderables: List[ConsoleRenderable]
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from threading import RLock
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from typing_extensions import Literal
|
||||
|
@ -5,7 +6,7 @@ from typing_extensions import Literal
|
|||
from ._loop import loop_last
|
||||
from .console import Console, ConsoleOptions, RenderableType, RenderResult
|
||||
from .control import Control
|
||||
from .segment import ControlCode, ControlType, Segment
|
||||
from .segment import ControlType, Segment
|
||||
from .style import StyleType
|
||||
from .text import Text
|
||||
|
||||
|
@ -77,9 +78,11 @@ class LiveRender:
|
|||
def __rich_console__(
|
||||
self, console: Console, options: ConsoleOptions
|
||||
) -> RenderResult:
|
||||
|
||||
renderable = self.renderable
|
||||
_Segment = Segment
|
||||
style = console.get_style(self.style)
|
||||
lines = console.render_lines(self.renderable, options, style=style, pad=False)
|
||||
lines = console.render_lines(renderable, options, style=style, pad=False)
|
||||
shape = _Segment.get_shape(lines)
|
||||
|
||||
_, height = shape
|
||||
|
|
|
@ -850,7 +850,7 @@ class Progress(JupyterMixin):
|
|||
|
||||
def refresh(self) -> None:
|
||||
"""Refresh (render) the progress information."""
|
||||
if not self.disable:
|
||||
if not self.disable and self.live.is_started:
|
||||
self.live.refresh()
|
||||
|
||||
def get_renderable(self) -> RenderableType:
|
||||
|
@ -872,7 +872,6 @@ class Progress(JupyterMixin):
|
|||
Returns:
|
||||
Table: A table instance.
|
||||
"""
|
||||
|
||||
table_columns = (
|
||||
(
|
||||
Column(no_wrap=True)
|
||||
|
@ -899,7 +898,8 @@ class Progress(JupyterMixin):
|
|||
|
||||
def __rich__(self) -> RenderableType:
|
||||
"""Makes the Progress class itself renderable."""
|
||||
return self.get_renderable()
|
||||
with self._lock:
|
||||
return self.get_renderable()
|
||||
|
||||
def add_task(
|
||||
self,
|
||||
|
|
|
@ -310,8 +310,7 @@ def test_columns() -> None:
|
|||
|
||||
result = replace_link_ids(console.file.getvalue())
|
||||
print(repr(result))
|
||||
expected = "\x1b[?25ltest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:37\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m\ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:36\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m\r\x1b[2K\x1b[1A\x1b[2Kfoo\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:37\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m\ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:36\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m\r\x1b[2K\x1b[1A\x1b[2K\x1b[2;36m[TIME]\x1b[0m\x1b[2;36m \x1b[0mhello \ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:37\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m\ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:36\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m\r\x1b[2K\x1b[1A\x1b[2Kworld\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:37\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m\ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:36\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m\r\x1b[2K\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[33m0:01:00\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m\ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[33m0:00:45\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m\r\x1b[2K\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[33m0:01:00\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m\ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[33m0:00:45\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m\n\x1b[?25h\r\x1b[1A\x1b[2K\x1b[1A\x1b[2K"
|
||||
|
||||
expected = "\x1b[?25ltest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:07\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m\ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:16\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m\r\x1b[2K\x1b[1A\x1b[2Kfoo\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:07\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m\ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:16\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m\r\x1b[2K\x1b[1A\x1b[2K\x1b[2;36m[TIME]\x1b[0m\x1b[2;36m \x1b[0mhello \ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:07\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m\ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:16\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m\r\x1b[2K\x1b[1A\x1b[2Kworld\ntest foo \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:07\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m0/10 bytes\x1b[0m \x1b[31m?\x1b[0m\ntest bar \x1b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m \x1b[36m-:--:--\x1b[0m \x1b[33m0:00:16\x1b[0m \x1b[32m0 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m0/7 bytes \x1b[0m \x1b[31m?\x1b[0m\r\x1b[2K\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[33m0:00:30\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m\ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[33m0:00:25\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m\r\x1b[2K\x1b[1A\x1b[2Ktest foo \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[33m0:00:30\x1b[0m \x1b[32m12 bytes\x1b[0m \x1b[32m10 bytes\x1b[0m \x1b[32m12/10 bytes\x1b[0m \x1b[31m1 byte/s \x1b[0m\ntest bar \x1b[38;2;114;156;31m━━━━━━━━━━━━━━━\x1b[0m \x1b[36m0:00:00\x1b[0m \x1b[33m0:00:25\x1b[0m \x1b[32m16 bytes\x1b[0m \x1b[32m7 bytes \x1b[0m \x1b[32m16/7 bytes \x1b[0m \x1b[31m2 bytes/s\x1b[0m\n\x1b[?25h\r\x1b[1A\x1b[2K\x1b[1A\x1b[2K"
|
||||
assert result == expected
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue