diff options
author | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:24:06 +0300 |
---|---|---|
committer | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:41:34 +0300 |
commit | e0e3e1717e3d33762ce61950504f9637a6e669ed (patch) | |
tree | bca3ff6939b10ed60c3d5c12439963a1146b9711 /contrib/python/click/py3/tests/test_utils.py | |
parent | 38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff) | |
download | ydb-e0e3e1717e3d33762ce61950504f9637a6e669ed.tar.gz |
add ydb deps
Diffstat (limited to 'contrib/python/click/py3/tests/test_utils.py')
-rw-r--r-- | contrib/python/click/py3/tests/test_utils.py | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/contrib/python/click/py3/tests/test_utils.py b/contrib/python/click/py3/tests/test_utils.py new file mode 100644 index 0000000000..656d2f9e53 --- /dev/null +++ b/contrib/python/click/py3/tests/test_utils.py @@ -0,0 +1,507 @@ +import os +import pathlib +import stat +import sys +from io import StringIO + +import pytest + +import click._termui_impl +import click.utils +from click._compat import WIN + + +def test_echo(runner): + with runner.isolation() as outstreams: + click.echo("\N{SNOWMAN}") + click.echo(b"\x44\x44") + click.echo(42, nl=False) + click.echo(b"a", nl=False) + click.echo("\x1b[31mx\x1b[39m", nl=False) + bytes = outstreams[0].getvalue().replace(b"\r\n", b"\n") + assert bytes == b"\xe2\x98\x83\nDD\n42ax" + + # if wrapped, we expect bytes to survive. + @click.command() + def cli(): + click.echo(b"\xf6") + + result = runner.invoke(cli, []) + assert result.stdout_bytes == b"\xf6\n" + + # Ensure we do not strip for bytes. + with runner.isolation() as outstreams: + click.echo(bytearray(b"\x1b[31mx\x1b[39m"), nl=False) + assert outstreams[0].getvalue() == b"\x1b[31mx\x1b[39m" + + +def test_echo_custom_file(): + import io + + f = io.StringIO() + click.echo("hello", file=f) + assert f.getvalue() == "hello\n" + + +def test_echo_no_streams(monkeypatch, runner): + """echo should not fail when stdout and stderr are None with pythonw on Windows.""" + with runner.isolation(): + sys.stdout = None + sys.stderr = None + click.echo("test") + click.echo("test", err=True) + + +@pytest.mark.parametrize( + ("styles", "ref"), + [ + ({"fg": "black"}, "\x1b[30mx y\x1b[0m"), + ({"fg": "red"}, "\x1b[31mx y\x1b[0m"), + ({"fg": "green"}, "\x1b[32mx y\x1b[0m"), + ({"fg": "yellow"}, "\x1b[33mx y\x1b[0m"), + ({"fg": "blue"}, "\x1b[34mx y\x1b[0m"), + ({"fg": "magenta"}, "\x1b[35mx y\x1b[0m"), + ({"fg": "cyan"}, "\x1b[36mx y\x1b[0m"), + ({"fg": "white"}, "\x1b[37mx y\x1b[0m"), + ({"bg": "black"}, "\x1b[40mx y\x1b[0m"), + ({"bg": "red"}, "\x1b[41mx y\x1b[0m"), + ({"bg": "green"}, "\x1b[42mx y\x1b[0m"), + ({"bg": "yellow"}, "\x1b[43mx y\x1b[0m"), + ({"bg": "blue"}, "\x1b[44mx y\x1b[0m"), + ({"bg": "magenta"}, "\x1b[45mx y\x1b[0m"), + ({"bg": "cyan"}, "\x1b[46mx y\x1b[0m"), + ({"bg": "white"}, "\x1b[47mx y\x1b[0m"), + ({"bg": 91}, "\x1b[48;5;91mx y\x1b[0m"), + ({"bg": (135, 0, 175)}, "\x1b[48;2;135;0;175mx y\x1b[0m"), + ({"bold": True}, "\x1b[1mx y\x1b[0m"), + ({"dim": True}, "\x1b[2mx y\x1b[0m"), + ({"underline": True}, "\x1b[4mx y\x1b[0m"), + ({"overline": True}, "\x1b[53mx y\x1b[0m"), + ({"italic": True}, "\x1b[3mx y\x1b[0m"), + ({"blink": True}, "\x1b[5mx y\x1b[0m"), + ({"reverse": True}, "\x1b[7mx y\x1b[0m"), + ({"strikethrough": True}, "\x1b[9mx y\x1b[0m"), + ({"bold": False}, "\x1b[22mx y\x1b[0m"), + ({"dim": False}, "\x1b[22mx y\x1b[0m"), + ({"underline": False}, "\x1b[24mx y\x1b[0m"), + ({"overline": False}, "\x1b[55mx y\x1b[0m"), + ({"italic": False}, "\x1b[23mx y\x1b[0m"), + ({"blink": False}, "\x1b[25mx y\x1b[0m"), + ({"reverse": False}, "\x1b[27mx y\x1b[0m"), + ({"strikethrough": False}, "\x1b[29mx y\x1b[0m"), + ({"fg": "black", "reset": False}, "\x1b[30mx y"), + ], +) +def test_styling(styles, ref): + assert click.style("x y", **styles) == ref + assert click.unstyle(ref) == "x y" + + +@pytest.mark.parametrize(("text", "expect"), [("\x1b[?25lx y\x1b[?25h", "x y")]) +def test_unstyle_other_ansi(text, expect): + assert click.unstyle(text) == expect + + +def test_filename_formatting(): + assert click.format_filename(b"foo.txt") == "foo.txt" + assert click.format_filename(b"/x/foo.txt") == "/x/foo.txt" + assert click.format_filename("/x/foo.txt") == "/x/foo.txt" + assert click.format_filename("/x/foo.txt", shorten=True) == "foo.txt" + assert click.format_filename(b"/x/\xff.txt", shorten=True) == "�.txt" + + +def test_prompts(runner): + @click.command() + def test(): + if click.confirm("Foo"): + click.echo("yes!") + else: + click.echo("no :(") + + result = runner.invoke(test, input="y\n") + assert not result.exception + assert result.output == "Foo [y/N]: y\nyes!\n" + + result = runner.invoke(test, input="\n") + assert not result.exception + assert result.output == "Foo [y/N]: \nno :(\n" + + result = runner.invoke(test, input="n\n") + assert not result.exception + assert result.output == "Foo [y/N]: n\nno :(\n" + + @click.command() + def test_no(): + if click.confirm("Foo", default=True): + click.echo("yes!") + else: + click.echo("no :(") + + result = runner.invoke(test_no, input="y\n") + assert not result.exception + assert result.output == "Foo [Y/n]: y\nyes!\n" + + result = runner.invoke(test_no, input="\n") + assert not result.exception + assert result.output == "Foo [Y/n]: \nyes!\n" + + result = runner.invoke(test_no, input="n\n") + assert not result.exception + assert result.output == "Foo [Y/n]: n\nno :(\n" + + +def test_confirm_repeat(runner): + cli = click.Command( + "cli", params=[click.Option(["--a/--no-a"], default=None, prompt=True)] + ) + result = runner.invoke(cli, input="\ny\n") + assert result.output == "A [y/n]: \nError: invalid input\nA [y/n]: y\n" + + +@pytest.mark.skipif(WIN, reason="Different behavior on windows.") +def test_prompts_abort(monkeypatch, capsys): + def f(_): + raise KeyboardInterrupt() + + monkeypatch.setattr("click.termui.hidden_prompt_func", f) + + try: + click.prompt("Password", hide_input=True) + except click.Abort: + click.echo("interrupted") + + out, err = capsys.readouterr() + assert out == "Password:\ninterrupted\n" + + +def _test_gen_func(): + yield "a" + yield "b" + yield "c" + yield "abc" + + +@pytest.mark.skipif(WIN, reason="Different behavior on windows.") +@pytest.mark.parametrize("cat", ["cat", "cat ", "cat "]) +@pytest.mark.parametrize( + "test", + [ + # We need lambda here, because pytest will + # reuse the parameters, and then the generators + # are already used and will not yield anymore + ("just text\n", lambda: "just text"), + ("iterable\n", lambda: ["itera", "ble"]), + ("abcabc\n", lambda: _test_gen_func), + ("abcabc\n", lambda: _test_gen_func()), + ("012345\n", lambda: (c for c in range(6))), + ], +) +def test_echo_via_pager(monkeypatch, capfd, cat, test): + monkeypatch.setitem(os.environ, "PAGER", cat) + monkeypatch.setattr(click._termui_impl, "isatty", lambda x: True) + + expected_output = test[0] + test_input = test[1]() + + click.echo_via_pager(test_input) + + out, err = capfd.readouterr() + assert out == expected_output + + +@pytest.mark.skipif(WIN, reason="Test does not make sense on Windows.") +def test_echo_color_flag(monkeypatch, capfd): + isatty = True + monkeypatch.setattr(click._compat, "isatty", lambda x: isatty) + + text = "foo" + styled_text = click.style(text, fg="red") + assert styled_text == "\x1b[31mfoo\x1b[0m" + + click.echo(styled_text, color=False) + out, err = capfd.readouterr() + assert out == f"{text}\n" + + click.echo(styled_text, color=True) + out, err = capfd.readouterr() + assert out == f"{styled_text}\n" + + isatty = True + click.echo(styled_text) + out, err = capfd.readouterr() + assert out == f"{styled_text}\n" + + isatty = False + click.echo(styled_text) + out, err = capfd.readouterr() + assert out == f"{text}\n" + + +def test_prompt_cast_default(capfd, monkeypatch): + monkeypatch.setattr(sys, "stdin", StringIO("\n")) + value = click.prompt("value", default="100", type=int) + capfd.readouterr() + assert type(value) is int + + +@pytest.mark.skipif(WIN, reason="Test too complex to make work windows.") +def test_echo_writing_to_standard_error(capfd, monkeypatch): + def emulate_input(text): + """Emulate keyboard input.""" + monkeypatch.setattr(sys, "stdin", StringIO(text)) + + click.echo("Echo to standard output") + out, err = capfd.readouterr() + assert out == "Echo to standard output\n" + assert err == "" + + click.echo("Echo to standard error", err=True) + out, err = capfd.readouterr() + assert out == "" + assert err == "Echo to standard error\n" + + emulate_input("asdlkj\n") + click.prompt("Prompt to stdin") + out, err = capfd.readouterr() + assert out == "Prompt to stdin: " + assert err == "" + + emulate_input("asdlkj\n") + click.prompt("Prompt to stderr", err=True) + out, err = capfd.readouterr() + assert out == " " + assert err == "Prompt to stderr:" + + emulate_input("y\n") + click.confirm("Prompt to stdin") + out, err = capfd.readouterr() + assert out == "Prompt to stdin [y/N]: " + assert err == "" + + emulate_input("y\n") + click.confirm("Prompt to stderr", err=True) + out, err = capfd.readouterr() + assert out == " " + assert err == "Prompt to stderr [y/N]:" + + monkeypatch.setattr(click.termui, "isatty", lambda x: True) + monkeypatch.setattr(click.termui, "getchar", lambda: " ") + + click.pause("Pause to stdout") + out, err = capfd.readouterr() + assert out == "Pause to stdout\n" + assert err == "" + + click.pause("Pause to stderr", err=True) + out, err = capfd.readouterr() + assert out == "" + assert err == "Pause to stderr\n" + + +def test_echo_with_capsys(capsys): + click.echo("Capture me.") + out, err = capsys.readouterr() + assert out == "Capture me.\n" + + +def test_open_file(runner): + @click.command() + @click.argument("filename") + def cli(filename): + with click.open_file(filename) as f: + click.echo(f.read()) + + click.echo("meep") + + with runner.isolated_filesystem(): + with open("hello.txt", "w") as f: + f.write("Cool stuff") + + result = runner.invoke(cli, ["hello.txt"]) + assert result.exception is None + assert result.output == "Cool stuff\nmeep\n" + + result = runner.invoke(cli, ["-"], input="foobar") + assert result.exception is None + assert result.output == "foobar\nmeep\n" + + +def test_open_file_pathlib_dash(runner): + @click.command() + @click.argument( + "filename", type=click.Path(allow_dash=True, path_type=pathlib.Path) + ) + def cli(filename): + click.echo(str(type(filename))) + + with click.open_file(filename) as f: + click.echo(f.read()) + + result = runner.invoke(cli, ["-"], input="value") + assert result.exception is None + assert result.output == "pathlib.Path\nvalue\n" + + +def test_open_file_ignore_errors_stdin(runner): + @click.command() + @click.argument("filename") + def cli(filename): + with click.open_file(filename, errors="ignore") as f: + click.echo(f.read()) + + result = runner.invoke(cli, ["-"], input=os.urandom(16)) + assert result.exception is None + + +def test_open_file_respects_ignore(runner): + with runner.isolated_filesystem(): + with open("test.txt", "w") as f: + f.write("Hello world!") + + with click.open_file("test.txt", encoding="utf8", errors="ignore") as f: + assert f.errors == "ignore" + + +def test_open_file_ignore_invalid_utf8(runner): + with runner.isolated_filesystem(): + with open("test.txt", "wb") as f: + f.write(b"\xe2\x28\xa1") + + with click.open_file("test.txt", encoding="utf8", errors="ignore") as f: + f.read() + + +def test_open_file_ignore_no_encoding(runner): + with runner.isolated_filesystem(): + with open("test.bin", "wb") as f: + f.write(os.urandom(16)) + + with click.open_file("test.bin", errors="ignore") as f: + f.read() + + +@pytest.mark.skipif(WIN, reason="os.chmod() is not fully supported on Windows.") +@pytest.mark.parametrize("permissions", [0o400, 0o444, 0o600, 0o644]) +def test_open_file_atomic_permissions_existing_file(runner, permissions): + with runner.isolated_filesystem(): + with open("existing.txt", "w") as f: + f.write("content") + os.chmod("existing.txt", permissions) + + @click.command() + @click.argument("filename") + def cli(filename): + click.open_file(filename, "w", atomic=True).close() + + result = runner.invoke(cli, ["existing.txt"]) + assert result.exception is None + assert stat.S_IMODE(os.stat("existing.txt").st_mode) == permissions + + +@pytest.mark.skipif(WIN, reason="os.stat() is not fully supported on Windows.") +def test_open_file_atomic_permissions_new_file(runner): + with runner.isolated_filesystem(): + + @click.command() + @click.argument("filename") + def cli(filename): + click.open_file(filename, "w", atomic=True).close() + + # Create a test file to get the expected permissions for new files + # according to the current umask. + with open("test.txt", "w"): + pass + permissions = stat.S_IMODE(os.stat("test.txt").st_mode) + + result = runner.invoke(cli, ["new.txt"]) + assert result.exception is None + assert stat.S_IMODE(os.stat("new.txt").st_mode) == permissions + + +def test_iter_keepopenfile(tmpdir): + expected = list(map(str, range(10))) + p = tmpdir.mkdir("testdir").join("testfile") + p.write("\n".join(expected)) + with p.open() as f: + for e_line, a_line in zip(expected, click.utils.KeepOpenFile(f)): + assert e_line == a_line.strip() + + +def test_iter_lazyfile(tmpdir): + expected = list(map(str, range(10))) + p = tmpdir.mkdir("testdir").join("testfile") + p.write("\n".join(expected)) + with p.open() as f: + with click.utils.LazyFile(f.name) as lf: + for e_line, a_line in zip(expected, lf): + assert e_line == a_line.strip() + + +class MockMain: + __slots__ = "__package__" + + def __init__(self, package_name): + self.__package__ = package_name + + +@pytest.mark.parametrize( + ("path", "main", "expected"), + [ + ("example.py", None, "example.py"), + (str(pathlib.Path("/foo/bar/example.py")), None, "example.py"), + ("example", None, "example"), + (str(pathlib.Path("example/__main__.py")), "example", "python -m example"), + (str(pathlib.Path("example/cli.py")), "example", "python -m example.cli"), + (str(pathlib.Path("./example")), "", "example"), + ], +) +def test_detect_program_name(path, main, expected): + assert click.utils._detect_program_name(path, _main=MockMain(main)) == expected + + +def test_expand_args(monkeypatch): + user = os.path.expanduser("~") + assert user in click.utils._expand_args(["~"]) + monkeypatch.setenv("CLICK_TEST", "hello") + assert "hello" in click.utils._expand_args(["$CLICK_TEST"]) + #assert "setup.cfg" in click.utils._expand_args(["*.cfg"]) + #assert os.path.join("docs", "conf.py") in click.utils._expand_args(["**/conf.py"]) + assert "*.not-found" in click.utils._expand_args(["*.not-found"]) + # a bad glob pattern, such as a pytest identifier, should return itself + assert click.utils._expand_args(["test.py::test_bad"])[0] == "test.py::test_bad" + + +@pytest.mark.parametrize( + ("value", "max_length", "expect"), + [ + pytest.param("", 10, "", id="empty"), + pytest.param("123 567 90", 10, "123 567 90", id="equal length, no dot"), + pytest.param("123 567 9. aaaa bbb", 10, "123 567 9.", id="sentence < max"), + pytest.param("123 567\n\n 9. aaaa bbb", 10, "123 567", id="paragraph < max"), + pytest.param("123 567 90123.", 10, "123 567...", id="truncate"), + pytest.param("123 5678 xxxxxx", 10, "123...", id="length includes suffix"), + pytest.param( + "token in ~/.netrc ciao ciao", + 20, + "token in ~/.netrc...", + id="ignore dot in word", + ), + ], +) +@pytest.mark.parametrize( + "alter", + [ + pytest.param(None, id=""), + pytest.param( + lambda text: "\n\b\n" + " ".join(text.split(" ")) + "\n", id="no-wrap mark" + ), + ], +) +def test_make_default_short_help(value, max_length, alter, expect): + assert len(expect) <= max_length + + if alter: + value = alter(value) + + out = click.utils.make_default_short_help(value, max_length) + assert out == expect |