Skip to content

Commit

Permalink
Merge branch 'master' into highlight_regex_compiled
Browse files Browse the repository at this point in the history
  • Loading branch information
willmcgugan authored Sep 30, 2024
2 parents 03e574b + 68ead31 commit b5d063c
Show file tree
Hide file tree
Showing 29 changed files with 359 additions and 82 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
include:
- { os: ubuntu-latest, python-version: "3.7" }
- { os: windows-latest, python-version: "3.7" }
- { os: macos-12, python-version: "3.7" }
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
exclude:
- { os: windows-latest, python-version: "3.13" }
defaults:
run:
shell: bash
Expand All @@ -22,6 +21,7 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Install and configure Poetry
# TODO: workaround for https://github.com/snok/install-poetry/issues/94
uses: snok/[email protected]
Expand Down
36 changes: 35 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,32 @@ 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).


## Unreleased


### Changed

- Rich will display tracebacks with finely grained error locations on python 3.11+ https://github.com/Textualize/rich/pull/3486


### Fixed

- Fixed issue with Segment._split_cells https://github.com/Textualize/rich/pull/3506
- Fix auto detection of terminal size on Windows https://github.com/Textualize/rich/pull/2916

### Added

- Add a new `column` object `IterationSpeedColumn`. https://github.com/Textualize/rich/pull/3332

## [13.8.1] - 2024-09-10

### Fixed

- Added support for Python 3.13 https://github.com/Textualize/rich/pull/3481
- Fixed infinite loop when appending Text to same instance https://github.com/Textualize/rich/pull/3480

## [13.8.0] - 2024-08-26

### Fixed

- Fixed `Table` rendering of box elements so "footer" elements truly appear at bottom of table, "mid" elements in main table body.
Expand All @@ -17,8 +40,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Progress track thread is now a daemon thread https://github.com/Textualize/rich/pull/3402
- Fixed cached hash preservation upon clearing meta and links https://github.com/Textualize/rich/issues/2942
- Fixed overriding the `background_color` of `Syntax` not including padding https://github.com/Textualize/rich/issues/3295
- Fixed pretty printing of dataclasses with a default repr in Python 3.13 https://github.com/Textualize/rich/pull/3455
- Fixed selective enabling of highlighting when disabled in the `Console` https://github.com/Textualize/rich/issues/3419
- Fixed BrokenPipeError writing an error message https://github.com/Textualize/rich/pull/3468
- Fixed superfluous space above Markdown tables https://github.com/Textualize/rich/pull/3469
- Fixed issue with record and capture interaction https://github.com/Textualize/rich/pull/3470
- Fixed control codes breaking in `append_tokens` https://github.com/Textualize/rich/pull/3471
- Fixed exception pretty printing a dataclass with missing fields https://github.com/Textualize/rich/pull/3472

### Changed

Expand All @@ -42,6 +70,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Updated the widths of some characters https://github.com/Textualize/rich/pull/3289

### Added

- Included a `name` attribute to the `Spinner` class https://github.com/Textualize/rich/pull/3359

## [13.7.0] - 2023-11-15

### Added
Expand Down Expand Up @@ -2056,6 +2088,8 @@ Major version bump for a breaking change to `Text.stylize signature`, which corr

- First official release, API still to be stabilized

