mirror of https://github.com/Textualize/rich.git
Merge pull request #1729 from willmcgugan/v10.15.1
revert thread safety issue
This commit is contained in:
commit
3db6396a0c
|
@ -6,19 +6,18 @@ labels: Needs triage
|
|||
assignees: ""
|
||||
---
|
||||
|
||||
**Read the docs**
|
||||
You might find a solution to your problem in the [docs](https://rich.readthedocs.io/en/latest/introduction.html) -- consider using the search function there.
|
||||
You may find a solution to your problem in the [docs](https://rich.readthedocs.io/en/latest/introduction.html) or [issues](https://github.com/willmcgugan/rich/issues).
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
A minimal code example that reproduces the problem would be a big help if you can provide it. If the issue is visual in nature, consider posting a screenshot.
|
||||
Edit this with a clear and concise description of what the bug.
|
||||
|
||||
Provide a minimal code example that demonstrates the issue if you can. If the issue is visual in nature, consider posting a screenshot.
|
||||
|
||||
**Platform**
|
||||
|
||||
What platform (Win/Linux/Mac) are you running on? What terminal software are you using?
|
||||
|
||||
**Diagnose**
|
||||
I may ask you to cut and paste the output of the following commands. It may save some time if you do it now.
|
||||
|
||||
```
|
||||
|
@ -26,7 +25,3 @@ python -m rich.diagnose
|
|||
python -m rich._windows
|
||||
pip freeze | grep rich
|
||||
```
|
||||
|
||||
**Did I help?**
|
||||
|
||||
If I was able to resolve your problem, consider [sponsoring](https://github.com/sponsors/willmcgugan) my work on Rich, or [buy me a coffee](https://ko-fi.com/willmcgugan) to say thanks.
|
||||
|
|
|
@ -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.15.1] - 2021-11-29
|
||||
|
||||
### Fixed
|
||||
|
||||
- Reverted thread-safety fix for Live that introduced deadlock potential
|
||||
|
||||
## [10.15.0] - 2021-11-28
|
||||
|
||||
### Added
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import inspect
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import sys
|
||||
import threading
|
||||
from abc import ABC, abstractmethod
|
||||
|
@ -13,6 +12,7 @@ from html import escape
|
|||
from inspect import isclass
|
||||
from itertools import islice
|
||||
from time import monotonic
|
||||
from threading import RLock
|
||||
from types import FrameType, TracebackType, ModuleType
|
||||
from typing import (
|
||||
IO,
|
||||
|
@ -25,7 +25,6 @@ from typing import (
|
|||
Mapping,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Set,
|
||||
TextIO,
|
||||
Tuple,
|
||||
Type,
|
||||
|
@ -836,14 +835,10 @@ class Console:
|
|||
def __enter__(self) -> "Console":
|
||||
"""Own context manager to enter buffer context."""
|
||||
self._enter_buffer()
|
||||
if self._live:
|
||||
self._live._lock.acquire()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
||||
"""Exit buffer context."""
|
||||
if self._live:
|
||||
self._live._lock.release()
|
||||
self._exit_buffer()
|
||||
|
||||
def begin_capture(self) -> None:
|
||||
|
@ -1512,9 +1507,8 @@ class Console:
|
|||
control_codes (str): Control codes, such as those that may move the cursor.
|
||||
"""
|
||||
if not self.is_dumb_terminal:
|
||||
for _control in control:
|
||||
self._buffer.append(_control.segment)
|
||||
self._check_buffer()
|
||||
with self:
|
||||
self._buffer.extend(_control.segment for _control in control)
|
||||
|
||||
def out(
|
||||
self,
|
||||
|
@ -1596,7 +1590,7 @@ class Console:
|
|||
if overflow is None:
|
||||
overflow = "ignore"
|
||||
crop = False
|
||||
|
||||
render_hooks = self._render_hooks[:]
|
||||
with self:
|
||||
renderables = self._collect_renderables(
|
||||
objects,
|
||||
|
@ -1607,7 +1601,7 @@ class Console:
|
|||
markup=markup,
|
||||
highlight=highlight,
|
||||
)
|
||||
for hook in self._render_hooks:
|
||||
for hook in render_hooks:
|
||||
renderables = hook.process_renderables(renderables)
|
||||
render_options = self.options.update(
|
||||
justify=justify,
|
||||
|
@ -1864,6 +1858,8 @@ class Console:
|
|||
if not objects:
|
||||
objects = (NewLine(),)
|
||||
|
||||
render_hooks = self._render_hooks[:]
|
||||
|
||||
with self:
|
||||
renderables = self._collect_renderables(
|
||||
objects,
|
||||
|
@ -1898,7 +1894,7 @@ class Console:
|
|||
link_path=link_path,
|
||||
)
|
||||
]
|
||||
for hook in self._render_hooks:
|
||||
for hook in render_hooks:
|
||||
renderables = hook.process_renderables(renderables)
|
||||
new_segments: List[Segment] = []
|
||||
extend = new_segments.extend
|
||||
|
|
57
rich/live.py
57
rich/live.py
|
@ -128,38 +128,37 @@ class Live(JupyterMixin, RenderHook):
|
|||
with self._lock:
|
||||
if not self._started:
|
||||
return
|
||||
self.console.clear_live()
|
||||
self._started = False
|
||||
try:
|
||||
if self.auto_refresh and self._refresh_thread is not None:
|
||||
self._refresh_thread.stop()
|
||||
self._refresh_thread.join()
|
||||
# allow it to fully render on the last even if overflow
|
||||
self.vertical_overflow = "visible"
|
||||
if not self._alt_screen and not self.console.is_jupyter:
|
||||
self.refresh()
|
||||
|
||||
finally:
|
||||
if self._refresh_thread is not None:
|
||||
self._refresh_thread = None
|
||||
|
||||
self._disable_redirect_io()
|
||||
self.console.pop_render_hook()
|
||||
if not self._alt_screen and self.console.is_terminal:
|
||||
self.console.line()
|
||||
self.console.show_cursor(True)
|
||||
if self._alt_screen:
|
||||
self.console.set_alt_screen(False)
|
||||
|
||||
if self.transient and not self._alt_screen:
|
||||
self.console.control(self._live_render.restore_cursor())
|
||||
if self.ipy_widget is not None: # pragma: no cover
|
||||
if self.transient:
|
||||
self.ipy_widget.close()
|
||||
else:
|
||||
# jupyter last refresh must occur after console pop render hook
|
||||
# i am not sure why this is needed
|
||||
if self.auto_refresh and self._refresh_thread is not None:
|
||||
self._refresh_thread.stop()
|
||||
self._refresh_thread.join()
|
||||
self._refresh_thread = None
|
||||
# allow it to fully render on the last even if overflow
|
||||
self.vertical_overflow = "visible"
|
||||
with self.console:
|
||||
try:
|
||||
if not self._alt_screen and not self.console.is_jupyter:
|
||||
self.refresh()
|
||||
finally:
|
||||
self._disable_redirect_io()
|
||||
self.console.pop_render_hook()
|
||||
if not self._alt_screen and self.console.is_terminal:
|
||||
self.console.line()
|
||||
self.console.show_cursor(True)
|
||||
if self._alt_screen:
|
||||
self.console.set_alt_screen(False)
|
||||
|
||||
if self.transient and not self._alt_screen:
|
||||
self.console.control(self._live_render.restore_cursor())
|
||||
if self.ipy_widget is not None: # pragma: no cover
|
||||
if self.transient:
|
||||
self.ipy_widget.close()
|
||||
else:
|
||||
# jupyter last refresh must occur after console pop render hook
|
||||
# i am not sure why this is needed
|
||||
self.refresh()
|
||||
self.console.clear_live()
|
||||
|
||||
def __enter__(self) -> "Live":
|
||||
self.start(refresh=self._renderable is not None)
|
||||
|
|
|
@ -84,16 +84,15 @@ class LiveRender:
|
|||
) -> RenderResult:
|
||||
|
||||
renderable = self.renderable
|
||||
_Segment = Segment
|
||||
style = console.get_style(self.style)
|
||||
lines = console.render_lines(renderable, options, style=style, pad=False)
|
||||
shape = _Segment.get_shape(lines)
|
||||
shape = Segment.get_shape(lines)
|
||||
|
||||
_, height = shape
|
||||
if height > options.size.height:
|
||||
if self.vertical_overflow == "crop":
|
||||
lines = lines[: options.size.height]
|
||||
shape = _Segment.get_shape(lines)
|
||||
shape = Segment.get_shape(lines)
|
||||
elif self.vertical_overflow == "ellipsis":
|
||||
lines = lines[: (options.size.height - 1)]
|
||||
overflow_text = Text(
|
||||
|
@ -104,10 +103,11 @@ class LiveRender:
|
|||
style="live.ellipsis",
|
||||
)
|
||||
lines.append(list(console.render(overflow_text)))
|
||||
shape = _Segment.get_shape(lines)
|
||||
shape = Segment.get_shape(lines)
|
||||
self._shape = shape
|
||||
|
||||
new_line = Segment.line()
|
||||
for last, line in loop_last(lines):
|
||||
yield from line
|
||||
if not last:
|
||||
yield _Segment.line()
|
||||
yield new_line
|
||||
|
|
Loading…
Reference in New Issue