mirror of https://github.com/Textualize/rich.git
allow attribute abbreviations
This commit is contained in:
parent
54b7e3c881
commit
1db22c9d88
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -5,7 +5,17 @@ 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.1.6] - Unreleased
|
||||
## [1.1.7] - 2020-05-19
|
||||
|
||||
### Changed
|
||||
|
||||
- Some style attributes may be abbreviated (b for bold, i for italic etc). Previously abbreviations worked in console markup but only one at a time, i.e. "[b]Hello[/]" but not "[b i]Hello[/]" -- now they work everywhere.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed zero division if total is 0 in progress bar
|
||||
|
||||
## [1.1.6] - 2020-05-17
|
||||
|
||||
### Added
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ Justify / Alignment
|
|||
|
||||
Both print and log support a ``justify`` argument which if set must be one of "left", "right", "center", or "full". If "left", any text printed (or logged) will be left aligned, if "right" text will be aligned to the right of the terminal, if "center" the text will be centered, and if "full" the text will be lined up with both the left and right edges of the terminal (like printed text in a book).
|
||||
|
||||
The default for ``justify`` is ``None`` which will generally look the same as ``"left"`` but with a subtle different. Left justify will pad the right of the text with spaces, while a None justify will not. You will only notice the difference if you set a background color with the ``style`` argument. The following example demonstrates the difference::
|
||||
The default for ``justify`` is ``None`` which will generally look the same as ``"left"`` but with a subtle difference. Left justify will pad the right of the text with spaces, while a None justify will not. You will only notice the difference if you set a background color with the ``style`` argument. The following example demonstrates the difference::
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
|
@ -76,6 +76,15 @@ This produces the following output:
|
|||
</span></pre>
|
||||
|
||||
|
||||
Input
|
||||
-----
|
||||
|
||||
The console class has an :meth:`~rich.console.Console.input` which works in the same way as Python's builtin ``input()`` method, but can use anything that Rich can print as a prompt. For example, here's a colorful prompt with an emoji::
|
||||
|
||||
from rich.console import Console
|
||||
console = Console()
|
||||
console.input("What is [i]your[/i] [bold red]name[/]? :smiley: ")
|
||||
|
||||
Exporting
|
||||
---------
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ Here's an example of how to set up a rich logger::
|
|||
|
||||
FORMAT = "%(message)s"
|
||||
logging.basicConfig(
|
||||
level="NOTSET", format=FORMAT, datefmt="[%X] ", handlers=[RichHandler()]
|
||||
level="NOTSET", format=FORMAT, datefmt="[%X]", handlers=[RichHandler()]
|
||||
)
|
||||
|
||||
log = logging.getLogger("rich")
|
||||
|
|
|
@ -46,6 +46,9 @@ Occasionally you may want to print something that Rich would interpret as markup
|
|||
|
||||
The function :func:`~rich.markup.escape` will handle escape of text for you.
|
||||
|
||||
.. warning::
|
||||
Be careful when using f-strings with console markup. You will need to escape any variables if they could contain square brackets.
|
||||
|
||||
Rendering Markup
|
||||
----------------
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ Reference
|
|||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
reference/align.rst
|
||||
reference/bar.rst
|
||||
reference/color.rst
|
||||
reference/console.rst
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
rich.align
|
||||
==========
|
||||
|
||||
.. automodule:: rich.align
|
||||
:members:
|
||||
|
||||
|
|
@ -30,20 +30,21 @@ The hex and rgb forms allow you to select from the full *truecolor* set of 16.7
|
|||
.. note::
|
||||
Some terminals only support 256 colors. Rich will attempt to pick the closest color it can if your color isn't available.
|
||||
|
||||
|
||||
By itself, a color will change the *foreground* color. To specify a *background* color precede the color with the word "on". For example, the following prints text in red on a white background::
|
||||
|
||||
console.print("DANGER!", style="red on white")
|
||||
|
||||
You can also set a color with the word ``"default"`` which will reset the color to a default managed by your terminal software. This works for back grounds as well, so the style of ``"default on default"`` is what your terminal starts with.
|
||||
|
||||
You can set a style attribute by adding one or more of the following words:
|
||||
|
||||
* ``"bold"`` For bold text.
|
||||
* ``"blink"`` For text that flashes (use this one sparingly).
|
||||
* ``"conceal"`` For *concealed* text (not supported by many terminals).
|
||||
* ``"italic"`` For italic text.
|
||||
* ``"reverse"`` For text with foreground and background colors reversed.
|
||||
* ``"strike"`` For text with a line through it.
|
||||
* ``"underline"`` For underlined text.
|
||||
* ``"bold"`` or ``"b"`` for bold text.
|
||||
* ``"blink"`` for text that flashes (use this one sparingly).
|
||||
* ``"conceal"`` for *concealed* text (not supported by many terminals).
|
||||
* ``"italic"`` or ``"i"`` for italic text.
|
||||
* ``"reverse"`` or ``"r"`` for text with foreground and background colors reversed.
|
||||
* ``"strike"`` or ``"s"`` for text with a line through it.
|
||||
* ``"underline"`` or ``"u"`` for underlined text.
|
||||
|
||||
Style attributes and colors may be used in combination with each other. For example::
|
||||
|
||||
|
@ -74,7 +75,7 @@ Ultimately the style definition is parsed and an instance of a :class:`~rich.sty
|
|||
from rich.style import Style
|
||||
console.print("Danger, Will Robinson!", style=Style(color="red", blink=True, bold=True)
|
||||
|
||||
It is slightly quicker to construct a Style class like this, since a style definition takes a little time to parse -- but only on the first call, as Rich will cache any style definitions it parses.
|
||||
It is slightly quicker to construct a Style class like this, since a style definition takes a little time to parse -- but only on the first call, as Rich will cache parsed style definitions.
|
||||
|
||||
You can parse a style definition explicitly with the :meth:`~rich.style.Style.parse` method.
|
||||
|
||||
|
|
|
@ -21,4 +21,9 @@ Alternatively, you can construct styled text by calling :meth:`~rich.text.Text.a
|
|||
text.append(" World!")
|
||||
console.print(text)
|
||||
|
||||
You can also apply a style to given words in the text with :meth:`~rich.text.Text.highlight_words` or for ultimate control call :meth:`~rich.text.Text.highlight_regex` to highlight text matching a *regular expression*.
|
||||
Since building Text instances from parts is a common requirement, Rich offers :meth:`~rich.text.Text.assemble` which will combine strings or pairs of string and Style, and return an Text instance. The follow example is equivalent to the code above::
|
||||
|
||||
text = Text.assemble(("Hello", "bold magenta"), " World!")
|
||||
console.print(text)
|
||||
|
||||
You can apply a style to given words in the text with :meth:`~rich.text.Text.highlight_words` or for ultimate control call :meth:`~rich.text.Text.highlight_regex` to highlight text matching a *regular expression*.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "rich"
|
||||
homepage = "https://github.com/willmcgugan/rich"
|
||||
documentation = "https://rich.readthedocs.io/en/latest/"
|
||||
version = "1.1.6"
|
||||
version = "1.1.7"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
authors = ["Will McGugan <willmcgugan@gmail.com>"]
|
||||
license = "MIT"
|
||||
|
|
|
@ -62,7 +62,9 @@ class Bar:
|
|||
bar = "▓" if legacy_windows else "━"
|
||||
half_bar_right = "░" if legacy_windows else "╸"
|
||||
half_bar_left = " " if legacy_windows else "╺"
|
||||
complete_halves = int(width * 2 * completed / self.total)
|
||||
complete_halves = (
|
||||
int(width * 2 * completed / self.total) if self.total else width * 2
|
||||
)
|
||||
bar_count = complete_halves // 2
|
||||
half_bar_count = complete_halves % 2
|
||||
style = console.get_style(self.style)
|
||||
|
|
|
@ -22,17 +22,13 @@ DEFAULT_STYLES: Dict[str, Style] = {
|
|||
"bold": Style(bold=True),
|
||||
"strong": Style(bold=True),
|
||||
"code": Style(reverse=True, bold=True),
|
||||
"b": Style(bold=True),
|
||||
"italic": Style(italic=True),
|
||||
"emphasize": Style(italic=True),
|
||||
"i": Style(italic=True),
|
||||
"underline": Style(underline=True),
|
||||
"u": Style(underline=True),
|
||||
"blink": Style(blink=True),
|
||||
"blink2": Style(blink2=True),
|
||||
"reverse": Style(reverse=True),
|
||||
"strike": Style(strike=True),
|
||||
"s": Style(strike=True),
|
||||
"black": Style(color="black"),
|
||||
"red": Style(color="red"),
|
||||
"green": Style(color="green"),
|
||||
|
|
|
@ -26,7 +26,7 @@ class Tag(NamedTuple):
|
|||
return (
|
||||
f"[{self.name}]"
|
||||
if self.parameters is None
|
||||
else f"[{self.name} {self.parameters}]"
|
||||
else f"[{self.name}={self.parameters}]"
|
||||
)
|
||||
|
||||
|
||||
|
@ -61,7 +61,7 @@ def _parse(markup: str) -> Iterable[Tuple[int, Optional[str], Optional[Tag]]]:
|
|||
if equals:
|
||||
yield start, None, Tag(text, parameters)
|
||||
else:
|
||||
yield start, None, Tag(normalize(tag_text.strip()), None)
|
||||
yield start, None, Tag(tag_text.strip(), None)
|
||||
else:
|
||||
yield start, (escape_open and "[") or (escape_close and "]"), None # type: ignore
|
||||
position = end
|
||||
|
@ -84,6 +84,7 @@ def render(markup: str, style: Union[str, Style] = "", emoji: bool = True) -> Te
|
|||
"""
|
||||
text = Text(style=style)
|
||||
append = text.append
|
||||
normalize = Style.normalize
|
||||
|
||||
style_stack: List[Tuple[int, Tag]] = []
|
||||
pop = style_stack.pop
|
||||
|
@ -108,6 +109,7 @@ def render(markup: str, style: Union[str, Style] = "", emoji: bool = True) -> Te
|
|||
if tag.name.startswith("/"): # Closing tag
|
||||
style_name = tag.name[1:].strip()
|
||||
if style_name: # explicit close
|
||||
style_name = normalize(style_name)
|
||||
try:
|
||||
start, open_tag = pop_style(style_name)
|
||||
except KeyError:
|
||||
|
@ -124,7 +126,8 @@ def render(markup: str, style: Union[str, Style] = "", emoji: bool = True) -> Te
|
|||
|
||||
append_span(_Span(start, len(text), str(open_tag)))
|
||||
else: # Opening tag
|
||||
style_stack.append((len(text), tag))
|
||||
normalized_tag = Tag(normalize(tag.name), tag.parameters)
|
||||
style_stack.append((len(text), normalized_tag))
|
||||
|
||||
text_length = len(text)
|
||||
while style_stack:
|
||||
|
|
|
@ -57,6 +57,8 @@ class Measurement(NamedTuple):
|
|||
|
||||
if isinstance(renderable, str):
|
||||
renderable = console.render_str(renderable)
|
||||
|
||||
renderable = getattr(renderable, "__rich__", renderable)
|
||||
if is_renderable(renderable):
|
||||
get_console_width = getattr(renderable, "__measure__", None)
|
||||
if get_console_width is not None:
|
||||
|
|
|
@ -153,7 +153,11 @@ class BarColumn(ProgressColumn):
|
|||
|
||||
def render(self, task: "Task") -> Bar:
|
||||
"""Gets a progress bar widget for a task."""
|
||||
return Bar(total=task.total, completed=task.completed, width=self.bar_width)
|
||||
return Bar(
|
||||
total=max(0, task.total),
|
||||
completed=max(0, task.completed),
|
||||
width=max(1, self.bar_width),
|
||||
)
|
||||
|
||||
|
||||
class TimeRemainingColumn(ProgressColumn):
|
||||
|
|
|
@ -215,15 +215,22 @@ class Style:
|
|||
`Style`: A Style instance.
|
||||
"""
|
||||
style_attributes = {
|
||||
"dim",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"blink",
|
||||
"blink2",
|
||||
"reverse",
|
||||
"conceal",
|
||||
"strike",
|
||||
"dim": "dim",
|
||||
"d": "dim",
|
||||
"bold": "bold",
|
||||
"b": "bold",
|
||||
"italic": "italic",
|
||||
"i": "italic",
|
||||
"underline": "underline",
|
||||
"u": "underline",
|
||||
"blink": "blink",
|
||||
"blink2": "blink2",
|
||||
"reverse": "reverse",
|
||||
"r": "reverse",
|
||||
"conceal": "conceal",
|
||||
"c": "conceal",
|
||||
"strike": "strike",
|
||||
"s": "strike"
|
||||
}
|
||||
color: Optional[str] = None
|
||||
bgcolor: Optional[str] = None
|
||||
|
@ -247,11 +254,12 @@ class Style:
|
|||
|
||||
elif word == "not":
|
||||
word = next(words, "")
|
||||
if word not in style_attributes:
|
||||
attribute = style_attributes.get(word)
|
||||
if attribute is None:
|
||||
raise errors.StyleSyntaxError(
|
||||
f"expected style attribute after 'not', found {original_word!r}"
|
||||
)
|
||||
attributes[word] = False
|
||||
)
|
||||
attributes[attribute] = False
|
||||
|
||||
elif word == "link":
|
||||
word = next(words, "")
|
||||
|
@ -260,7 +268,7 @@ class Style:
|
|||
link = word
|
||||
|
||||
elif word in style_attributes:
|
||||
attributes[word] = True
|
||||
attributes[style_attributes[word]] = True
|
||||
|
||||
else:
|
||||
try:
|
||||
|
|
|
@ -260,6 +260,9 @@ class Table:
|
|||
def add_row(self, *renderables: Optional["RenderableType"]) -> None:
|
||||
"""Add a row of renderables.
|
||||
|
||||
Args:
|
||||
*renderables (None or renderable): Each cell in a row must be None (for a blank cell) or a renderable object (including str).
|
||||
|
||||
Raises:
|
||||
errors.NotRenderableError: If you add something that can't be rendered.
|
||||
"""
|
||||
|
@ -289,7 +292,7 @@ class Table:
|
|||
add_cell(column, renderable)
|
||||
else:
|
||||
raise errors.NotRenderableError(
|
||||
f"unable to render {renderable!r}; str or object with a __console__ method is required"
|
||||
f"unable to render {type(renderable).__name__}; a string or other renderable object is required"
|
||||
)
|
||||
self._row_count += 1
|
||||
|
||||
|
|
|
@ -45,6 +45,12 @@ def test_measure():
|
|||
assert measurement.maximum == 120
|
||||
|
||||
|
||||
def test_zero_total():
|
||||
# Shouldn't throw zero division error
|
||||
bar = Bar(total=0)
|
||||
render(bar)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bar = Bar(completed=11, width=50)
|
||||
bar_render = render(bar)
|
||||
|
|
Loading…
Reference in New Issue