Skip to content

Commit

Permalink
Merge branch 'master' into markdown-code
Browse files Browse the repository at this point in the history
  • Loading branch information
willmcgugan committed Sep 20, 2022
2 parents 5e35957 + 73a285c commit 9fc01f1
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 35 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Handle stdout/stderr being null https://github.com/Textualize/rich/pull/2513
- Fix NO_COLOR support on legacy Windows https://github.com/Textualize/rich/pull/2458
- Fix pretty printer handling of cyclic references https://github.com/Textualize/rich/pull/2524
- Fix missing `mode` property on file wrapper breaking uploads via `requests` https://github.com/Textualize/rich/pull/2495

### Changed
Expand All @@ -36,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Fixed missing typing extensions dependency on 3.9 https://github.com/Textualize/rich/issues/2386
- Fixed Databricks Notebook is not detected as Jupyter environment. https://github.com/Textualize/rich/issues/2422

## [12.5.0] - 2022-07-11

Expand Down
3 changes: 3 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ The following people have contributed to the development of Rich:
- [Gregory Beauregard](https://github.com/GBeauregard/pyffstream)
- [Dennis Brakhane](https://github.com/brakhane)
- [Darren Burns](https://github.com/darrenburns)
- [Jim Crist-Harif](https://github.com/jcrist)
- [Ed Davis](https://github.com/davised)
- [Pete Davison](https://github.com/pd93)
- [James Estevez](https://github.com/jstvz)
- [Oleksis Fraga](https://github.com/oleksis)
- [Andy Gimblett](https://github.com/gimbo)
- [Michał Górny](https://github.com/mgorny)
- [Nok Lam Chan](https://github.com/noklam)
- [Leron Gray](https://github.com/daddycocoaman)
- [Kenneth Hoste](https://github.com/boegel)
- [Lanqing Huang](https://github.com/lqhuang)
Expand Down Expand Up @@ -46,6 +48,7 @@ The following people have contributed to the development of Rich:
- [Nicolas Simonds](https://github.com/0xDEC0DE)
- [Aaron Stephens](https://github.com/aaronst)
- [Gabriele N. Tornetta](https://github.com/p403n1x87)
- [Nils Vu](https://github.com/nilsvu)
- [Arian Mollik Wasi](https://github.com/wasi-master)
- [Handhika Yanuar Pratama](https://github.com/theDreamer911)
- [za](https://github.com/za)
Expand Down
5 changes: 3 additions & 2 deletions rich/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from ._extension import load_ipython_extension # noqa: F401

__all__ = ["get_console", "reconfigure", "print", "inspect"]
__all__ = ["get_console", "reconfigure", "print", "inspect", "print_json"]

if TYPE_CHECKING:
from .console import Console
Expand Down Expand Up @@ -40,7 +40,8 @@ def reconfigure(*args: Any, **kwargs: Any) -> None:
"""Reconfigures the global console by replacing it with another.
Args:
console (Console): Replacement console instance.
*args (Any): Positional arguments for the replacement :class:`~rich.console.Console`.
**kwargs (Any): Keyword arguments for the replacement :class:`~rich.console.Console`.
"""
from rich.console import Console

Expand Down
2 changes: 1 addition & 1 deletion rich/ansi.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def __init__(self) -> None:
self.style = Style.null()

def decode(self, terminal_text: str) -> Iterable[Text]:
"""Decode ANSI codes in an interable of lines.
"""Decode ANSI codes in an iterable of lines.
Args:
lines (Iterable[str]): An iterable of lines of terminal output.
Expand Down
6 changes: 5 additions & 1 deletion rich/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,11 @@ def _is_jupyter() -> bool: # pragma: no cover
return False
ipython = get_ipython() # type: ignore[name-defined]
shell = ipython.__class__.__name__
if "google.colab" in str(ipython.__class__) or shell == "ZMQInteractiveShell":
if (
"google.colab" in str(ipython.__class__)
or os.getenv("DATABRICKS_RUNTIME_VERSION")
or shell == "ZMQInteractiveShell"
):
return True # Jupyter notebook or qtconsole
elif shell == "TerminalInteractiveShell":
return False # Terminal running IPython
Expand Down
2 changes: 1 addition & 1 deletion rich/filesize.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"""Functions for reporting filesizes. Borrowed from https://github.com/PyFilesystem/pyfilesystem2
The functions declared in this module should cover the different
usecases needed to generate a string representation of a file size
use cases needed to generate a string representation of a file size
using several different units. Since there are many standards regarding
file size units, three different functions have been implemented.
Expand Down
23 changes: 12 additions & 11 deletions rich/pretty.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,11 @@ def to_repr(obj: Any) -> str:
def _traverse(obj: Any, root: bool = False, depth: int = 0) -> Node:
"""Walk the object depth first."""

obj_id = id(obj)
if obj_id in visited_ids:
# Recursion detected
return Node(value_repr="...")

obj_type = type(obj)
py_version = (sys.version_info.major, sys.version_info.minor)
children: List[Node]
Expand Down Expand Up @@ -673,6 +678,7 @@ def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]:
pass

if rich_repr_result is not None:
push_visited(obj_id)
angular = getattr(obj.__rich_repr__, "angular", False)
args = list(iter_rich_args(rich_repr_result))
class_name = obj.__class__.__name__
Expand Down Expand Up @@ -720,7 +726,9 @@ def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]:
children=[],
last=root,
)
pop_visited(obj_id)
elif _is_attr_object(obj) and not fake_attributes:
push_visited(obj_id)
children = []
append = children.append

Expand Down Expand Up @@ -767,19 +775,14 @@ def iter_attrs() -> Iterable[
node = Node(
value_repr=f"{obj.__class__.__name__}()", children=[], last=root
)

pop_visited(obj_id)
elif (
is_dataclass(obj)
and not _safe_isinstance(obj, type)
and not fake_attributes
and (_is_dataclass_repr(obj) or py_version == (3, 6))
):
obj_id = id(obj)
if obj_id in visited_ids:
# Recursion detected
return Node(value_repr="...")
push_visited(obj_id)

children = []
append = children.append
if reached_max_depth:
Expand All @@ -801,8 +804,9 @@ def iter_attrs() -> Iterable[
child_node.key_separator = "="
append(child_node)

pop_visited(obj_id)
pop_visited(obj_id)
elif _is_namedtuple(obj) and _has_default_namedtuple_repr(obj):
push_visited(obj_id)
class_name = obj.__class__.__name__
if reached_max_depth:
# If we've reached the max depth, we still show the class name, but not its contents
Expand All @@ -824,16 +828,13 @@ def iter_attrs() -> Iterable[
child_node.last = last
child_node.key_separator = "="
append(child_node)
pop_visited(obj_id)
elif _safe_isinstance(obj, _CONTAINERS):
for container_type in _CONTAINERS:
if _safe_isinstance(obj, container_type):
obj_type = container_type
break

obj_id = id(obj)
if obj_id in visited_ids:
# Recursion detected
return Node(value_repr="...")
push_visited(obj_id)

open_brace, close_brace, empty = _BRACES[obj_type](obj)
Expand Down
12 changes: 5 additions & 7 deletions rich/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ def extract(
from rich import _IMPORT_CWD

def safe_str(_object: Any) -> str:
"""Don't allow exceptions from __str__ to propegate."""
"""Don't allow exceptions from __str__ to propagate."""
try:
return str(_object)
except Exception:
Expand Down Expand Up @@ -389,19 +389,17 @@ def safe_str(_object: Any) -> str:
del stack.frames[:]

cause = getattr(exc_value, "__cause__", None)
if cause and cause.__traceback__:
if cause:
exc_type = cause.__class__
exc_value = cause
# __traceback__ can be None, e.g. for exceptions raised by the
# 'multiprocessing' module
traceback = cause.__traceback__
is_cause = True
continue

cause = exc_value.__context__
if (
cause
and cause.__traceback__
and not getattr(exc_value, "__suppress_context__", False)
):
if cause and not getattr(exc_value, "__suppress_context__", False):
exc_type = cause.__class__
exc_value = cause
traceback = cause.__traceback__
Expand Down
110 changes: 105 additions & 5 deletions tests/test_pretty.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from array import array
from collections import UserDict, defaultdict
from dataclasses import dataclass, field
from typing import List, NamedTuple
from typing import List, NamedTuple, Any
from unittest.mock import patch

import attr
Expand Down Expand Up @@ -315,12 +315,112 @@ def __repr__(self):
assert result == "BrokenAttr()"


def test_recursive():
def test_reference_cycle_container():
test = []
test.append(test)
result = pretty_repr(test)
expected = "[...]"
assert result == expected
res = pretty_repr(test)
assert res == "[...]"

test = [1, []]
test[1].append(test)
res = pretty_repr(test)
assert res == "[1, [...]]"

# Not a cyclic reference, just a repeated reference
a = [2]
test = [1, [a, a]]
res = pretty_repr(test)
assert res == "[1, [[2], [2]]]"


def test_reference_cycle_namedtuple():
class Example(NamedTuple):
x: int
y: Any

test = Example(1, [Example(2, [])])
test.y[0].y.append(test)
res = pretty_repr(test)
assert res == "Example(x=1, y=[Example(x=2, y=[...])])"

# Not a cyclic reference, just a repeated reference
a = Example(2, None)
test = Example(1, [a, a])
res = pretty_repr(test)
assert res == "Example(x=1, y=[Example(x=2, y=None), Example(x=2, y=None)])"


def test_reference_cycle_dataclass():
@dataclass
class Example:
x: int
y: Any

test = Example(1, None)
test.y = test
res = pretty_repr(test)
assert res == "Example(x=1, y=...)"

test = Example(1, Example(2, None))
test.y.y = test
res = pretty_repr(test)
assert res == "Example(x=1, y=Example(x=2, y=...))"

# Not a cyclic reference, just a repeated reference
a = Example(2, None)
test = Example(1, [a, a])
res = pretty_repr(test)
assert res == "Example(x=1, y=[Example(x=2, y=None), Example(x=2, y=None)])"


def test_reference_cycle_attrs():
@attr.define
class Example:
x: int
y: Any

test = Example(1, None)
test.y = test
res = pretty_repr(test)
assert res == "Example(x=1, y=...)"

test = Example(1, Example(2, None))
test.y.y = test
res = pretty_repr(test)
assert res == "Example(x=1, y=Example(x=2, y=...))"

# Not a cyclic reference, just a repeated reference
a = Example(2, None)
test = Example(1, [a, a])
res = pretty_repr(test)
assert res == "Example(x=1, y=[Example(x=2, y=None), Example(x=2, y=None)])"


def test_reference_cycle_custom_repr():
class Example:
def __init__(self, x, y):
self.x = x
self.y = y

def __rich_repr__(self):
yield ("x", self.x)
yield ("y", self.y)

test = Example(1, None)
test.y = test
res = pretty_repr(test)
assert res == "Example(x=1, y=...)"

test = Example(1, Example(2, None))
test.y.y = test
res = pretty_repr(test)
assert res == "Example(x=1, y=Example(x=2, y=...))"

# Not a cyclic reference, just a repeated reference
a = Example(2, None)
test = Example(1, [a, a])
res = pretty_repr(test)
assert res == "Example(x=1, y=[Example(x=2, y=None), Example(x=2, y=None)])"


def test_max_depth():
Expand Down
4 changes: 2 additions & 2 deletions tests/test_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def test_render_tree_hide_root_win32():
def test_tree_measure():
tree = Tree("foo")
tree.add("bar")
tree.add("musroom risotto")
tree.add("mushroom risotto")
console = Console()
measurement = Measurement.get(console, console.options, tree)
assert measurement == Measurement(11, 19)
assert measurement == Measurement(12, 20)
10 changes: 5 additions & 5 deletions tools/cats.json
Original file line number Diff line number Diff line change
Expand Up @@ -1500,7 +1500,7 @@
},
{
"_id": "5b1b455f841d9700146158db",
"text": "The Egyptian Mau breed was saved from extinciton when Russian princess Natalie Trubetskaya was given a Mau that was imported from the Middle East. When she emigrated to New York City in 1956, she brought along three Mau cats. She used these kitties to establish the Fatima Egyptian Mau cattery, which produced many of the ancestors of today’s Egyptian Maus in America.",
"text": "The Egyptian Mau breed was saved from extinction when Russian princess Natalie Trubetskaya was given a Mau that was imported from the Middle East. When she emigrated to New York City in 1956, she brought along three Mau cats. She used these kitties to establish the Fatima Egyptian Mau cattery, which produced many of the ancestors of today’s Egyptian Maus in America.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
Expand Down Expand Up @@ -2193,7 +2193,7 @@
},
{
"_id": "5c609758e54902001453302c",
"text": "According to the American Society for the Prevention of Cruelty of Animals, newborn kittens get all the nutrition they need duing the first four weeks of life fom their mother's milk. If you are taking care of a kitten without its mother, or if the mother isn't producing enough milk, you can feed the kitten a commercial milk substitute.",
"text": "According to the American Society for the Prevention of Cruelty of Animals, newborn kittens get all the nutrition they need during the first four weeks of life fom their mother's milk. If you are taking care of a kitten without its mother, or if the mother isn't producing enough milk, you can feed the kitten a commercial milk substitute.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
Expand Down Expand Up @@ -2656,7 +2656,7 @@
{
"_id": "5d38b2510f1c57001592f12e",
"type": "cat",
"text": "Courgars are the largest wild cats that can purr.",
"text": "Cougars are the largest wild cats that can purr.",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
"name": {
Expand Down Expand Up @@ -2768,7 +2768,7 @@
{
"_id": "5e80e7cc2d4b850015003072",
"type": "cat",
"text": "Cats can climb on trees faster than squirels.",
"text": "Cats can climb on trees faster than squirrels.",
"user": {
"_id": "5e80e6c72d4b85001500306e",
"name": {
Expand Down Expand Up @@ -3284,4 +3284,4 @@
"userUpvoted": null
}
]
}
}

0 comments on commit 9fc01f1

Please sign in to comment.