allow attribute abbreviations

This commit is contained in:
Will McGugan 2020-05-19 13:38:13 +01:00
parent 54b7e3c881
commit 1db22c9d88
17 changed files with 97 additions and 37 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@ Reference
.. toctree::
:maxdepth: 3
reference/align.rst
reference/bar.rst
reference/color.rst
reference/console.rst

View File

@ -0,0 +1,7 @@
rich.align
==========
.. automodule:: rich.align
:members:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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