import os.path import pathlib import pytest from conftest import check_symlink_impl import click @pytest.mark.parametrize( ("type", "value", "expect"), [ (click.IntRange(0, 5), "3", 3), (click.IntRange(5), "5", 5), (click.IntRange(5), "100", 100), (click.IntRange(max=5), "5", 5), (click.IntRange(max=5), "-100", -100), (click.IntRange(0, clamp=True), "-1", 0), (click.IntRange(max=5, clamp=True), "6", 5), (click.IntRange(0, min_open=True, clamp=True), "0", 1), (click.IntRange(max=5, max_open=True, clamp=True), "5", 4), (click.FloatRange(0.5, 1.5), "1.2", 1.2), (click.FloatRange(0.5, min_open=True), "0.51", 0.51), (click.FloatRange(max=1.5, max_open=True), "1.49", 1.49), (click.FloatRange(0.5, clamp=True), "-0.0", 0.5), (click.FloatRange(max=1.5, clamp=True), "inf", 1.5), ], ) def test_range(type, value, expect): assert type.convert(value, None, None) == expect @pytest.mark.parametrize( ("type", "value", "expect"), [ (click.IntRange(0, 5), "6", "6 is not in the range 0<=x<=5."), (click.IntRange(5), "4", "4 is not in the range x>=5."), (click.IntRange(max=5), "6", "6 is not in the range x<=5."), (click.IntRange(0, 5, min_open=True), 0, "00.5"), (click.FloatRange(max=1.5, max_open=True), 1.5, "x<1.5"), ], ) def test_range_fail(type, value, expect): with pytest.raises(click.BadParameter) as exc_info: type.convert(value, None, None) assert expect in exc_info.value.message def test_float_range_no_clamp_open(): with pytest.raises(TypeError): click.FloatRange(0, 1, max_open=True, clamp=True) sneaky = click.FloatRange(0, 1, max_open=True) sneaky.clamp = True with pytest.raises(RuntimeError): sneaky.convert("1.5", None, None) @pytest.mark.parametrize( ("nargs", "multiple", "default", "expect"), [ (2, False, None, None), (2, False, (None, None), (None, None)), (None, True, None, ()), (None, True, (None, None), (None, None)), (2, True, None, ()), (2, True, [(None, None)], ((None, None),)), (-1, None, None, ()), ], ) def test_cast_multi_default(runner, nargs, multiple, default, expect): if nargs == -1: param = click.Argument(["a"], nargs=nargs, default=default) else: param = click.Option(["-a"], nargs=nargs, multiple=multiple, default=default) cli = click.Command("cli", params=[param], callback=lambda a: a) result = runner.invoke(cli, standalone_mode=False) assert result.exception is None assert result.return_value == expect @pytest.mark.parametrize( ("cls", "expect"), [ (None, "a/b/c.txt"), (str, "a/b/c.txt"), (bytes, b"a/b/c.txt"), (pathlib.Path, pathlib.Path("a", "b", "c.txt")), ], ) def test_path_type(runner, cls, expect): cli = click.Command( "cli", params=[click.Argument(["p"], type=click.Path(path_type=cls))], callback=lambda p: p, ) result = runner.invoke(cli, ["a/b/c.txt"], standalone_mode=False) assert result.exception is None assert result.return_value == expect @pytest.mark.skipif(not check_symlink_impl(), reason="symlink not allowed on device") @pytest.mark.parametrize( ("sym_file", "abs_fun"), [ (("relative_symlink",), os.path.basename), (("test", "absolute_symlink"), lambda x: x), ], ) def test_symlink_resolution(tmpdir, sym_file, abs_fun): """This test ensures symlinks are properly resolved by click""" tempdir = str(tmpdir) real_path = os.path.join(tempdir, "test_file") sym_path = os.path.join(tempdir, *sym_file) # create dirs and files os.makedirs(os.path.join(tempdir, "test"), exist_ok=True) open(real_path, "w").close() os.symlink(abs_fun(real_path), sym_path) # test ctx = click.Context(click.Command("do_stuff")) rv = click.Path(resolve_path=True).convert(sym_path, None, ctx) # os.readlink prepends path prefixes to absolute # links in windows. # https://docs.microsoft.com/en-us/windows/win32/ # ... fileio/naming-a-file#win32-file-namespaces # # Here we strip win32 path prefix from the resolved path rv_drive, rv_path = os.path.splitdrive(rv) stripped_rv_drive = rv_drive.split(os.path.sep)[-1] rv = os.path.join(stripped_rv_drive, rv_path) assert rv == real_path