mirror of https://github.com/Textualize/rich.git
improved jupyter support
This commit is contained in:
parent
856ee79658
commit
e50a6be3c6
|
@ -1,3 +1,4 @@
|
|||
*.ipynb
|
||||
.pytype
|
||||
.DS_Store
|
||||
.vscode
|
||||
|
|
|
@ -5,11 +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).
|
||||
|
||||
## [1.2.4] Unreleased
|
||||
## [1.3.0] Unreleased
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated `markdown.Heading.create()` to work with subclassing.
|
||||
- Console now transparently works with Jupyter
|
||||
|
||||
## [1.2.3] - 2020-05-24
|
||||
|
||||
|
|
|
@ -7,18 +7,27 @@ if TYPE_CHECKING:
|
|||
_console: Optional["Console"] = None
|
||||
|
||||
|
||||
def get_console() -> "Console":
|
||||
"""Get a global Console instance.
|
||||
|
||||
Returns:
|
||||
Console: A console instance.
|
||||
"""
|
||||
global _console
|
||||
if _console is None:
|
||||
from .console import Console
|
||||
|
||||
_console = Console()
|
||||
|
||||
return _console
|
||||
|
||||
|
||||
def print(
|
||||
*objects: Any, sep=" ", end="\n", file: IO[str] = None, flush: bool = False,
|
||||
):
|
||||
from .console import Console
|
||||
|
||||
global _console
|
||||
if _console is None:
|
||||
from ._global import console
|
||||
|
||||
_console = console
|
||||
|
||||
write_console = _console if file is None else Console(file=file)
|
||||
write_console = get_console() if file is None else Console(file=file)
|
||||
return write_console.print(*objects, sep=sep, end=end)
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
"""A global instance of a Console."""
|
||||
|
||||
from .console import Console
|
||||
from . import jupyter
|
||||
|
||||
console = jupyter.get_console() if jupyter.is_jupyter() else Console()
|
|
@ -44,7 +44,6 @@ from .highlighter import NullHighlighter, ReprHighlighter
|
|||
from .pretty import Pretty
|
||||
from .style import Style
|
||||
from .tabulate import tabulate_mapping
|
||||
from . import jupyter
|
||||
from . import highlighter
|
||||
from . import themes
|
||||
from .pretty import Pretty
|
||||
|
@ -193,6 +192,25 @@ class ConsoleDimensions(NamedTuple):
|
|||
height: int
|
||||
|
||||
|
||||
def _is_jupyter() -> bool:
|
||||
"""Check if we're running in a Jupyter notebook."""
|
||||
try:
|
||||
from IPython.display import display
|
||||
except ImportError:
|
||||
return False
|
||||
try:
|
||||
get_ipython # type: ignore
|
||||
except NameError:
|
||||
return False
|
||||
shell = get_ipython().__class__.__name__ # type: ignore
|
||||
if shell == "ZMQInteractiveShell":
|
||||
return True # Jupyter notebook or qtconsole
|
||||
elif shell == "TerminalInteractiveShell":
|
||||
return False # Terminal running IPython
|
||||
else:
|
||||
return False # Other type (?)
|
||||
|
||||
|
||||
COLOR_SYSTEMS = {
|
||||
"standard": ColorSystem.STANDARD,
|
||||
"256": ColorSystem.EIGHT_BIT,
|
||||
|
@ -229,6 +247,8 @@ class Console:
|
|||
Args:
|
||||
color_system (str, optional): The color system supported by your terminal,
|
||||
either ``"standard"``, ``"256"`` or ``"truecolor"``. Leave as ``"auto"`` to autodetect.
|
||||
force_terminal (bool, optional): Force the Console to write control codes even when a terminal is not detected. Defaults to False.
|
||||
force_jupyter (bool, optional): Force the Console to write to Jupyter even when Jupyter is not detected. Defaults to False
|
||||
theme (Theme, optional): An optional style theme object, or ``None`` for default theme.
|
||||
file (IO, optional): A file object where the console should write to. Defaults to stdoutput.
|
||||
width (int, optional): The width of the terminal. Leave as default to auto-detect width.
|
||||
|
@ -246,10 +266,12 @@ class Console:
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
color_system: Optional[
|
||||
Literal["auto", "standard", "256", "truecolor", "windows"]
|
||||
] = "auto",
|
||||
force_terminal: bool = False,
|
||||
force_jupyter: bool = False,
|
||||
theme: Theme = None,
|
||||
file: IO[str] = None,
|
||||
width: int = None,
|
||||
|
@ -264,6 +286,10 @@ class Console:
|
|||
log_time_format: str = "[%X]",
|
||||
highlighter: Optional["HighlighterType"] = ReprHighlighter(),
|
||||
):
|
||||
self.is_jupyter = force_jupyter or _is_jupyter()
|
||||
if self.is_jupyter:
|
||||
width = width or 93
|
||||
height = height or 100
|
||||
self._styles = themes.DEFAULT.styles if theme is None else theme.styles
|
||||
self._width = width
|
||||
self._height = height
|
||||
|
@ -398,9 +424,6 @@ class Console:
|
|||
Returns:
|
||||
ConsoleDimensions: A named tuple containing the dimensions.
|
||||
"""
|
||||
if jupyter.is_jupyter():
|
||||
return ConsoleDimensions(93, 100)
|
||||
|
||||
if self._width is not None and self._height is not None:
|
||||
return ConsoleDimensions(self._width, self._height)
|
||||
|
||||
|
@ -833,8 +856,10 @@ class Console:
|
|||
"""Check if the buffer may be rendered."""
|
||||
with self._lock:
|
||||
if self._buffer_index == 0:
|
||||
if jupyter.is_jupyter():
|
||||
jupyter.display(self._buffer)
|
||||
if self.is_jupyter:
|
||||
from .jupyter import display
|
||||
|
||||
display(self._buffer)
|
||||
del self._buffer[:]
|
||||
else:
|
||||
text = self._render_buffer()
|
||||
|
|
|
@ -2,45 +2,19 @@ import io
|
|||
from typing import Any, Iterable, IO, List, Optional, TYPE_CHECKING, Union
|
||||
|
||||
# from .console import Console as BaseConsole
|
||||
from .__init__ import get_console
|
||||
from .segment import Segment
|
||||
from .style import Style
|
||||
from .terminal_theme import DEFAULT_TERMINAL_THEME
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .console import Console
|
||||
from .console import Console, RenderableType
|
||||
|
||||
JUPYTER_HTML_FORMAT = """\
|
||||
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">{code}</pre>
|
||||
"""
|
||||
|
||||
|
||||
_console: Optional["Console"] = None
|
||||
|
||||
|
||||
def get_console() -> "Console":
|
||||
from .console import Console
|
||||
|
||||
global _console
|
||||
if _console is None:
|
||||
_console = Console(width=100)
|
||||
return _console
|
||||
|
||||
|
||||
def is_jupyter() -> bool:
|
||||
"""Check if we're running in a Jupyter notebook."""
|
||||
try:
|
||||
get_ipython # type: ignore
|
||||
except NameError:
|
||||
return False
|
||||
shell = get_ipython().__class__.__name__ # type: ignore
|
||||
if shell == "ZMQInteractiveShell":
|
||||
return True # Jupyter notebook or qtconsole
|
||||
elif shell == "TerminalInteractiveShell":
|
||||
return False # Terminal running IPython
|
||||
else:
|
||||
return False # Other type (?)
|
||||
|
||||
|
||||
class JupyterRenderable:
|
||||
"""A shim to write html to Jupyter notebook."""
|
||||
|
||||
|
@ -48,7 +22,7 @@ class JupyterRenderable:
|
|||
self.html = html
|
||||
|
||||
@classmethod
|
||||
def render(self, rich_renderable) -> str:
|
||||
def render(self, rich_renderable: "RenderableType") -> str:
|
||||
console = get_console()
|
||||
segments = console.render(rich_renderable, console.options)
|
||||
html = _render_segments(segments)
|
||||
|
@ -100,3 +74,9 @@ def display(segments: Iterable[Segment]) -> None:
|
|||
html = _render_segments(segments)
|
||||
jupyter_renderable = JupyterRenderable(html)
|
||||
ipython_display(jupyter_renderable)
|
||||
|
||||
|
||||
def print(*args, **kwargs) -> None:
|
||||
"""Proxy for Console print."""
|
||||
console = get_console()
|
||||
return console.print(*args, **kwargs)
|
||||
|
|
|
@ -25,6 +25,7 @@ from typing import (
|
|||
Union,
|
||||
)
|
||||
|
||||
from . import get_console
|
||||
from .bar import Bar
|
||||
from .console import Console, JustifyValues, RenderGroup, RenderableType
|
||||
from .highlighter import Highlighter
|
||||
|
@ -387,7 +388,7 @@ class Progress:
|
|||
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
||||
TimeRemainingColumn(),
|
||||
)
|
||||
self.console = console or Console(file=sys.stdout)
|
||||
self.console = console or get_console()
|
||||
self.auto_refresh = auto_refresh
|
||||
self.refresh_per_second = refresh_per_second
|
||||
self.speed_estimate_period = speed_estimate_period
|
||||
|
|
|
@ -5,15 +5,18 @@ import rich
|
|||
from rich.console import Console
|
||||
|
||||
|
||||
def test_rich_print():
|
||||
output = io.StringIO()
|
||||
def test_get_console():
|
||||
console = rich.get_console()
|
||||
assert isinstance(console, Console)
|
||||
|
||||
assert rich._console is None
|
||||
backup_file = sys.stdout
|
||||
|
||||
def test_rich_print():
|
||||
console = rich.get_console()
|
||||
output = io.StringIO()
|
||||
backup_file = console.file
|
||||
try:
|
||||
sys.stdout = output
|
||||
console.file = output
|
||||
rich.print("foo")
|
||||
assert isinstance(rich._console, Console)
|
||||
assert output.getvalue() == "foo\n"
|
||||
finally:
|
||||
sys.stdout = backup_file
|
||||
console.file = backup_file
|
||||
|
|
Loading…
Reference in New Issue