11import logging
22import threading
3- from typing import Any , List , Union , Dict
4- from rich .console import RenderableType
3+ from typing import Any , List , Union , Dict , Tuple , Optional
4+ from rich .console import RenderableType , ConsoleRenderable
55
66from nornir .core import Nornir
77from nornir .core .inventory import Inventory
1010from rich import print
1111from rich .columns import Columns
1212from rich .panel import Panel
13- from rich .scope import render_scope
1413from rich .padding import PaddingDimensions
1514from rich .pretty import Pretty
1615from rich .protocol import is_renderable , rich_cast
16+ from rich .table import Table
17+ from rich .text import Text
1718
1819
1920LOCK = threading .Lock ()
@@ -31,17 +32,19 @@ class RichHelper:
3132 vars: Which attributes you want to print
3233 severity_level: Print only errors with this severity level or higher
3334 failed: if ``True`` assume the task failed
35+ line_breaks: if ``True`` line breaks in strings will be printed
3436 """
3537
3638 def __init__ (
3739 self ,
3840 columns_settings : Dict [str , Any ] = dict (),
39- padding : PaddingDimensions = None ,
41+ padding : Optional [ PaddingDimensions ] = None ,
4042 expand : bool = False ,
4143 equal : bool = True ,
42- vars : List [str ] = None ,
44+ vars : Optional [ List [str ] ] = None ,
4345 severity_level : int = 0 ,
44- failed : bool = None ,
46+ failed : Optional [bool ] = None ,
47+ line_breaks : Optional [bool ] = None ,
4548 ) -> None :
4649 self .columns_settings = columns_settings
4750 self .columns_settings ["expand" ] = expand
@@ -51,6 +54,7 @@ def __init__(
5154 self .vars = vars
5255 self .severity_level = severity_level
5356 self .failed = failed
57+ self .line_breaks = line_breaks
5458
5559 def print_aggregated_result (self , result : AggregatedResult ) -> Panel :
5660 """
@@ -110,7 +114,7 @@ def print_result(self, result: Result) -> Union[Panel, None]:
110114 return None
111115 if self .vars :
112116 return Panel (
113- render_scope ( {x : getattr (result , x ) for x in self .vars }),
117+ self . _scope_talbe ( scope = {x : getattr (result , x ) for x in self .vars }),
114118 title = result .name ,
115119 style = "red" if result .failed else "green" ,
116120 )
@@ -129,13 +133,15 @@ def print_result(self, result: Result) -> Union[Panel, None]:
129133 def print_scopes (self , scopes : Dict [str , Any ]) -> Columns :
130134 if self .vars :
131135 columns = [
132- render_scope (
136+ self . _scope_talbe (
133137 {k : v for k , v in map .items () if k in self .vars }, title = name
134138 )
135139 for name , map in scopes .items ()
136140 ]
137141 else :
138- columns = [render_scope (map , title = name ) for name , map in scopes .items ()]
142+ columns = [
143+ self ._scope_talbe (map , title = name ) for name , map in scopes .items ()
144+ ]
139145 return Columns (
140146 columns ,
141147 ** self .columns_settings ,
@@ -161,16 +167,65 @@ def print_dispatch(
161167 return self .print_result (result )
162168 return Panel (f"Unable to find printer function for { result } " )
163169
170+ def _scope_talbe (
171+ self ,
172+ scope : "Dict[str, Any]" ,
173+ title : Optional [str ] = None ,
174+ ) -> Panel :
175+ """Render python variables in a given scope.
176+
177+ ***This code is heavily inspired/copied by/from nornir.scope.render_scope***
178+ https://github.com/Textualize/rich/blob/master/rich/scope.py
179+
180+ Args:
181+ scope (Dict[str, Any]): A mapping containing variable names and values.
182+
183+ Returns:
184+ Panel: Panel containing the table with key value mapping
185+ """
186+ items_table = Table .grid (padding = (0 , 1 ), expand = False )
187+ items_table .add_column (justify = "right" )
188+
189+ def sort_items (item : Tuple [str , Any ]) -> Tuple [bool , str ]:
190+ """Sort special variables first, then alphabetically."""
191+ key , _ = item
192+ return (not key .startswith ("__" ), key .lower ())
193+
194+ items = sorted (scope .items (), key = sort_items )
195+
196+ for key , value in items :
197+ key_text = Text .assemble (
198+ (key , "scope.key.special" if key .startswith ("__" ) else "scope.key" ),
199+ (" =" , "scope.equals" ),
200+ )
201+
202+ if self .line_breaks and isinstance (value , str ):
203+ value_text : ConsoleRenderable = Text (value .strip ())
204+ else :
205+ value_text = Pretty (value )
206+
207+ items_table .add_row (
208+ key_text ,
209+ value_text ,
210+ )
211+ return Panel .fit (
212+ items_table ,
213+ title = title ,
214+ border_style = "scope.border" ,
215+ padding = (0 , 1 ),
216+ )
217+
164218
165219def print_result (
166220 result : Union [Result , MultiResult , AggregatedResult ],
167- vars : List [str ] = None ,
221+ vars : Optional [ List [str ] ] = None ,
168222 failed : bool = False ,
169223 severity_level : int = logging .INFO ,
170224 columns_settings : Dict [str , Any ] = dict (),
171- padding : PaddingDimensions = None ,
225+ padding : Optional [ PaddingDimensions ] = None ,
172226 expand : bool = False ,
173227 equal : bool = True ,
228+ line_breaks : bool = False ,
174229) -> None :
175230 """
176231 Prints an object of type `nornir.core.task.Result` || `nornir.core.task.MultiResult` || `nornir.core.task.AggregatedResult`
@@ -184,6 +239,7 @@ def print_result(
184239 padding: Optional padding around cells. Defaults to (0, 1).
185240 expand: Expand columns to full width. Defaults to False.
186241 equal: Equal sized columns. Defaults to False
242+ line_breaks: if ``True`` line breaks in strings will be printed
187243 """
188244 LOCK .acquire ()
189245 equal = False if expand else equal
@@ -195,6 +251,7 @@ def print_result(
195251 vars = vars ,
196252 severity_level = severity_level ,
197253 failed = failed ,
254+ line_breaks = line_breaks ,
198255 )
199256 try :
200257 if isinstance (result , AggregatedResult ):
@@ -209,13 +266,14 @@ def print_result(
209266
210267def print_failed_hosts (
211268 result : AggregatedResult ,
212- vars : List [str ] = None ,
269+ vars : Optional [ List [str ] ] = None ,
213270 failed : bool = False ,
214271 severity_level : int = logging .INFO ,
215272 columns_settings : Dict [str , Any ] = dict (),
216- padding : PaddingDimensions = None ,
273+ padding : Optional [ PaddingDimensions ] = None ,
217274 expand : bool = False ,
218275 equal : bool = True ,
276+ line_breaks : bool = False ,
219277) -> None :
220278 """
221279 Prints results of all failed hosts from `nornir.core.task.AggregatedResult`
@@ -229,6 +287,7 @@ def print_failed_hosts(
229287 padding: Optional padding around cells. Defaults to (0, 1).
230288 expand: Expand columns to full width. Defaults to False.
231289 equal: Equal sized columns. Defaults to False
290+ line_breaks: if ``True`` line breaks in strings will be printed
232291 """
233292 LOCK .acquire ()
234293 equal = False if expand else equal
@@ -240,6 +299,7 @@ def print_failed_hosts(
240299 vars = vars ,
241300 severity_level = severity_level ,
242301 failed = failed ,
302+ line_breaks = line_breaks ,
243303 )
244304 try :
245305 for host , multi_result in result .failed_hosts .items ():
@@ -250,11 +310,11 @@ def print_failed_hosts(
250310
251311def print_inventory (
252312 inventory : Union [Inventory , Nornir ],
253- vars : List [str ] = None ,
313+ vars : Optional [ List [str ] ] = None ,
254314 failed : bool = False ,
255315 severity_level : int = logging .INFO ,
256316 columns_settings : Dict [str , Any ] = dict (),
257- padding : PaddingDimensions = None ,
317+ padding : Optional [ PaddingDimensions ] = None ,
258318 expand : bool = False ,
259319 equal : bool = True ,
260320) -> None :
0 commit comments