Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Newline fix #180

Merged
merged 9 commits into from
Jul 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
name: Test Rich module

on: [push, pull_request]
on: [pull_request]

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
max-parallel: 4
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
os: [windows-latest, ubuntu-latest, macos-latest]
python-version: [3.6, 3.7, 3.8]

steps:
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ 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).

## [4.2.0] - 2020-07-27

### Fixed

- Fixed missing new lines https://github.com/willmcgugan/rich/issues/178
- Fixed Progress.track https://github.com/willmcgugan/rich/issues/184
- Remove control codes from exported text https://github.com/willmcgugan/rich/issues/181
- Implemented auto-detection and color rendition of 16-color mode

## [4.1.0] - 2020-07-26

### Changed
Expand Down
4 changes: 2 additions & 2 deletions docs/source/reference/emoji.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
rich.box
========
rich.emoji
==========

.. automodule:: rich.emoji
:members: Emoji
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "rich"
homepage = "https://github.com/willmcgugan/rich"
documentation = "https://rich.readthedocs.io/en/latest/"
version = "4.1.0"
version = "4.2.0"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
authors = ["Will McGugan <willmcgugan@gmail.com>"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion rich/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def make_test_card() -> Table:
"Text",
RenderGroup(
Text.from_markup(
"""Word wrap text. Justify [green]left[/], [yellow]center[/], [blue]right[/] or [red]full[/].\n\n"""
"""Word wrap text. Justify [green]left[/], [yellow]center[/], [blue]right[/] or [red]full[/].\n"""
),
lorem_table,
),
Expand Down
36 changes: 30 additions & 6 deletions rich/_palettes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,42 @@


# The standard ansi colors
WINDOWS_PALETTE = Palette(
[
(0, 0, 0),
(128, 0, 0),
(0, 128, 0),
(128, 128, 0),
(0, 0, 128),
(128, 0, 128),
(0, 128, 128),
(192, 192, 192),
]
)

# # The standard ansi colors (including bright variants)
STANDARD_PALETTE = Palette(
[
(0, 0, 0),
(255, 0, 0),
(0, 255, 0),
(255, 255, 0),
(0, 0, 255),
(255, 0, 255),
(0, 255, 255),
(170, 0, 0),
(0, 170, 0),
(170, 85, 0),
(0, 0, 170),
(170, 0, 170),
(0, 170, 170),
(170, 170, 170),
(85, 85, 85),
(255, 85, 85),
(85, 255, 85),
(255, 255, 85),
(85, 85, 255),
(255, 85, 255),
(85, 255, 255),
(255, 255, 255),
]
)


# The 256 color palette
EIGHT_BIT_PALETTE = Palette(
[
Expand Down Expand Up @@ -276,3 +299,4 @@
(238, 238, 238),
]
)

30 changes: 19 additions & 11 deletions rich/color.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import platform
import re
from colorsys import rgb_to_hls
from enum import IntEnum
from functools import lru_cache
import platform
from typing import NamedTuple, Optional, Tuple, TYPE_CHECKING
from typing import TYPE_CHECKING, NamedTuple, Optional, Tuple

from ._palettes import STANDARD_PALETTE, EIGHT_BIT_PALETTE
from ._palettes import EIGHT_BIT_PALETTE, STANDARD_PALETTE, WINDOWS_PALETTE
from .color_triplet import ColorTriplet
from .terminal_theme import DEFAULT_TERMINAL_THEME

Expand Down Expand Up @@ -297,10 +297,7 @@ def get_truecolor(
return self.triplet
elif self.type == ColorType.EIGHT_BIT:
assert self.number is not None
if self.number <= 15:
return theme.ansi_colors[self.number]
else:
return EIGHT_BIT_PALETTE[self.number]
return EIGHT_BIT_PALETTE[self.number]
elif self.type == ColorType.STANDARD:
assert self.number is not None
return theme.ansi_colors[self.number]
Expand Down Expand Up @@ -347,7 +344,7 @@ def parse(cls, color: str) -> "Color":
color,
type=(
ColorType.STANDARD
if named_color_number < 8
if named_color_number < 16
else ColorType.EIGHT_BIT
),
number=named_color_number,
Expand All @@ -362,7 +359,11 @@ def parse(cls, color: str) -> "Color":
number = int(color_8)
if number > 255:
raise ColorParseError(f"8bit colors must be <= 255 in {color!r}")
return cls(color, ColorType.EIGHT_BIT, number=number)
return cls(
color,
ColorType.STANDARD if number < 16 else ColorType.EIGHT_BIT,
number=number,
)

elif color_24:
triplet = ColorTriplet(
Expand All @@ -387,11 +388,17 @@ def get_ansi_codes(self, foreground: bool = True) -> Tuple[str, ...]:
if _type == ColorType.DEFAULT:
return ("39" if foreground else "49",)

elif _type in (ColorType.STANDARD, ColorType.WINDOWS):
elif _type == ColorType.WINDOWS:
number = self.number
assert number is not None
return (str(30 + number if foreground else 40 + number),)

elif _type == ColorType.STANDARD:
number = self.number
assert number is not None
fore, back = (30, 40) if number < 8 else (82, 92)
return (str(fore + number if foreground else back + number),)

elif _type == ColorType.EIGHT_BIT:
assert self.number is not None
return ("38" if foreground else "48", "5", str(self.number))
Expand All @@ -410,6 +417,7 @@ def downgrade(self, system: ColorSystem) -> "Color":
# Convert to 8-bit color from truecolor color
if system == ColorSystem.EIGHT_BIT and self.system == ColorSystem.TRUECOLOR:
assert self.triplet is not None

red, green, blue = self.triplet.normalized
_h, l, s = rgb_to_hls(red, green, blue)

Expand Down Expand Up @@ -453,7 +461,7 @@ def downgrade(self, system: ColorSystem) -> "Color":
return Color(self.name, ColorType.WINDOWS, number=self.number - 8)
triplet = ColorTriplet(*EIGHT_BIT_PALETTE[self.number])

color_number = STANDARD_PALETTE.match(triplet)
color_number = WINDOWS_PALETTE.match(triplet)
return Color(self.name, ColorType.WINDOWS, number=color_number)

return self
Expand Down
23 changes: 16 additions & 7 deletions rich/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
</html>
"""

_TERM_COLORS = {"256color": ColorSystem.EIGHT_BIT, "16color": ColorSystem.STANDARD}


@dataclass
class ConsoleOptions:
Expand Down Expand Up @@ -406,11 +408,12 @@ def _detect_color_system(self) -> Optional[ColorSystem]:
)
else:
color_term = self._environ.get("COLORTERM", "").strip().lower()
return (
ColorSystem.TRUECOLOR
if color_term in ("truecolor", "24bit")
else ColorSystem.EIGHT_BIT
)
if color_term in ("truecolor", "24bit"):
return ColorSystem.TRUECOLOR
term = self._environ.get("TERM", "").strip().lower()
_term_name, _hyphen, colors = term.partition("-")
color_system = _TERM_COLORS.get(colors, ColorSystem.STANDARD)
return color_system

def _enter_buffer(self) -> None:
"""Enter in to a buffer context, and buffer all output."""
Expand Down Expand Up @@ -784,6 +787,7 @@ def check_text() -> None:
append_text(_highlighter(str(renderable)))

check_text()

return renderables

def rule(
Expand Down Expand Up @@ -1046,7 +1050,7 @@ def export_text(self, *, clear: bool = True, styles: bool = False) -> str:

Args:
clear (bool, optional): Set to ``True`` to clear the record buffer after exporting.
styles (bool, optional): If ``True``, ansi style codes will be included. ``False`` for plain text.
styles (bool, optional): If ``True``, ansi escape codes will be included. ``False`` for plain text.
Defaults to ``False``.

Returns:
Expand All @@ -1064,7 +1068,11 @@ def export_text(self, *, clear: bool = True, styles: bool = False) -> str:
for text, style, _ in self._record_buffer
)
else:
text = "".join(text for text, _, _ in self._record_buffer)
text = "".join(
segment.text
for segment in self._record_buffer
if not segment.is_control
)
if clear:
del self._record_buffer[:]
return text
Expand Down Expand Up @@ -1196,6 +1204,7 @@ def save_html(

if __name__ == "__main__": # pragma: no cover
console = Console()

console.log(
"JSONRPC [i]request[/i]",
5,
Expand Down
6 changes: 4 additions & 2 deletions rich/markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def __rich_console__(
else:
# Styled text for h2 and beyond
if self.level == 2:
yield Text("\n")
yield Text("")
yield text


Expand All @@ -180,7 +180,9 @@ def __rich_console__(
) -> RenderResult:
code = str(self.text).rstrip()
syntax = Panel(
Syntax(code, self.lexer_name, theme=self.theme), style="dim", box=box.SQUARE
Syntax(code, self.lexer_name, theme=self.theme),
border_style="dim",
box=box.SQUARE,
)
yield syntax

Expand Down
8 changes: 3 additions & 5 deletions rich/palette.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ def match(self, color: Tuple[int, int, int]) -> int:
red1, green1, blue1 = color
_sqrt = sqrt

def get_color_distance(color: Tuple[int, int, int]) -> float:
def get_color_distance(index: int) -> float:
"""Get the distance to a color."""
red2, green2, blue2 = color
red2, green2, blue2 = self._colors[index]
red_mean = int((red1 + red2) / 2)
red = red1 - red2
green = green1 - green2
Expand All @@ -41,7 +41,5 @@ def get_color_distance(color: Tuple[int, int, int]) -> float:
+ (((767 - red_mean) * blue * blue) >> 8)
)

min_index, _min_color = min(
enumerate(self._colors), key=lambda _color: get_color_distance(_color[1]),
)
min_index = min(range(len(self._colors)), key=get_color_distance)
return min_index
13 changes: 8 additions & 5 deletions rich/progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@


def iter_track(
values: Iterable[ProgressType], total: int, update_period: float = 0.05
values: Iterable[ProgressType],
total: int,
update_period: float = 0.05,
get_time: Callable[[], float] = None,
) -> Iterable[Iterable[ProgressType]]:
"""Break a sequence in to chunks based on time.

Expand All @@ -71,7 +74,7 @@ def iter_track(
yield [value]
return

get_time = perf_counter
get_time = get_time or perf_counter
period_size = 1.0

def gen_values(
Expand All @@ -87,7 +90,7 @@ def gen_values(
value_count = 0

while value_count < total:
_count = int(period_size)
_count = min(int(period_size), total - value_count)
start_time = get_time()
yield gen_values(iter_values, _count)
time_taken = get_time() - start_time
Expand Down Expand Up @@ -649,7 +652,7 @@ def stop(self) -> None:
self._refresh_thread = None
if self.transient:
self.console.control(self._live_render.restore_cursor())
if self.ipy_widget is not None and self.transient:
if self.ipy_widget is not None and self.transient: # pragma: no cover
self.ipy_widget.clear_output()
self.ipy_widget.close()

Expand Down Expand Up @@ -707,7 +710,7 @@ def track(
advance_total += 1
advance(task_id, advance_total)
if not self.auto_refresh:
progress.refresh()
self.refresh()

def start_task(self, task_id: TaskID) -> None:
"""Start a task.
Expand Down
2 changes: 2 additions & 0 deletions rich/syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ def __rich_console__(
if self.dedent:
code = textwrap.dedent(code)
text = self._highlight(self.lexer_name)
if text.plain.endswith("\n"):
text.plain = text.plain[:-1]
if not self.line_numbers:
if self.code_width is None:
yield text
Expand Down