Control: tags -1 patch pending Dear maintainer,
I've prepared an NMU for rich (versioned as 13.9.4-1.1) and uploaded it to DELAYED/2. Please feel free to tell me if I should cancel it.
I'm attaching the diff for form's sake, but the aforementioned merge request (https://salsa.debian.org/morph/rich/-/merge_requests/1) is probably more useful.
Thanks, -- Colin Watson (he/him) [[email protected]]
diffstat for rich-13.9.4 rich-13.9.4 changelog | 8 ++ control | 2 patches/py314.patch | 146 ++++++++++++++++++++++++++++++++++++++++++++ patches/pygments-2.19.patch | 46 +++++++++++++ patches/series | 2 5 files changed, 203 insertions(+), 1 deletion(-) diff -Nru rich-13.9.4/debian/changelog rich-13.9.4/debian/changelog --- rich-13.9.4/debian/changelog 2024-12-24 06:22:19.000000000 +0000 +++ rich-13.9.4/debian/changelog 2026-01-09 14:44:48.000000000 +0000 @@ -1,3 +1,11 @@ +rich (13.9.4-1.1) unstable; urgency=medium + + * Non-maintainer upload. + * Fix tests for Python 3.14 (closes: #1123335). + * Fix tests for Pygments 2.19. + + -- Colin Watson <[email protected]> Fri, 09 Jan 2026 14:44:48 +0000 + rich (13.9.4-1) unstable; urgency=medium * New upstream release; Closes: #1082290 diff -Nru rich-13.9.4/debian/control rich-13.9.4/debian/control --- rich-13.9.4/debian/control 2024-12-24 06:22:19.000000000 +0000 +++ rich-13.9.4/debian/control 2026-01-09 14:44:48.000000000 +0000 @@ -9,7 +9,7 @@ python3-attr <!nocheck>, python3-mypy (>= 0.782), python3-poetry-core, - python3-pygments (>= 2.14.0), + python3-pygments (>= 2.19.0), python3-pytest, python3-setuptools, python3-typing-extensions (>= 3.7.4), diff -Nru rich-13.9.4/debian/patches/py314.patch rich-13.9.4/debian/patches/py314.patch --- rich-13.9.4/debian/patches/py314.patch 1970-01-01 01:00:00.000000000 +0100 +++ rich-13.9.4/debian/patches/py314.patch 2026-01-09 14:44:48.000000000 +0000 @@ -0,0 +1,146 @@ +From: Will McGugan <[email protected]> +Date: Thu, 9 Oct 2025 09:35:09 +0100 +Subject: Fix tests for Python 3.14 + +Origin: upstream, https://github.com/Textualize/rich/pull/3861/commits/655b5210cb1403100a646d167cf027eec760dd9f +Bug-Debian: https://bugs.debian.org/1123335 +Last-Update: 2026-01-07 +--- + rich/style.py | 18 +++++++----------- + tests/test_inspect.py | 10 ++++++++++ + tests/test_pretty.py | 5 +++++ + tests/test_text.py | 7 ++++++- + 4 files changed, 28 insertions(+), 12 deletions(-) + +diff --git a/rich/style.py b/rich/style.py +index 262fd6e..0912542 100644 +--- a/rich/style.py ++++ b/rich/style.py +@@ -1,6 +1,7 @@ + import sys + from functools import lru_cache +-from marshal import dumps, loads ++from operator import attrgetter ++from pickle import dumps, loads + from random import randint + from typing import Any, Dict, Iterable, List, Optional, Type, Union, cast + +@@ -9,6 +10,10 @@ from .color import Color, ColorParseError, ColorSystem, blend_rgb + from .repr import Result, rich_repr + from .terminal_theme import DEFAULT_TERMINAL_THEME, TerminalTheme + ++_hash_getter = attrgetter( ++ "_color", "_bgcolor", "_attributes", "_set_attributes", "_link", "_meta" ++) ++ + # Style instances and style definitions are often interchangeable + StyleType = Union[str, "Style"] + +@@ -432,16 +437,7 @@ class Style: + def __hash__(self) -> int: + if self._hash is not None: + return self._hash +- self._hash = hash( +- ( +- self._color, +- self._bgcolor, +- self._attributes, +- self._set_attributes, +- self._link, +- self._meta, +- ) +- ) ++ self._hash = hash(_hash_getter(self)) + return self._hash + + @property +diff --git a/tests/test_inspect.py b/tests/test_inspect.py +index 130e8df..3f6e5b9 100644 +--- a/tests/test_inspect.py ++++ b/tests/test_inspect.py +@@ -43,6 +43,12 @@ skip_py313 = pytest.mark.skipif( + reason="rendered differently on py3.13", + ) + ++skip_py314 = pytest.mark.skipif( ++ sys.version_info.minor == 14 and sys.version_info.major == 3, ++ reason="rendered differently on py3.14", ++) ++ ++ + skip_pypy3 = pytest.mark.skipif( + hasattr(sys, "pypy_version_info"), + reason="rendered differently on pypy3", +@@ -139,6 +145,7 @@ def test_inspect_empty_dict(): + assert render({}).startswith(expected) + + ++@skip_py314 + @skip_py313 + @skip_py312 + @skip_py311 +@@ -219,6 +226,7 @@ def test_inspect_integer_with_value(): + @skip_py311 + @skip_py312 + @skip_py313 ++@skip_py314 + def test_inspect_integer_with_methods_python38_and_python39(): + expected = ( + "╭──────────────── <class 'int'> ─────────────────╮\n" +@@ -257,6 +265,7 @@ def test_inspect_integer_with_methods_python38_and_python39(): + @skip_py311 + @skip_py312 + @skip_py313 ++@skip_py314 + def test_inspect_integer_with_methods_python310only(): + expected = ( + "╭──────────────── <class 'int'> ─────────────────╮\n" +@@ -299,6 +308,7 @@ def test_inspect_integer_with_methods_python310only(): + @skip_py310 + @skip_py312 + @skip_py313 ++@skip_py314 + def test_inspect_integer_with_methods_python311(): + # to_bytes and from_bytes methods on int had minor signature change - + # they now, as of 3.11, have default values for all of their parameters +diff --git a/tests/test_pretty.py b/tests/test_pretty.py +index 90be42f..29331d9 100644 +--- a/tests/test_pretty.py ++++ b/tests/test_pretty.py +@@ -38,6 +38,10 @@ skip_py313 = pytest.mark.skipif( + sys.version_info.minor == 13 and sys.version_info.major == 3, + reason="rendered differently on py3.13", + ) ++skip_py314 = pytest.mark.skipif( ++ sys.version_info.minor == 14 and sys.version_info.major == 3, ++ reason="rendered differently on py3.14", ++) + + + def test_install() -> None: +@@ -639,6 +643,7 @@ def test_attrs_empty() -> None: + @skip_py311 + @skip_py312 + @skip_py313 ++@skip_py314 + def test_attrs_broken() -> None: + @attr.define + class Foo: +diff --git a/tests/test_text.py b/tests/test_text.py +index fee7302..9258033 100644 +--- a/tests/test_text.py ++++ b/tests/test_text.py +@@ -843,7 +843,12 @@ def test_assemble(): + def test_assemble_meta(): + text = Text.assemble("foo", ("bar", "bold"), meta={"foo": "bar"}) + assert str(text) == "foobar" +- assert text._spans == [Span(3, 6, "bold"), Span(0, 6, Style(meta={"foo": "bar"}))] ++ ++ spans = text._spans ++ expected = [Span(3, 6, "bold"), Span(0, 6, Style(meta={"foo": "bar"}))] ++ ++ assert spans == expected ++ + console = Console() + assert text.get_style_at_offset(console, 0).meta == {"foo": "bar"} + diff -Nru rich-13.9.4/debian/patches/pygments-2.19.patch rich-13.9.4/debian/patches/pygments-2.19.patch --- rich-13.9.4/debian/patches/pygments-2.19.patch 1970-01-01 01:00:00.000000000 +0100 +++ rich-13.9.4/debian/patches/pygments-2.19.patch 2026-01-09 14:44:48.000000000 +0000 @@ -0,0 +1,46 @@ +From: Will McGugan <[email protected]> +Date: Wed, 18 Jun 2025 08:29:31 +0100 +Subject: Fix tests for Pygments 2.19 + +Origin: backport, https://github.com/Textualize/rich/commit/82afcb4ff58db4d7628062a8a695de3bc64febab +Last-Update: 2026-01-07 +--- + tests/test_markdown.py | 2 +- + tests/test_syntax.py | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tests/test_markdown.py b/tests/test_markdown.py +index 803c1ae..2ffbdd9 100644 +--- a/tests/test_markdown.py ++++ b/tests/test_markdown.py +@@ -110,7 +110,7 @@ def test_inline_code(): + inline_code_theme="emacs", + ) + result = render(markdown) +- expected = "inline \x1b[1;38;2;170;34;255;48;2;248;248;248mimport\x1b[0m\x1b[38;2;0;0;0;48;2;248;248;248m \x1b[0m\x1b[1;38;2;0;0;255;48;2;248;248;248mthis\x1b[0m code \n" ++ expected = "inline \x1b[1;38;2;170;34;255;48;2;248;248;248mimport\x1b[0m\x1b[38;2;187;187;187;48;2;248;248;248m \x1b[0m\x1b[1;38;2;0;0;255;48;2;248;248;248mthis\x1b[0m code \n" + print(result) + print(repr(result)) + assert result == expected +diff --git a/tests/test_syntax.py b/tests/test_syntax.py +index bbd4c7a..64288b3 100644 +--- a/tests/test_syntax.py ++++ b/tests/test_syntax.py +@@ -53,7 +53,7 @@ def test_blank_lines(): + print(repr(result)) + assert ( + result +- == "\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m1 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m2 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m3 \x1b[0m\x1b[1;38;2;0;128;0;48;2;248;248;248mimport\x1b[0m\x1b[38;2;0;0;0;48;2;248;248;248m \x1b[0m\x1b[1;38;2;0;0;255;48;2;248;248;248mthis\x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m4 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m5 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n" ++ == "\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m1 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m2 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m3 \x1b[0m\x1b[1;38;2;0;128;0;48;2;248;248;248mimport\x1b[0m\x1b[38;2;187;187;187;48;2;248;248;248m \x1b[0m\x1b[1;38;2;0;0;255;48;2;248;248;248mthis\x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m4 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m5 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n" + ) + + +@@ -119,7 +119,7 @@ def test_python_render_simple_indent_guides(): + ) + rendered_syntax = render(syntax) + print(repr(rendered_syntax)) +- expected = '\x1b[34mdef\x1b[0m \x1b[32mloop_first_last\x1b[0m(values: Iterable[T]) -> Iterable[Tuple[\x1b[36mb\x1b[0m\n\x1b[2;37m│ \x1b[0m\x1b[33m"""Iterate and generate a tuple with a flag for first an\x1b[0m\n\x1b[2m│ \x1b[0miter_values = \x1b[36miter\x1b[0m(values)\n\x1b[2m│ \x1b[0m\x1b[34mtry\x1b[0m:\n\x1b[2m│ │ \x1b[0mprevious_value = \x1b[36mnext\x1b[0m(iter_values)\n\x1b[2m│ \x1b[0m\x1b[34mexcept\x1b[0m \x1b[36mStopIteration\x1b[0m:\n\x1b[2m│ │ \x1b[0m\x1b[34mreturn\x1b[0m\n\x1b[2m│ \x1b[0mfirst = \x1b[34mTrue\x1b[0m\n\x1b[2m│ \x1b[0m\x1b[34mfor\x1b[0m value \x1b[35min\x1b[0m iter_values:\n\x1b[2m│ │ \x1b[0m\x1b[34myield\x1b[0m first, \x1b[34mFalse\x1b[0m, previous_value\n\x1b[2m│ │ \x1b[0mfirst = \x1b[34mFalse\x1b[0m\n\x1b[2m│ │ \x1b[0mprevious_value = value\n\x1b[2m│ \x1b[0m\x1b[34myield\x1b[0m first, \x1b[34mTrue\x1b[0m, previous_value\n' ++ expected = '\x1b[34mdef\x1b[0m\x1b[37m \x1b[0m\x1b[32mloop_first_last\x1b[0m(values: Iterable[T]) -> Iterable[Tuple[\x1b[36mb\x1b[0m\n\x1b[2;37m│ \x1b[0m\x1b[33m"""Iterate and generate a tuple with a flag for first an\x1b[0m\n\x1b[2m│ \x1b[0miter_values = \x1b[36miter\x1b[0m(values)\n\x1b[2m│ \x1b[0m\x1b[34mtry\x1b[0m:\n\x1b[2m│ │ \x1b[0mprevious_value = \x1b[36mnext\x1b[0m(iter_values)\n\x1b[2m│ \x1b[0m\x1b[34mexcept\x1b[0m \x1b[36mStopIteration\x1b[0m:\n\x1b[2m│ │ \x1b[0m\x1b[34mreturn\x1b[0m\n\x1b[2m│ \x1b[0mfirst = \x1b[34mTrue\x1b[0m\n\x1b[2m│ \x1b[0m\x1b[34mfor\x1b[0m value \x1b[35min\x1b[0m iter_values:\n\x1b[2m│ │ \x1b[0m\x1b[34myield\x1b[0m first, \x1b[34mFalse\x1b[0m, previous_value\n\x1b[2m│ │ \x1b[0mfirst = \x1b[34mFalse\x1b[0m\n\x1b[2m│ │ \x1b[0mprevious_value = value\n\x1b[2m│ \x1b[0m\x1b[34myield\x1b[0m first, \x1b[34mTrue\x1b[0m, previous_value\n' + assert rendered_syntax == expected + + diff -Nru rich-13.9.4/debian/patches/series rich-13.9.4/debian/patches/series --- rich-13.9.4/debian/patches/series 1970-01-01 01:00:00.000000000 +0100 +++ rich-13.9.4/debian/patches/series 2026-01-09 14:44:48.000000000 +0000 @@ -0,0 +1,2 @@ +py314.patch +pygments-2.19.patch

