Skip to content
Open
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ 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

### Fixed

- Fixed `inspect()` not respecting `sort=False` parameter for instance attributes https://github.com/Textualize/rich/issues/3432

## [14.2.0] - 2025-10-09

### Changed
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,5 @@ The following people have contributed to the development of Rich:
- [chthollyphile](https://github.com/chthollyphile)
- [Jonathan Helmus](https://github.com/jjhelmus)
- [Brandon Capener](https://github.com/bcapener)
- [Nab](https://github.com/Nabeelshar)
- [Alex Zheng](https://github.com/alexzheng111)
10 changes: 10 additions & 0 deletions rich/_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,16 @@ def safe_getattr(attr_name: str) -> Tuple[Any, Any]:
obj = self.obj
keys = dir(obj)
total_items = len(keys)

# When sort=False, prefer __dict__ order for instance attributes
if not self.sort and hasattr(obj, "__dict__"):
# Get instance attributes in insertion order
instance_keys = list(vars(obj).keys())
# Get remaining attributes from dir() that aren't in __dict__
class_keys = [key for key in keys if key not in instance_keys]
# Combine: instance attributes first (in order), then class attributes
keys = instance_keys + class_keys

if not self.dunder:
keys = [key for key in keys if not key.startswith("__")]
if not self.private:
Expand Down
34 changes: 34 additions & 0 deletions tests/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,3 +501,37 @@ def test_object_is_one_of_types(
obj: object, types: Sequence[str], expected_result: bool
):
assert is_object_one_of_types(obj, types) is expected_result


def test_inspect_sort_parameter():
"""Test that sort=False preserves insertion order of instance attributes."""

class Test:
def __init__(self):
self.c = 1
self.b = 2
self.a = 3

test = Test()

# Test sort=False preserves insertion order (c, b, a)
console = Console(width=50, file=io.StringIO(), legacy_windows=False)
inspect(test, console=console, sort=False, value=False, docs=False)
result_unsorted = console.file.getvalue()

# Check that 'c' appears before 'b' and 'b' appears before 'a'
pos_c = result_unsorted.find("c = 1")
pos_b = result_unsorted.find("b = 2")
pos_a = result_unsorted.find("a = 3")
assert pos_c < pos_b < pos_a, "sort=False should preserve insertion order"

# Test sort=True sorts alphabetically (a, b, c)
console = Console(width=50, file=io.StringIO(), legacy_windows=False)
inspect(test, console=console, sort=True, value=False, docs=False)
result_sorted = console.file.getvalue()

# Check that 'a' appears before 'b' and 'b' appears before 'c'
pos_a = result_sorted.find("a = 3")
pos_b = result_sorted.find("b = 2")
pos_c = result_sorted.find("c = 1")
assert pos_a < pos_b < pos_c, "sort=True should sort alphabetically"