Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
exclude:
- { os: windows-latest, python-version: "3.13" }
defaults:
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ 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).

## [14.2.0] - 2025-10-09

### Changed

- Python3.14 compatibility https://github.com/Textualize/rich/pull/3861

## [14.1.0] - 2025-06-25

Expand Down
379 changes: 226 additions & 153 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions 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 = "14.1.0"
version = "14.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 All @@ -21,6 +21,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Typing :: Typed",
]
include = ["rich/py.typed"]
Expand All @@ -42,7 +43,6 @@ mypy = "^1.11"
pytest-cov = "^3.0.0"
attrs = "^21.4.0"
pre-commit = "^2.17.0"
asv = "^0.5.1"
typing-extensions = ">=4.0.0, <5.0"

[build-system]
Expand Down
18 changes: 7 additions & 11 deletions rich/style.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -9,6 +10,10 @@
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"]

Expand Down Expand Up @@ -432,16 +437,7 @@ def __ne__(self, other: Any) -> bool:
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
Expand Down
10 changes: 10 additions & 0 deletions tests/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
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",
Expand Down Expand Up @@ -139,6 +145,7 @@ def test_inspect_empty_dict():
assert render({}).startswith(expected)


@skip_py314
@skip_py313
@skip_py312
@skip_py311
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions tests/test_pretty.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
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:
Expand Down Expand Up @@ -639,6 +643,7 @@ class Nada:
@skip_py311
@skip_py312
@skip_py313
@skip_py314
def test_attrs_broken() -> None:
@attr.define
class Foo:
Expand Down
7 changes: 6 additions & 1 deletion tests/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"}

Expand Down
Loading