@@ -308,6 +308,14 @@ class AsyncAlert:
308308 timestamp : float = field (default_factory = time .monotonic , init = False )
309309
310310
311+ class _ConsoleCache (threading .local ):
312+ """Thread-local storage for cached Rich consoles used by print_to()."""
313+
314+ def __init__ (self ) -> None :
315+ self .stdout : Cmd2BaseConsole | None = None
316+ self .stderr : Cmd2BaseConsole | None = None
317+
318+
311319class Cmd :
312320 """An easy but powerful framework for writing line-oriented command interpreters.
313321
@@ -441,6 +449,9 @@ def __init__(
441449 self .scripts_add_to_history = True # Scripts and pyscripts add commands to history
442450 self .timing = False # Prints elapsed time for each command
443451
452+ # Thread-local storage for cached Rich consoles used by print_to()
453+ self ._console_cache = _ConsoleCache ()
454+
444455 # The maximum number of items to display in a completion table. If the number of completion
445456 # suggestions exceeds this number, then no table will appear.
446457 self .max_completion_table_items : int = 50
@@ -1332,20 +1343,48 @@ def _create_base_printing_console(
13321343 markup : bool ,
13331344 highlight : bool ,
13341345 ) -> Cmd2BaseConsole :
1335- """Create a Cmd2BaseConsole with formatting overrides.
1346+ """Get a Cmd2BaseConsole configured for the specified stream and formatting settings.
1347+
1348+ This method manages a thread-local cache for consoles printing to self.stdout or
1349+ sys.stderr to avoid the overhead of repeated initialization. It returns a cached
1350+ instance if its configuration matches the request. Otherwise, a new console is
1351+ created and cached.
1352+
1353+ Note: This implementation works around a bug in Rich where passing formatting settings
1354+ (emoji, markup, and highlight) directly to console.print() or console.log() does not
1355+ always work when printing certain Renderables. Passing them to the constructor instead
1356+ ensures they are correctly propagated. Once this bug is fixed, these parameters can
1357+ be removed from this method. For more details, see:
1358+ https://github.com/Textualize/rich/issues/4028
1359+ """
1360+ # Dictionary of settings to check against cached consoles
1361+ kwargs = {
1362+ "emoji" : emoji ,
1363+ "markup" : markup ,
1364+ "highlight" : highlight ,
1365+ }
13361366
1337- This works around a bug in Rich where passing these formatting settings directly to
1338- console.print() or console.log() does not always work when printing certain Renderables.
1339- Passing them to the constructor instead ensures they are correctly propagated.
1367+ # Check if we should use or update a cached console
1368+ if file is self .stdout :
1369+ cached = self ._console_cache .stdout
1370+ if cached is not None and cached .matches_config (file , ** kwargs ):
1371+ return cached
13401372
1341- See: https://github.com/Textualize/rich/issues/4028
1342- """
1343- return Cmd2BaseConsole (
1344- file = file ,
1345- emoji = emoji ,
1346- markup = markup ,
1347- highlight = highlight ,
1348- )
1373+ # Create new console and update cache
1374+ self ._console_cache .stdout = Cmd2BaseConsole (file = file , ** kwargs )
1375+ return self ._console_cache .stdout
1376+
1377+ if file is sys .stderr :
1378+ cached = self ._console_cache .stderr
1379+ if cached is not None and cached .matches_config (file , ** kwargs ):
1380+ return cached
1381+
1382+ # Create new console and update cache
1383+ self ._console_cache .stderr = Cmd2BaseConsole (file = file , ** kwargs )
1384+ return self ._console_cache .stderr
1385+
1386+ # For any other file, just create a new console
1387+ return Cmd2BaseConsole (file = file , ** kwargs )
13491388
13501389 def print_to (
13511390 self ,
0 commit comments