New upstream version 8.0.3
This commit is contained in:
parent
46bc5bd117
commit
4555a76448
13
CHANGES.rst
13
CHANGES.rst
|
@ -1,5 +1,18 @@
|
||||||
.. currentmodule:: click
|
.. currentmodule:: click
|
||||||
|
|
||||||
|
Version 8.0.3
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Released 2021-10-10
|
||||||
|
|
||||||
|
- Fix issue with ``Path(resolve_path=True)`` type creating invalid
|
||||||
|
paths. :issue:`2088`
|
||||||
|
- Importing ``readline`` does not cause the ``confirm()`` prompt to
|
||||||
|
disappear when pressing backspace. :issue:`2092`
|
||||||
|
- Any default values injected by ``invoke()`` are cast to the
|
||||||
|
corresponding parameter's type. :issue:`2089, 2090`
|
||||||
|
|
||||||
|
|
||||||
Version 8.0.2
|
Version 8.0.2
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
@ -72,4 +72,4 @@ from .utils import get_os_args as get_os_args
|
||||||
from .utils import get_text_stream as get_text_stream
|
from .utils import get_text_stream as get_text_stream
|
||||||
from .utils import open_file as open_file
|
from .utils import open_file as open_file
|
||||||
|
|
||||||
__version__ = "8.0.2"
|
__version__ = "8.0.3"
|
||||||
|
|
|
@ -739,7 +739,9 @@ class Context:
|
||||||
|
|
||||||
for param in other_cmd.params:
|
for param in other_cmd.params:
|
||||||
if param.name not in kwargs and param.expose_value:
|
if param.name not in kwargs and param.expose_value:
|
||||||
kwargs[param.name] = param.get_default(ctx) # type: ignore
|
kwargs[param.name] = param.type_cast_value( # type: ignore
|
||||||
|
ctx, param.get_default(ctx)
|
||||||
|
)
|
||||||
|
|
||||||
# Track all kwargs as params, so that forward() will pass
|
# Track all kwargs as params, so that forward() will pass
|
||||||
# them on in subsequent calls.
|
# them on in subsequent calls.
|
||||||
|
|
|
@ -231,8 +231,10 @@ def confirm(
|
||||||
try:
|
try:
|
||||||
# Write the prompt separately so that we get nice
|
# Write the prompt separately so that we get nice
|
||||||
# coloring through colorama on Windows
|
# coloring through colorama on Windows
|
||||||
echo(prompt, nl=False, err=err)
|
echo(prompt.rstrip(" "), nl=False, err=err)
|
||||||
value = visible_prompt_func("").lower().strip()
|
# Echo a space to stdout to work around an issue where
|
||||||
|
# readline causes backspace to clear the whole line.
|
||||||
|
value = visible_prompt_func(" ").lower().strip()
|
||||||
except (KeyboardInterrupt, EOFError):
|
except (KeyboardInterrupt, EOFError):
|
||||||
raise Abort() from None
|
raise Abort() from None
|
||||||
if value in ("y", "yes"):
|
if value in ("y", "yes"):
|
||||||
|
|
|
@ -836,20 +836,11 @@ class Path(ParamType):
|
||||||
|
|
||||||
if not is_dash:
|
if not is_dash:
|
||||||
if self.resolve_path:
|
if self.resolve_path:
|
||||||
# Get the absolute directory containing the path.
|
# os.path.realpath doesn't resolve symlinks on Windows
|
||||||
dir_ = os.path.dirname(os.path.abspath(rv))
|
# until Python 3.8. Use pathlib for now.
|
||||||
|
import pathlib
|
||||||
|
|
||||||
# Resolve a symlink. realpath on Windows Python < 3.9
|
rv = os.fsdecode(pathlib.Path(rv).resolve())
|
||||||
# doesn't resolve symlinks. This might return a relative
|
|
||||||
# path even if the path to the link is absolute.
|
|
||||||
if os.path.islink(rv):
|
|
||||||
rv = os.readlink(rv)
|
|
||||||
|
|
||||||
# Join dir_ with the resolved symlink if the resolved
|
|
||||||
# path is relative. This will make it relative to the
|
|
||||||
# original containing directory.
|
|
||||||
if not os.path.isabs(rv):
|
|
||||||
rv = os.path.join(dir_, rv)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
st = os.stat(rv)
|
st = os.stat(rv)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -12,20 +11,17 @@ def runner(request):
|
||||||
return CliRunner()
|
return CliRunner()
|
||||||
|
|
||||||
|
|
||||||
def check_symlink_impl():
|
def _check_symlinks_supported():
|
||||||
"""This function checks if using symlinks is allowed
|
with tempfile.TemporaryDirectory(prefix="click-pytest-") as tempdir:
|
||||||
on the host machine"""
|
target = os.path.join(tempdir, "target")
|
||||||
tempdir = tempfile.mkdtemp(prefix="click-")
|
open(target, "w").close()
|
||||||
test_pth = os.path.join(tempdir, "check_sym_impl")
|
link = os.path.join(tempdir, "link")
|
||||||
sym_pth = os.path.join(tempdir, "link")
|
|
||||||
open(test_pth, "w").close()
|
try:
|
||||||
rv = True
|
os.symlink(target, link)
|
||||||
try:
|
return True
|
||||||
os.symlink(test_pth, sym_pth)
|
except OSError:
|
||||||
except (NotImplementedError, OSError):
|
return False
|
||||||
# Creating symlinks on Windows require elevated access.
|
|
||||||
# OSError is thrown if the function is called without it.
|
|
||||||
rv = False
|
symlinks_supported = _check_symlinks_supported()
|
||||||
finally:
|
|
||||||
shutil.rmtree(tempdir, ignore_errors=True)
|
|
||||||
return rv
|
|
||||||
|
|
|
@ -246,15 +246,17 @@ def test_other_command_invoke_with_defaults(runner):
|
||||||
return ctx.invoke(other_cmd)
|
return ctx.invoke(other_cmd)
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.option("--foo", type=click.INT, default=42)
|
@click.option("-a", type=click.INT, default=42)
|
||||||
|
@click.option("-b", type=click.INT, default="15")
|
||||||
|
@click.option("-c", multiple=True)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def other_cmd(ctx, foo):
|
def other_cmd(ctx, a, b, c):
|
||||||
assert ctx.info_name == "other-cmd"
|
return ctx.info_name, a, b, c
|
||||||
click.echo(foo)
|
|
||||||
|
|
||||||
result = runner.invoke(cli, [])
|
result = runner.invoke(cli, standalone_mode=False)
|
||||||
assert not result.exception
|
# invoke should type cast default values, str becomes int, empty
|
||||||
assert result.output == "42\n"
|
# multiple should be empty tuple instead of None
|
||||||
|
assert result.return_value == ("other-cmd", 42, 15, ())
|
||||||
|
|
||||||
|
|
||||||
def test_invoked_subcommand(runner):
|
def test_invoked_subcommand(runner):
|
||||||
|
|
|
@ -2,7 +2,7 @@ import os.path
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from conftest import check_symlink_impl
|
from conftest import symlinks_supported
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
|
@ -104,37 +104,27 @@ def test_path_type(runner, cls, expect):
|
||||||
assert result.return_value == expect
|
assert result.return_value == expect
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not check_symlink_impl(), reason="symlink not allowed on device")
|
@pytest.mark.skipif(
|
||||||
@pytest.mark.parametrize(
|
not symlinks_supported, reason="The current OS or FS doesn't support symlinks."
|
||||||
("sym_file", "abs_fun"),
|
|
||||||
[
|
|
||||||
(("relative_symlink",), os.path.basename),
|
|
||||||
(("test", "absolute_symlink"), lambda x: x),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
def test_symlink_resolution(tmpdir, sym_file, abs_fun):
|
def test_path_resolve_symlink(tmp_path, runner):
|
||||||
"""This test ensures symlinks are properly resolved by click"""
|
test_file = tmp_path / "file"
|
||||||
tempdir = str(tmpdir)
|
test_file_str = os.fsdecode(test_file)
|
||||||
real_path = os.path.join(tempdir, "test_file")
|
test_file.write_text("")
|
||||||
sym_path = os.path.join(tempdir, *sym_file)
|
|
||||||
|
|
||||||
# create dirs and files
|
path_type = click.Path(resolve_path=True)
|
||||||
os.makedirs(os.path.join(tempdir, "test"), exist_ok=True)
|
param = click.Argument(["a"], type=path_type)
|
||||||
open(real_path, "w").close()
|
ctx = click.Context(click.Command("cli", params=[param]))
|
||||||
os.symlink(abs_fun(real_path), sym_path)
|
|
||||||
|
|
||||||
# test
|
test_dir = tmp_path / "dir"
|
||||||
ctx = click.Context(click.Command("do_stuff"))
|
test_dir.mkdir()
|
||||||
rv = click.Path(resolve_path=True).convert(sym_path, None, ctx)
|
|
||||||
|
|
||||||
# os.readlink prepends path prefixes to absolute
|
abs_link = test_dir / "abs"
|
||||||
# links in windows.
|
abs_link.symlink_to(test_file)
|
||||||
# https://docs.microsoft.com/en-us/windows/win32/
|
abs_rv = path_type.convert(os.fsdecode(abs_link), param, ctx)
|
||||||
# ... fileio/naming-a-file#win32-file-namespaces
|
assert abs_rv == test_file_str
|
||||||
#
|
|
||||||
# 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
|
rel_link = test_dir / "rel"
|
||||||
|
rel_link.symlink_to(pathlib.Path("..") / "file")
|
||||||
|
rel_rv = path_type.convert(os.fsdecode(rel_link), param, ctx)
|
||||||
|
assert rel_rv == test_file_str
|
||||||
|
|
|
@ -275,8 +275,8 @@ def test_echo_writing_to_standard_error(capfd, monkeypatch):
|
||||||
emulate_input("y\n")
|
emulate_input("y\n")
|
||||||
click.confirm("Prompt to stderr", err=True)
|
click.confirm("Prompt to stderr", err=True)
|
||||||
out, err = capfd.readouterr()
|
out, err = capfd.readouterr()
|
||||||
assert out == ""
|
assert out == " "
|
||||||
assert err == "Prompt to stderr [y/N]: "
|
assert err == "Prompt to stderr [y/N]:"
|
||||||
|
|
||||||
monkeypatch.setattr(click.termui, "isatty", lambda x: True)
|
monkeypatch.setattr(click.termui, "isatty", lambda x: True)
|
||||||
monkeypatch.setattr(click.termui, "getchar", lambda: " ")
|
monkeypatch.setattr(click.termui, "getchar", lambda: " ")
|
||||||
|
|
Loading…
Reference in a new issue