Merge pull request #1729 from willmcgugan/v10.15.1

revert thread safety issue
This commit is contained in:
Will McGugan 2021-11-29 17:56:55 +00:00 committed by GitHub
commit 3db6396a0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 52 additions and 56 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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