Skip to content

Commit 62fa2d3

Browse files
committed
Add initial simple integration with rich for rendering ValidationError's as a rich Tree
1 parent f9a5462 commit 62fa2d3

3 files changed

Lines changed: 88 additions & 51 deletions

File tree

docs/ref/contrib/rich/index.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
######################
2+
Better CLI's with Rich
3+
######################
4+
5+
Additional resources fields that include a unit.
6+
7+
.. note::
8+
9+
This contrib module depends on the `Rich <https://rich.readthedocs.io/en/latest/introduction.html>`_ library::
10+
11+
pip install rich
12+
13+
14+
15+
Render Validation Errors
16+
========================
17+
18+
Generate a tree or extend an existing one from a ``ValidationError`` exception.
19+
20+
.. code-block:: python
21+
22+
from rich import print
23+
from odin.contrib.rich.validation_tree import validation_error_tree
24+
25+
try:
26+
...
27+
except ValidationError as ex:
28+
tree = validation_error_tree(ex)
29+
print(tree)
30+
31+
.. note::
32+
A tree instance can be provided as the second argument to allow for customisations
33+
to the tree root label and styles.

src/odin/contrib/rich/__init__.py

Lines changed: 3 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,4 @@
1-
"""Integration with Rich for nicer CLI's!"""
2-
from typing import Iterable, Union
1+
"""Integration with Rich.
32
4-
from rich.tree import Tree
5-
6-
from odin.exceptions import ValidationError, NON_FIELD_ERRORS
7-
8-
9-
def _all_str(iterable: Iterable) -> bool:
10-
"""Does the supplied iterable only contain strings."""
11-
return all(isinstance(item, str) for item in iterable)
12-
13-
14-
def _validation_error_to_tree(error_messages: Union[list, dict], tree: Tree):
15-
"""Internal recursive method."""
16-
17-
if isinstance(error_messages, dict):
18-
for key, value in error_messages.items():
19-
20-
node = tree.add(
21-
f"[yellow]:memo:" if key == NON_FIELD_ERRORS else f"[green]{key}"
22-
)
23-
24-
_validation_error_to_tree(value, node)
25-
26-
elif isinstance(error_messages, list):
27-
if _all_str(error_messages):
28-
for message in error_messages:
29-
tree.add(f"[italic]{message}", guide_style="bold")
30-
31-
else:
32-
for idx, value in enumerate(error_messages):
33-
node = tree.add(str(idx))
34-
_validation_error_to_tree(value, node)
35-
36-
else:
37-
# Shouldn't technically be possible with a valid error messages structure.
38-
tree.add(f":warning: {error_messages}")
39-
40-
41-
def validation_error_to_tree(error: ValidationError, *, tree: Tree = None) -> Tree:
42-
"""Map a validation error into a tree.
43-
44-
.. codeblock:: python
45-
46-
error_tree = validation_error_to_tree(error)
47-
print(tree)
48-
49-
"""
50-
tree = tree or Tree("[red bold]Validation Errors")
51-
_validation_error_to_tree(error.error_messages, tree)
52-
return tree
3+
https://rich.readthedocs.io/en/latest/index.html
4+
"""
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Integration with Rich for nicer CLI's!"""
2+
from typing import Iterable, Union
3+
4+
from rich.tree import Tree
5+
6+
from odin.exceptions import ValidationError, NON_FIELD_ERRORS
7+
8+
9+
def _all_str(iterable: Iterable) -> bool:
10+
"""Does the supplied iterable only contain strings."""
11+
return all(isinstance(item, str) for item in iterable)
12+
13+
14+
def _validation_error_to_tree(error_messages: Union[list, dict], tree: Tree):
15+
"""Internal recursive method."""
16+
17+
if isinstance(error_messages, dict):
18+
for key, value in error_messages.items():
19+
20+
node = tree.add(
21+
f"[yellow]:memo:" if key == NON_FIELD_ERRORS else f"[green]{key}"
22+
)
23+
24+
_validation_error_to_tree(value, node)
25+
26+
elif isinstance(error_messages, list):
27+
if _all_str(error_messages):
28+
for message in error_messages:
29+
tree.add(f"[italic]{message}", guide_style="bold")
30+
31+
else:
32+
for idx, value in enumerate(error_messages):
33+
node = tree.add(str(idx))
34+
_validation_error_to_tree(value, node)
35+
36+
else:
37+
# Shouldn't technically be possible with a valid error messages structure.
38+
tree.add(f":warning: {error_messages}")
39+
40+
41+
def validation_error_tree(error: ValidationError, *, tree: Tree = None) -> Tree:
42+
"""Map a validation error into a tree.
43+
44+
.. codeblock:: python
45+
46+
error_tree = validation_error_to_tree(error)
47+
print(tree)
48+
49+
"""
50+
tree = tree or Tree("[red bold]Validation Errors")
51+
_validation_error_to_tree(error.error_messages, tree)
52+
return tree

0 commit comments

Comments
 (0)