Skip to content

Commit

Permalink
Merge pull request #1729 from willmcgugan/v10.15.1
Browse files Browse the repository at this point in the history
revert thread safety issue
  • Loading branch information
willmcgugan committed Nov 29, 2021
2 parents ad6e3de + 03f40a9 commit 3db6396
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 57 deletions.
15 changes: 5 additions & 10 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,22 @@ 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.

```
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.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 8 additions & 12 deletions rich/console.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import inspect
import os
import platform
import shutil
import sys
import threading
from abc import ABC, abstractmethod
Expand All @@ -13,6 +12,7 @@
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,
Expand All @@ -25,7 +25,6 @@
Mapping,
NamedTuple,
Optional,
Set,
TextIO,
Tuple,
Type,
Expand Down Expand Up @@ -836,14 +835,10 @@ def pop_render_hook(self) -> None:
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:
Expand Down Expand Up @@ -1512,9 +1507,8 @@ def control(self, *control: Control) -> None:
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,
Expand Down Expand Up @@ -1596,7 +1590,7 @@ def print(
if overflow is None:
overflow = "ignore"
crop = False

render_hooks = self._render_hooks[:]
with self:
renderables = self._collect_renderables(
objects,
Expand All @@ -1607,7 +1601,7 @@ def print(
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,
Expand Down Expand Up @@ -1864,6 +1858,8 @@ def log(
if not objects:
objects = (NewLine(),)

render_hooks = self._render_hooks[:]

with self:
renderables = self._collect_renderables(
objects,
Expand Down Expand Up @@ -1898,7 +1894,7 @@ def log(
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
Expand Down
59 changes: 29 additions & 30 deletions rich/live.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,38 +128,37 @@ def stop(self) -> None:
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)
Expand Down
10 changes: 5 additions & 5 deletions rich/live_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,15 @@ def __rich_console__(
) -> 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(
Expand All @@ -104,10 +103,11 @@ def __rich_console__(
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

0 comments on commit 3db6396

Please sign in to comment.