theme improvements and docs

This commit is contained in:
Will McGugan 2020-03-02 10:26:55 +00:00
parent c55b357f37
commit 433ee13234
4 changed files with 80 additions and 19 deletions

View File

@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added tab_size to Console and Text
- Added protocol.is_renderable for runtime check
- Added emoji switch to Console
- Added inherit boolean to Theme
- Made Console thread safe, with a thread local buffer
### Changed

View File

@ -63,20 +63,35 @@ It is slightly quicker to construct a Style class like this, since a style defin
You can parse a style definition explicitly with the :meth:`~rich.style.Style.parse` method.
Custom Styles
-------------
Style Themes
------------
While it is possible to explicitly specify styles each time you use them, it is discouraged. Rich allows you to predefine style names which you can use in place on an explicit style. For instance, you may want to define styles for info, warning, danger etc.
If you re-use styles it can be a maintenance headache if you ever want to modify an attribute or color -- you would have to change every line where the style is used. Rich provides a :class:`rich.theme.Theme` class which you can use to pre-define styles, so if you ever need to modify a style you only need change one file.
You can define a mapping of names to styles in the :class:`~rich.console.Console` constructor. These styles will be merged in to the default styles, potentially overwriting them. Here's an example::
Style themes can also make your code more semantic, for instance a style called ``"warning"`` better expresses intent that ``"italic magenta underline"``.
To use a style theme, construct a :class:`rich.theme.Theme` instance and pass it to the :class:`~rich.console.Console` constructor. Here's an example::
from rich.console import Console
styles = {
from rich.theme import Theme
custom_theme = Theme({
"info" : Style.parse("dim cyan"),
"warning": Style.parse("magenta"),
"danger": Style.parse("bold red")
}
console = Console(styles=styles)
})
console = Console(theme=custom_theme)
console.print("This is information", style="info")
console.print("Something terrible happened!", style="danger")
You can also use these custom styles via markup. For example::
console.print("[warning]The pod bay doors are locked[/warning]")
If you prefer you can write your styles in an external config file rather than in Python. Here's an example of the format::
[styles]
info = dim cyan
warning = magenta
danger bold red
You can read these files with the :method:`~rich.theme.Theme.read` method.

View File

@ -12,6 +12,7 @@ import platform
import re
import shutil
import sys
import threading
from typing import (
Any,
Callable,
@ -286,7 +287,7 @@ class Console:
else:
self._color_system = COLOR_SYSTEMS[color_system]
self.buffer: List[Segment] = []
self._thread_locals = threading.local()
self._buffer_index = 0
self._record_buffer: List[Segment] = []
@ -302,6 +303,14 @@ class Console:
def __repr__(self) -> str:
return f"<console width={self.width} {str(self._color_system)}>"
@property
def _buffer(self) -> List[Segment]:
"""Get a thread local buffer."""
buffer = getattr(self._thread_locals, "buffer", None)
if buffer is None:
buffer = self._thread_locals.buffer = []
return buffer
def _detect_color_system(self,) -> Optional[ColorSystem]:
"""Detect color system from env vars."""
if not self.is_terminal:
@ -423,7 +432,7 @@ class Console:
assert count >= 0, "count must be >= 0"
if count:
self.buffer.append(Segment("\n" * count))
self._buffer.append(Segment("\n" * count))
self._check_buffer()
def _render(
@ -771,7 +780,7 @@ class Console:
)
render_options = self.options
extend = self.buffer.extend
extend = self._buffer.extend
render = self.render
with self.style(style):
for renderable in renderables:
@ -838,7 +847,7 @@ class Console:
renderables.append(tabulate_mapping(locals_map, title="Locals"))
with self:
self.buffer.extend(
self._buffer.extend(
self.render(
self._log_render(self, renderables, path=path, line_no=line_no),
self.options,
@ -856,10 +865,10 @@ class Console:
output: List[str] = []
append = output.append
color_system = self._color_system
buffer = self.buffer[:]
buffer = self._buffer[:]
if self.record:
self._record_buffer.extend(buffer)
del self.buffer[:]
del self._buffer[:]
for line in Segment.split_and_crop_lines(buffer, self.width, pad=False):
for text, style in line:
if style:

View File

@ -1,12 +1,25 @@
import configparser
from typing import Dict, IO
from .default_styles import DEFAULT_STYLES
from .style import Style
class Theme:
def __init__(self, styles: Dict[str, Style] = None):
self.styles = styles or {}
"""An container for style information, used by :class:`~rich.console.Console`.
Args:
styles (Dict[str, Style], optional): A mapping of style names on to styles. Defaults to None for empty styles.
inherit (bool, optional): Switch to inherit default styles. Defaults to True.
"""
def __init__(self, styles: Dict[str, Style] = None, inherit: bool = True):
if inherit:
self.styles = DEFAULT_STYLES
else:
self.styles = {}
if styles is not None:
self.styles.update(styles)
@property
def config(self) -> str:
@ -19,14 +32,35 @@ class Theme:
return config
@classmethod
def from_file(cls, config_file: IO[str], source: str = None) -> "Theme":
def from_file(
cls, config_file: IO[str], source: str = None, inherit: bool = True
) -> "Theme":
"""Load a theme from a text mode file.
Args:
config_file (IO[str]): An open conf file.
source (str, optional): The filename of the open file. Defaults to None.
inherit (bool, optional): Switch to inherit default styles. Defaults to True.
Returns:
Theme: A New theme instance.
"""
config = configparser.ConfigParser()
config.read_file(config_file, source=source)
styles = {name: Style.parse(value) for name, value in config.items("styles")}
theme = Theme(styles)
theme = Theme(styles, inherit=inherit)
return theme
@classmethod
def read(cls, path: str) -> "Theme":
def read(cls, path: str, inherit: bool = True) -> "Theme":
"""Read a theme from a path.
Args:
path (str): Path to a config file readable by Python configparser module.
inherit (bool, optional): Switch to inherit default styles. Defaults to True.
Returns:
Theme: A new theme instance.
"""
with open(path, "rt") as config_file:
return cls.from_file(config_file, source=path)
return cls.from_file(config_file, source=path, inherit=inherit)