[13.8.1]: https://github.com/textualize/rich/compare/v13.8.0...v13.8.1
[13.8.0]: https://github.com/textualize/rich/compare/v13.7.1...v13.8.0
[13.7.1]: https://github.com/textualize/rich/compare/v13.7.0...v13.7.1
[13.7.0]: https://github.com/textualize/rich/compare/v13.6.0...v13.7.0
[13.6.0]: https://github.com/textualize/rich/compare/v13.5.3...v13.6.0
Expand Down
5 changes: 5 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The following people have contributed to the development of Rich:
- [Aryaz Eghbali](https://github.com/AryazE)
- [Oleksis Fraga](https://github.com/oleksis)
- [Andy Gimblett](https://github.com/gimbo)
- [Kai Giokas](https://github.com/kaisforza)
- [Tom Gooding](https://github.com/TomJGooding)
- [Michał Górny](https://github.com/mgorny)
- [Nok Lam Chan](https://github.com/noklam)
Expand Down Expand Up @@ -63,6 +64,7 @@ The following people have contributed to the development of Rich:
- [Tushar Sadhwani](https://github.com/tusharsadhwani)
- [Luca Salvarani](https://github.com/LukeSavefrogs)
- [Paul Sanders](https://github.com/sanders41)
- [Louis Sautier](https://github.com/sbraz)
- [Tim Savage](https://github.com/timsavage)
- [Anthony Shaw](https://github.com/tonybaloney)
- [Nicolas Simonds](https://github.com/0xDEC0DE)
Expand All @@ -86,3 +88,6 @@ The following people have contributed to the development of Rich:
- [Bernhard Wagner](https://github.com/bwagner)
- [Aaron Beaudoin](https://github.com/AaronBeaudoin)
- [Sam Woodward](https://github.com/PyWoody)
- [L. Yeung](https://github.com/lewis-yeung)
- [chthollyphile](https://github.com/chthollyphile)
- [Jonathan Helmus](https://github.com/jjhelmus)
2 changes: 1 addition & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
alabaster==0.7.12
alabaster==1.0.0
Sphinx==7.3.7
sphinx-rtd-theme==2.0.0
sphinx-copybutton==0.5.1
Expand Down
3 changes: 2 additions & 1 deletion docs/source/progress.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ Columns

You may customize the columns in the progress display with the positional arguments to the :class:`~rich.progress.Progress` constructor. The columns are specified as either a `format string <https://docs.python.org/3/library/string.html#formatspec>`_ or a :class:`~rich.progress.ProgressColumn` object.

Format strings will be rendered with a single value `"task"` which will be a :class:`~rich.progress.Task` instance. For example ``"{task.description}"`` would display the task description in the column, and ``"{task.completed} of {task.total}"`` would display how many of the total steps have been completed. Additional fields passed via keyword arguments to `~rich.progress.Progress.update` are store in ``task.fields``. You can add them to a format string with the following syntax: ``"extra info: {task.fields[extra]}"``.
Format strings will be rendered with a single value `"task"` which will be a :class:`~rich.progress.Task` instance. For example ``"{task.description}"`` would display the task description in the column, and ``"{task.completed} of {task.total}"`` would display how many of the total steps have been completed. Additional fields passed via keyword arguments to `~rich.progress.Progress.update` are stored in ``task.fields``. You can add them to a format string with the following syntax: ``"extra info: {task.fields[extra]}"``.

The default columns are equivalent to the following::

Expand Down Expand Up @@ -163,6 +163,7 @@ The following column objects are available:
- :class:`~rich.progress.TransferSpeedColumn` Displays transfer speed (assumes the steps are bytes).
- :class:`~rich.progress.SpinnerColumn` Displays a "spinner" animation.
- :class:`~rich.progress.RenderableColumn` Displays an arbitrary Rich renderable in the column.
- :class:`~rich.progress.IterationSpeedColumn` Displays iteration speed in it/s (iterations per second).

To implement your own columns, extend the :class:`~rich.progress.ProgressColumn` class and use it as you would the other columns.

Expand Down
3 changes: 2 additions & 1 deletion examples/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ def download(urls: Iterable[str], dest_dir: str):


if __name__ == "__main__":
# Try with https://releases.ubuntu.com/20.04/ubuntu-20.04.3-desktop-amd64.iso
# Try with https://releases.ubuntu.com/noble/ubuntu-24.04-desktop-amd64.iso
# and https://releases.ubuntu.com/noble/ubuntu-24.04-live-server-amd64.iso
if sys.argv[1:]:
download(sys.argv[1:], "./")
else:
Expand Down
6 changes: 3 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 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/Textualize/rich"
documentation = "https://rich.readthedocs.io/en/latest/"
version = "13.7.1"
version = "13.8.1"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
authors = ["Will McGugan <[email protected]>"]
license = "MIT"
Expand All @@ -21,6 +21,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Typing :: Typed",
]
include = ["rich/py.typed"]
Expand Down
16 changes: 5 additions & 11 deletions rich/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -1005,19 +1005,13 @@ def size(self) -> ConsoleDimensions:
width: Optional[int] = None
height: Optional[int] = None

if WINDOWS: # pragma: no cover
for file_descriptor in _STD_STREAMS_OUTPUT if WINDOWS else _STD_STREAMS:
try:
width, height = os.get_terminal_size()
width, height = os.get_terminal_size(file_descriptor)
except (AttributeError, ValueError, OSError): # Probably not a terminal
pass
else:
for file_descriptor in _STD_STREAMS:
try:
width, height = os.get_terminal_size(file_descriptor)
except (AttributeError, ValueError, OSError):
pass
else:
break
else:
break

columns = self._environ.get("COLUMNS")
if columns is not None and columns.isdigit():
Expand Down Expand Up @@ -2029,7 +2023,7 @@ def _write_buffer(self) -> None:
"""Write the buffer to the output file."""

with self._lock:
if self.record:
if self.record and not self._buffer_index:
with self._record_buffer_lock:
self._record_buffer.extend(self._buffer[:])

Expand Down
1 change: 1 addition & 0 deletions rich/default_styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
"traceback.exc_type": Style(color="bright_red", bold=True),
"traceback.exc_value": Style.null(),
"traceback.offset": Style(color="bright_red", bold=True),
"traceback.error_range": Style(underline=True, bold=True, dim=False),
"bar.back": Style(color="grey23"),
"bar.complete": Style(color="rgb(249,38,114)"),
"bar.finished": Style(color="rgb(114,156,31)"),
Expand Down
2 changes: 1 addition & 1 deletion rich/highlighter.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class ReprHighlighter(RegexHighlighter):
r"(?P<number>(?<!\w)\-?[0-9]+\.?[0-9]*(e[-+]?\d+?)?\b|0x[0-9a-fA-F]*)",
r"(?P<path>\B(/[-\w._+]+)*\/)(?P<filename>[-\w._+]*)?",
r"(?<![\\\w])(?P<str>b?'''.*?(?<!\\)'''|b?'.*?(?<!\\)'|b?\"\"\".*?(?<!\\)\"\"\"|b?\".*?(?<!\\)\")",
r"(?P<url>(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#~]*)",
r"(?P<url>(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#~@]*)",
),
]

Expand Down
2 changes: 1 addition & 1 deletion rich/live.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class Live(JupyterMixin, RenderHook):
Args:
renderable (RenderableType, optional): The renderable to live display. Defaults to displaying nothing.
console (Console, optional): Optional Console instance. Default will an internal Console instance writing to stdout.
console (Console, optional): Optional Console instance. Defaults to an internal Console instance writing to stdout.
screen (bool, optional): Enable alternate screen mode. Defaults to False.
auto_refresh (bool, optional): Enable auto refresh. If disabled, you will need to call `refresh()` or `update()` with refresh flag. Defaults to True
refresh_per_second (float, optional): Number of times per second to refresh the live display. Defaults to 4.
Expand Down
2 changes: 1 addition & 1 deletion rich/markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ def __rich_console__(
and context.stack.top.on_child_close(context, element)
)
if should_render:
if new_line:
if new_line and node_type != "inline":
yield _new_line_segment
yield from console.render(element, context.options)

Expand Down
10 changes: 8 additions & 2 deletions rich/pretty.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dataclasses
import inspect
import os
import reprlib
import sys
from array import array
from collections import Counter, UserDict, UserList, defaultdict, deque
Expand Down Expand Up @@ -78,7 +79,10 @@ def _is_dataclass_repr(obj: object) -> bool:
# Digging in to a lot of internals here
# Catching all exceptions in case something is missing on a non CPython implementation
try:
return obj.__repr__.__code__.co_filename == dataclasses.__file__
return obj.__repr__.__code__.co_filename in (
dataclasses.__file__,
reprlib.__file__,
)
except Exception: # pragma: no coverage
return False

Expand Down Expand Up @@ -777,7 +781,9 @@ def iter_attrs() -> (
)

for last, field in loop_last(
field for field in fields(obj) if field.repr
field
for field in fields(obj)
if field.repr and hasattr(obj, field.name)
):
child_node = _traverse(getattr(obj, field.name), depth=depth + 1)
child_node.key_repr = field.name
Expand Down
28 changes: 26 additions & 2 deletions rich/progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
else:
from typing_extensions import Literal # pragma: no cover

if sys.version_info >= (3, 11):
from typing import Self
else:
from typing_extensions import Self # pragma: no cover

from . import filesize, get_console
from .console import Console, Group, JustifyMethod, RenderableType
from .highlighter import Highlighter
Expand Down Expand Up @@ -917,6 +922,25 @@ def render(self, task: "Task") -> Text:
return Text(f"{data_speed}/s", style="progress.data.speed")


class IterationSpeedColumn(ProgressColumn):
"""Renders iterations per second, e.g. '11.4 it/s'."""

def render(self, task: "Task") -> Text:
last_speed = task.last_speed if hasattr(task, 'last_speed') else None
if task.finished and last_speed is not None:
return Text(f"{last_speed} it/s", style="progress.data.speed")
if task.speed is None:
return Text("", style="progress.data.speed")
unit, suffix = filesize.pick_unit_and_suffix(
int(task.speed),
["", "×10³", "×10⁶", "×10⁹", "×10¹²"],
1000,
)
data_speed = task.speed / unit
task.last_speed = f"{data_speed:.1f}{suffix}"
return Text(f"{task.last_speed} it/s", style="progress.data.speed")


class ProgressSample(NamedTuple):
"""Sample of progress for a given time."""

Expand Down Expand Up @@ -1056,7 +1080,7 @@ class Progress(JupyterMixin):
"""Renders an auto-updating progress bar(s).
Args:
console (Console, optional): Optional Console instance. Default will an internal Console instance writing to stdout.
console (Console, optional): Optional Console instance. Defaults to an internal Console instance writing to stdout.
auto_refresh (bool, optional): Enable auto refresh. If disabled, you will need to call `refresh()`.
refresh_per_second (Optional[float], optional): Number of times per second to refresh the progress information or None to use default (10). Defaults to None.
speed_estimate_period: (float, optional): Period (in seconds) used to calculate the speed estimate. Defaults to 30.
Expand Down Expand Up @@ -1170,7 +1194,7 @@ def stop(self) -> None:
if not self.console.is_interactive and not self.console.is_jupyter:
self.console.print()

def __enter__(self) -> "Progress":
def __enter__(self) -> Self:
self.start()
return self

Expand Down
17 changes: 15 additions & 2 deletions rich/segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,29 @@ def is_control(self) -> bool:
@classmethod
@lru_cache(1024 * 16)
def _split_cells(cls, segment: "Segment", cut: int) -> Tuple["Segment", "Segment"]:
"""Split a segment in to two at a given cell position.
Note that splitting a double-width character, may result in that character turning
into two spaces.
Args:
segment (Segment): A segment to split.
cut (int): A cell position to cut on.
Returns:
A tuple of two segments.
"""
text, style, control = segment
_Segment = Segment

cell_length = segment.cell_length
if cut >= cell_length:
return segment, _Segment("", style, control)

cell_size = get_character_cell_size

pos = int((cut / cell_length) * (len(text) - 1))
pos = int((cut / cell_length) * (len(text))) - 1
if pos < 0:
pos = 0

before = text[:pos]
cell_pos = cell_len(before)
Expand Down
1 change: 1 addition & 0 deletions rich/spinner.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def __init__(
self.text: "Union[RenderableType, Text]" = (
Text.from_markup(text) if isinstance(text, str) else text
)
self.name = name
self.frames = cast(List[str], spinner["frames"])[:]
self.interval = cast(float, spinner["interval"])
self.start_time: Optional[float] = None
Expand Down
Loading

0 comments on commit b5d063c

Please sign in to comment.