Coverage for tests / test_terminal_control_characters.py: 100%

55 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-11-09 00:15 +0000

1import re 1abcdefg

2from typing import Annotated 1abcdefg

3 

4import pytest 1abcdefg

5import typer 1abcdefg

6from typer._click.exceptions import NoSuchOption 1abcdefg

7from typer.testing import CliRunner 1abcdefg

8 

9runner = CliRunner() 1abcdefg

10 

11 

12@pytest.fixture(params=["rich", None], ids=["rich", "plain"]) 1abcdefg

13def app(request: pytest.FixtureRequest) -> typer.Typer: 1abcdefg

14 app = typer.Typer(add_completion=False, rich_markup_mode=request.param) 1abcdefg

15 

16 @app.command() 1abcdefg

17 def no_args() -> None: 1abcdefg

18 pass # pragma: no cover 

19 

20 @app.command() 1abcdefg

21 def read_file(value: typer.FileText) -> None: 1abcdefg

22 pass # pragma: no cover 

23 

24 @app.command() 1abcdefg

25 def parse( 1abcdefg

26 value: Annotated[int, typer.Argument(parser=lambda x: int(x, 0))], 

27 ) -> None: 

28 pass # pragma: no cover 

29 

30 return app 1abcdefg

31 

32 

33@pytest.mark.parametrize("color", [False, True]) 1abcdefg

34@pytest.mark.parametrize( 1abcdefg

35 ("command", "prefix", "message"), 

36 [ 

37 ("no-args", "", "Got unexpected extra argument(s)"), 

38 ("no-args", "--", "No such option"), 

39 ("read-file", "", "Invalid value"), 

40 ("parse", "", "Invalid value"), 

41 ], 

42 ids=["extra-argument", "unknown-option", "file-open", "custom-parser"], 

43) 

44@pytest.mark.parametrize( 1abcdefg

45 ("value", "escaped"), 

46 [ 

47 ("\x1b[2J\x1b[H", r"\x1b[2J\x1b[H"), 

48 ("\x1b]52;c;YWJj\x07", r"\x1b]52;c;YWJj\x07"), 

49 ("\x1b]52;c;YWJj\x1b\\", "\\x1b]52;c;YWJj\\x1b\\"), 

50 ("\x9b2J\x9d52;c;YWJj\x9c", r"\x9b2J\x9d52;c;YWJj\x9c"), 

51 ("\r\n\t\b\x7f", r"\x0d\x0a\x09\x08\x7f"), 

52 ( 

53 "text-\u00e9-\u65e5\u672c-\U0001f469\u200d\U0001f4bb-'quoted'-\\text", 

54 "text-\u00e9-\u65e5\u672c-\U0001f469\u200d\U0001f4bb-'quoted'-\\text", 

55 ), 

56 ], 

57 ids=["screen", "osc-bel", "osc-st", "c1", "whitespace-controls", "unicode"], 

58) 

59def test_error_values( 1abcdefg

60 app: typer.Typer, 

61 command: str, 

62 prefix: str, 

63 message: str, 

64 value: str, 

65 escaped: str, 

66 color: bool, 

67 monkeypatch: pytest.MonkeyPatch, 

68) -> None: 

69 from typer import rich_utils 1abcdefg

70 

71 monkeypatch.delenv("NO_COLOR", raising=False) 1abcdefg

72 monkeypatch.setenv("TERM", "xterm-256color") 1abcdefg

73 monkeypatch.setattr(rich_utils, "FORCE_TERMINAL", color) 1abcdefg

74 monkeypatch.setattr(rich_utils, "MAX_WIDTH", 3000) 1abcdefg

75 result = runner.invoke(app, [command, prefix + "input-" + value], color=color) 1abcdefg

76 

77 assert result.exit_code == 2 1abcdefg

78 # Remove only generated color styles so other terminal controls cannot hide. 

79 output = re.sub(r"\x1b\[[0-9;]*m", "", result.stderr) 1abcdefg

80 assert message in output 1abcdefg

81 assert prefix + "input-" + escaped in output 1abcdefg

82 assert re.search(r"[\x00-\x09\x0b-\x1f\x7f-\x9f]", output) is None 1abcdefg

83 

84 

85def test_unknown_option_preserves_original_value() -> None: 1abcdefg

86 value = "--input-\x00\x1f\x7f\x80\x9f" 1abcdefg

87 error = NoSuchOption(value) 1abcdefg

88 

89 assert error.option_name == value 1abcdefg

90 assert error.format_message() == r"No such option: --input-\x00\x1f\x7f\x80\x9f" 1abcdefg

91 

92 

93def test_argument_preserves_original_value() -> None: 1abcdefg

94 app = typer.Typer(add_completion=False) 1abcdefg

95 value = "input-\x1b[2J\x1b]52;c;YWJj\x07" 1abcdefg

96 

97 @app.command() 1abcdefg

98 def main(value: str) -> None: 1abcdefg

99 typer.echo(repr(value)) 1abcdefg

100 

101 result = runner.invoke(app, [value]) 1abcdefg

102 

103 assert result.exit_code == 0 1abcdefg

104 assert result.stdout == repr(value) + "\n" 1abcdefg

105 

106 

107def test_echo_preserves_terminal_sequences() -> None: 1abcdefg

108 app = typer.Typer(add_completion=False) 1abcdefg

109 value = "\x1b[2J\x1b]52;c;YWJj\x07" 1abcdefg

110 

111 @app.command() 1abcdefg

112 def main() -> None: 1abcdefg

113 typer.echo(value, nl=False) 1abcdefg

114 

115 result = runner.invoke(app, color=True) 1abcdefg

116 

117 assert result.exit_code == 0 1abcdefg

118 assert result.stdout_bytes == value.encode() 1abcdefg