@@ -508,6 +508,19 @@ def __init__(
508
508
# Commands that will run at the beginning of the command loop
509
509
self ._startup_commands : list [str ] = []
510
510
511
+ # Store initial termios settings to restore after each command.
512
+ # This is a faster way of accomplishing what "stty sane" does.
513
+ self ._initial_termios_settings = None
514
+ if not sys .platform .startswith ('win' ) and self .stdin .isatty ():
515
+ try :
516
+ import io
517
+ import termios
518
+
519
+ self ._initial_termios_settings = termios .tcgetattr (self .stdin .fileno ())
520
+ except (ImportError , io .UnsupportedOperation , termios .error ):
521
+ # This can happen if termios isn't available or stdin is a pseudo-TTY
522
+ self ._initial_termios_settings = None
523
+
511
524
# If a startup script is provided and exists, then execute it in the startup commands
512
525
if startup_script :
513
526
startup_script = os .path .abspath (os .path .expanduser (startup_script ))
@@ -1198,22 +1211,39 @@ def print_to(
1198
1211
end : str = "\n " ,
1199
1212
style : StyleType | None = None ,
1200
1213
soft_wrap : bool = True ,
1214
+ emoji : bool = False ,
1215
+ markup : bool = False ,
1216
+ highlight : bool = False ,
1201
1217
rich_print_kwargs : RichPrintKwargs | None = None ,
1202
1218
** kwargs : Any , # noqa: ARG002
1203
1219
) -> None :
1204
1220
"""Print objects to a given file stream.
1205
1221
1222
+ This method is configured for general-purpose printing. By default, it enables
1223
+ soft wrap and disables Rich's automatic detection for markup, emoji, and highlighting.
1224
+ These defaults can be overridden by passing explicit keyword arguments.
1225
+
1206
1226
:param file: file stream being written to
1207
1227
:param objects: objects to print
1208
1228
:param sep: string to write between printed text. Defaults to " ".
1209
1229
:param end: string to write at end of printed text. Defaults to a newline.
1210
1230
:param style: optional style to apply to output
1211
- :param soft_wrap: Enable soft wrap mode. If True, lines of text will not be word-wrapped or cropped to
1212
- fit the terminal width. Defaults to True.
1231
+ :param soft_wrap: Enable soft wrap mode. If True, lines of text will not be
1232
+ word-wrapped or cropped to fit the terminal width. Defaults to True.
1233
+ :param emoji: If True, Rich will replace emoji codes (e.g., :smiley:) with their
1234
+ corresponding Unicode characters. Defaults to False.
1235
+ :param markup: If True, Rich will interpret strings with tags (e.g., [bold]hello[/bold])
1236
+ as styled output. Defaults to False.
1237
+ :param highlight: If True, Rich will automatically apply highlighting to elements within
1238
+ strings, such as common Python data types like numbers, booleans, or None.
1239
+ This is particularly useful when pretty printing objects like lists and
1240
+ dictionaries to display them in color. Defaults to False.
1213
1241
:param rich_print_kwargs: optional additional keyword arguments to pass to Rich's Console.print().
1214
1242
:param kwargs: Arbitrary keyword arguments. This allows subclasses to extend the signature of this
1215
1243
method and still call `super()` without encountering unexpected keyword argument errors.
1216
1244
These arguments are not passed to Rich's Console.print().
1245
+
1246
+ See the Rich documentation for more details on emoji codes, markup tags, and highlighting.
1217
1247
"""
1218
1248
prepared_objects = ru .prepare_objects_for_rendering (* objects )
1219
1249
@@ -1224,6 +1254,9 @@ def print_to(
1224
1254
end = end ,
1225
1255
style = style ,
1226
1256
soft_wrap = soft_wrap ,
1257
+ emoji = emoji ,
1258
+ markup = markup ,
1259
+ highlight = highlight ,
1227
1260
** (rich_print_kwargs if rich_print_kwargs is not None else {}),
1228
1261
)
1229
1262
except BrokenPipeError :
@@ -1242,6 +1275,9 @@ def poutput(
1242
1275
end : str = "\n " ,
1243
1276
style : StyleType | None = None ,
1244
1277
soft_wrap : bool = True ,
1278
+ emoji : bool = False ,
1279
+ markup : bool = False ,
1280
+ highlight : bool = False ,
1245
1281
rich_print_kwargs : RichPrintKwargs | None = None ,
1246
1282
** kwargs : Any , # noqa: ARG002
1247
1283
) -> None :
@@ -1256,6 +1292,9 @@ def poutput(
1256
1292
end = end ,
1257
1293
style = style ,
1258
1294
soft_wrap = soft_wrap ,
1295
+ emoji = emoji ,
1296
+ markup = markup ,
1297
+ highlight = highlight ,
1259
1298
rich_print_kwargs = rich_print_kwargs ,
1260
1299
)
1261
1300
@@ -1266,6 +1305,9 @@ def perror(
1266
1305
end : str = "\n " ,
1267
1306
style : StyleType | None = Cmd2Style .ERROR ,
1268
1307
soft_wrap : bool = True ,
1308
+ emoji : bool = False ,
1309
+ markup : bool = False ,
1310
+ highlight : bool = False ,
1269
1311
rich_print_kwargs : RichPrintKwargs | None = None ,
1270
1312
** kwargs : Any , # noqa: ARG002
1271
1313
) -> None :
@@ -1282,6 +1324,9 @@ def perror(
1282
1324
end = end ,
1283
1325
style = style ,
1284
1326
soft_wrap = soft_wrap ,
1327
+ emoji = emoji ,
1328
+ markup = markup ,
1329
+ highlight = highlight ,
1285
1330
rich_print_kwargs = rich_print_kwargs ,
1286
1331
)
1287
1332
@@ -1291,6 +1336,9 @@ def psuccess(
1291
1336
sep : str = " " ,
1292
1337
end : str = "\n " ,
1293
1338
soft_wrap : bool = True ,
1339
+ emoji : bool = False ,
1340
+ markup : bool = False ,
1341
+ highlight : bool = False ,
1294
1342
rich_print_kwargs : RichPrintKwargs | None = None ,
1295
1343
** kwargs : Any , # noqa: ARG002
1296
1344
) -> None :
@@ -1304,6 +1352,9 @@ def psuccess(
1304
1352
end = end ,
1305
1353
style = Cmd2Style .SUCCESS ,
1306
1354
soft_wrap = soft_wrap ,
1355
+ emoji = emoji ,
1356
+ markup = markup ,
1357
+ highlight = highlight ,
1307
1358
rich_print_kwargs = rich_print_kwargs ,
1308
1359
)
1309
1360
@@ -1313,6 +1364,9 @@ def pwarning(
1313
1364
sep : str = " " ,
1314
1365
end : str = "\n " ,
1315
1366
soft_wrap : bool = True ,
1367
+ emoji : bool = False ,
1368
+ markup : bool = False ,
1369
+ highlight : bool = False ,
1316
1370
rich_print_kwargs : RichPrintKwargs | None = None ,
1317
1371
** kwargs : Any , # noqa: ARG002
1318
1372
) -> None :
@@ -1326,6 +1380,9 @@ def pwarning(
1326
1380
end = end ,
1327
1381
style = Cmd2Style .WARNING ,
1328
1382
soft_wrap = soft_wrap ,
1383
+ emoji = emoji ,
1384
+ markup = markup ,
1385
+ highlight = highlight ,
1329
1386
rich_print_kwargs = rich_print_kwargs ,
1330
1387
)
1331
1388
@@ -1390,6 +1447,9 @@ def pfeedback(
1390
1447
end : str = "\n " ,
1391
1448
style : StyleType | None = None ,
1392
1449
soft_wrap : bool = True ,
1450
+ emoji : bool = False ,
1451
+ markup : bool = False ,
1452
+ highlight : bool = False ,
1393
1453
rich_print_kwargs : RichPrintKwargs | None = None ,
1394
1454
** kwargs : Any , # noqa: ARG002
1395
1455
) -> None :
@@ -1408,6 +1468,9 @@ def pfeedback(
1408
1468
end = end ,
1409
1469
style = style ,
1410
1470
soft_wrap = soft_wrap ,
1471
+ emoji = emoji ,
1472
+ markup = markup ,
1473
+ highlight = highlight ,
1411
1474
rich_print_kwargs = rich_print_kwargs ,
1412
1475
)
1413
1476
else :
@@ -1417,6 +1480,9 @@ def pfeedback(
1417
1480
end = end ,
1418
1481
style = style ,
1419
1482
soft_wrap = soft_wrap ,
1483
+ emoji = emoji ,
1484
+ markup = markup ,
1485
+ highlight = highlight ,
1420
1486
rich_print_kwargs = rich_print_kwargs ,
1421
1487
)
1422
1488
@@ -1428,6 +1494,9 @@ def ppaged(
1428
1494
style : StyleType | None = None ,
1429
1495
chop : bool = False ,
1430
1496
soft_wrap : bool = True ,
1497
+ emoji : bool = False ,
1498
+ markup : bool = False ,
1499
+ highlight : bool = False ,
1431
1500
rich_print_kwargs : RichPrintKwargs | None = None ,
1432
1501
** kwargs : Any , # noqa: ARG002
1433
1502
) -> None :
@@ -1479,6 +1548,9 @@ def ppaged(
1479
1548
end = end ,
1480
1549
style = style ,
1481
1550
soft_wrap = soft_wrap ,
1551
+ emoji = emoji ,
1552
+ markup = markup ,
1553
+ highlight = highlight ,
1482
1554
** (rich_print_kwargs if rich_print_kwargs is not None else {}),
1483
1555
)
1484
1556
output_bytes = capture .get ().encode ('utf-8' , 'replace' )
@@ -1503,6 +1575,9 @@ def ppaged(
1503
1575
end = end ,
1504
1576
style = style ,
1505
1577
soft_wrap = soft_wrap ,
1578
+ emoji = emoji ,
1579
+ markup = markup ,
1580
+ highlight = highlight ,
1506
1581
rich_print_kwargs = rich_print_kwargs ,
1507
1582
)
1508
1583
@@ -2760,14 +2835,15 @@ def onecmd_plus_hooks(
2760
2835
2761
2836
def _run_cmdfinalization_hooks (self , stop : bool , statement : Statement | None ) -> bool :
2762
2837
"""Run the command finalization hooks."""
2763
- with self .sigint_protection :
2764
- if not sys .platform .startswith ('win' ) and self .stdin .isatty ():
2765
- # Before the next command runs, fix any terminal problems like those
2766
- # caused by certain binary characters having been printed to it.
2767
- import subprocess
2768
-
2769
- proc = subprocess .Popen (['stty' , 'sane' ]) # noqa: S607
2770
- proc .communicate ()
2838
+ if self ._initial_termios_settings is not None and self .stdin .isatty ():
2839
+ import io
2840
+ import termios
2841
+
2842
+ # Before the next command runs, fix any terminal problems like those
2843
+ # caused by certain binary characters having been printed to it.
2844
+ with self .sigint_protection , contextlib .suppress (io .UnsupportedOperation , termios .error ):
2845
+ # This can fail if stdin is a pseudo-TTY, in which case we just ignore it
2846
+ termios .tcsetattr (self .stdin .fileno (), termios .TCSANOW , self ._initial_termios_settings )
2771
2847
2772
2848
data = plugin .CommandFinalizationData (stop , statement )
2773
2849
for func in self ._cmdfinalization_hooks :
0 commit comments