From 079f91a2396c5c9264f34f95894259b7b15eb89c Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 6 Mar 2020 12:45:31 -0800 Subject: [PATCH 01/10] move to src directory --- setup.py | 7 +++++-- {click => src/click}/__init__.py | 0 {click => src/click}/_bashcomplete.py | 0 {click => src/click}/_compat.py | 0 {click => src/click}/_termui_impl.py | 0 {click => src/click}/_textwrap.py | 0 {click => src/click}/_unicodefun.py | 0 {click => src/click}/_winconsole.py | 0 {click => src/click}/core.py | 0 {click => src/click}/decorators.py | 0 {click => src/click}/exceptions.py | 0 {click => src/click}/formatting.py | 0 {click => src/click}/globals.py | 0 {click => src/click}/parser.py | 0 {click => src/click}/termui.py | 0 {click => src/click}/testing.py | 0 {click => src/click}/types.py | 0 {click => src/click}/utils.py | 0 18 files changed, 5 insertions(+), 2 deletions(-) rename {click => src/click}/__init__.py (100%) rename {click => src/click}/_bashcomplete.py (100%) rename {click => src/click}/_compat.py (100%) rename {click => src/click}/_termui_impl.py (100%) rename {click => src/click}/_textwrap.py (100%) rename {click => src/click}/_unicodefun.py (100%) rename {click => src/click}/_winconsole.py (100%) rename {click => src/click}/core.py (100%) rename {click => src/click}/decorators.py (100%) rename {click => src/click}/exceptions.py (100%) rename {click => src/click}/formatting.py (100%) rename {click => src/click}/globals.py (100%) rename {click => src/click}/parser.py (100%) rename {click => src/click}/termui.py (100%) rename {click => src/click}/testing.py (100%) rename {click => src/click}/types.py (100%) rename {click => src/click}/utils.py (100%) diff --git a/setup.py b/setup.py index b038a5cf8..ab2cffc68 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,13 @@ import io import re + +from setuptools import find_packages from setuptools import setup with io.open("README.rst", "rt", encoding="utf8") as f: readme = f.read() -with io.open("click/__init__.py", "rt", encoding="utf8") as f: +with io.open("src/click/__init__.py", "rt", encoding="utf8") as f: version = re.search(r"__version__ = '(.*?)'", f.read()).group(1) setup( @@ -22,7 +24,8 @@ maintainer_email="contact@palletsprojects.com", description="Composable command line interface toolkit", long_description=readme, - packages=["click"], + packages=find_packages("src"), + package_dir={"": "src"}, include_package_data=True, python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", classifiers=[ diff --git a/click/__init__.py b/src/click/__init__.py similarity index 100% rename from click/__init__.py rename to src/click/__init__.py diff --git a/click/_bashcomplete.py b/src/click/_bashcomplete.py similarity index 100% rename from click/_bashcomplete.py rename to src/click/_bashcomplete.py diff --git a/click/_compat.py b/src/click/_compat.py similarity index 100% rename from click/_compat.py rename to src/click/_compat.py diff --git a/click/_termui_impl.py b/src/click/_termui_impl.py similarity index 100% rename from click/_termui_impl.py rename to src/click/_termui_impl.py diff --git a/click/_textwrap.py b/src/click/_textwrap.py similarity index 100% rename from click/_textwrap.py rename to src/click/_textwrap.py diff --git a/click/_unicodefun.py b/src/click/_unicodefun.py similarity index 100% rename from click/_unicodefun.py rename to src/click/_unicodefun.py diff --git a/click/_winconsole.py b/src/click/_winconsole.py similarity index 100% rename from click/_winconsole.py rename to src/click/_winconsole.py diff --git a/click/core.py b/src/click/core.py similarity index 100% rename from click/core.py rename to src/click/core.py diff --git a/click/decorators.py b/src/click/decorators.py similarity index 100% rename from click/decorators.py rename to src/click/decorators.py diff --git a/click/exceptions.py b/src/click/exceptions.py similarity index 100% rename from click/exceptions.py rename to src/click/exceptions.py diff --git a/click/formatting.py b/src/click/formatting.py similarity index 100% rename from click/formatting.py rename to src/click/formatting.py diff --git a/click/globals.py b/src/click/globals.py similarity index 100% rename from click/globals.py rename to src/click/globals.py diff --git a/click/parser.py b/src/click/parser.py similarity index 100% rename from click/parser.py rename to src/click/parser.py diff --git a/click/termui.py b/src/click/termui.py similarity index 100% rename from click/termui.py rename to src/click/termui.py diff --git a/click/testing.py b/src/click/testing.py similarity index 100% rename from click/testing.py rename to src/click/testing.py diff --git a/click/types.py b/src/click/types.py similarity index 100% rename from click/types.py rename to src/click/types.py diff --git a/click/utils.py b/src/click/utils.py similarity index 100% rename from click/utils.py rename to src/click/utils.py From c7812823f3e165f2979d888e19a82590b0169917 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 6 Mar 2020 13:08:04 -0800 Subject: [PATCH 02/10] add pre-commit config --- .pre-commit-config.yaml | 27 +++++++++++++++++++++++++++ setup.cfg | 24 +++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..579fe9c02 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,27 @@ +repos: + - repo: https://github.com/asottile/pyupgrade + rev: v2.1.0 + hooks: + - id: pyupgrade + - repo: https://github.com/asottile/reorder_python_imports + rev: v2.0.0 + hooks: + - id: reorder-python-imports + args: ["--application-directories", "src"] + - repo: https://github.com/ambv/black + rev: 19.10b0 + hooks: + - id: black + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.7.9 + hooks: + - id: flake8 + additional_dependencies: + - flake8-bugbear + - flake8-implicit-str-concat + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.5.0 + hooks: + - id: check-byte-order-marker + - id: trailing-whitespace + - id: end-of-file-fixer diff --git a/setup.cfg b/setup.cfg index a6751157d..4335cbf77 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,10 +12,32 @@ filterwarnings = [coverage:run] branch = True source = - click + src tests [coverage:paths] source = click */site-packages + +[flake8] +# B = bugbear +# E = pycodestyle errors +# F = flake8 pyflakes +# W = pycodestyle warnings +# B9 = bugbear opinions +select = B, E, F, W, B9 +ignore = + # slice notation whitespace, invalid + E203 + # line length, handled by bugbear B950 + E501 + # bare except, handled by bugbear B001 + E722 + # bin op line break, invalid + W503 +# up to 88 allowed by bugbear B950 +max-line-length = 80 +per-file-ignores = + # __init__ module exports names + src/click/__init__.py: F401 From 488739dfe0d51f415c7b20466648cc519962ecbb Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 6 Mar 2020 13:19:03 -0800 Subject: [PATCH 03/10] apply reorder-python-imports --- examples/aliases/aliases.py | 5 +- examples/bashcompletion/bashcompletion.py | 3 +- examples/complex/complex/cli.py | 1 + examples/complex/complex/commands/cmd_init.py | 3 +- .../complex/complex/commands/cmd_status.py | 3 +- examples/imagepipe/imagepipe.py | 8 +- examples/repo/repo.py | 2 +- examples/termui/termui.py | 12 +- examples/validation/validation.py | 1 + src/click/__init__.py | 142 ++++++++---------- src/click/_bashcomplete.py | 6 +- src/click/_compat.py | 5 +- src/click/_termui_impl.py | 23 ++- src/click/_unicodefun.py | 3 +- src/click/_winconsole.py | 32 +++- src/click/core.py | 45 ++++-- src/click/decorators.py | 5 +- src/click/exceptions.py | 4 +- src/click/formatting.py | 6 +- src/click/globals.py | 1 - src/click/parser.py | 8 +- src/click/termui.py | 26 ++-- src/click/testing.py | 11 +- src/click/types.py | 12 +- src/click/utils.py | 27 +++- tests/conftest.py | 4 +- tests/test_arguments.py | 7 +- tests/test_bashcomplete.py | 1 - tests/test_basic.py | 1 + tests/test_chain.py | 4 +- tests/test_commands.py | 1 - tests/test_compat.py | 6 +- tests/test_imports.py | 2 +- tests/test_options.py | 5 +- tests/test_termui.py | 5 +- tests/test_testing.py | 6 +- tests/test_utils.py | 6 +- 37 files changed, 254 insertions(+), 188 deletions(-) diff --git a/examples/aliases/aliases.py b/examples/aliases/aliases.py index 299d3a5ae..a377a58cf 100644 --- a/examples/aliases/aliases.py +++ b/examples/aliases/aliases.py @@ -1,10 +1,11 @@ import os + import click try: - import ConfigParser as configparser -except ImportError: import configparser +except ImportError: + import ConfigParser as configparser class Config(object): diff --git a/examples/bashcompletion/bashcompletion.py b/examples/bashcompletion/bashcompletion.py index 107284003..5dacae5d5 100644 --- a/examples/bashcompletion/bashcompletion.py +++ b/examples/bashcompletion/bashcompletion.py @@ -1,6 +1,7 @@ -import click import os +import click + @click.group() def cli(): diff --git a/examples/complex/complex/cli.py b/examples/complex/complex/cli.py index 0d10c628c..3ef6faeeb 100644 --- a/examples/complex/complex/cli.py +++ b/examples/complex/complex/cli.py @@ -1,5 +1,6 @@ import os import sys + import click diff --git a/examples/complex/complex/commands/cmd_init.py b/examples/complex/complex/commands/cmd_init.py index a77116bea..5da8df24b 100644 --- a/examples/complex/complex/commands/cmd_init.py +++ b/examples/complex/complex/commands/cmd_init.py @@ -1,6 +1,7 @@ -import click from complex.cli import pass_environment +import click + @click.command('init', short_help='Initializes a repo.') @click.argument('path', required=False, type=click.Path(resolve_path=True)) diff --git a/examples/complex/complex/commands/cmd_status.py b/examples/complex/complex/commands/cmd_status.py index 12229c42f..ac7976747 100644 --- a/examples/complex/complex/commands/cmd_status.py +++ b/examples/complex/complex/commands/cmd_status.py @@ -1,6 +1,7 @@ -import click from complex.cli import pass_environment +import click + @click.command('status', short_help='Shows file changes.') @pass_environment diff --git a/examples/imagepipe/imagepipe.py b/examples/imagepipe/imagepipe.py index 37a152113..f6f04ef7f 100644 --- a/examples/imagepipe/imagepipe.py +++ b/examples/imagepipe/imagepipe.py @@ -1,6 +1,10 @@ -import click from functools import update_wrapper -from PIL import Image, ImageFilter, ImageEnhance + +from PIL import Image +from PIL import ImageEnhance +from PIL import ImageFilter + +import click @click.group(chain=True) diff --git a/examples/repo/repo.py b/examples/repo/repo.py index 2b1992d3e..c14a82792 100644 --- a/examples/repo/repo.py +++ b/examples/repo/repo.py @@ -1,6 +1,6 @@ import os -import sys import posixpath +import sys import click diff --git a/examples/termui/termui.py b/examples/termui/termui.py index 793afa419..08f6a04ee 100644 --- a/examples/termui/termui.py +++ b/examples/termui/termui.py @@ -1,13 +1,9 @@ # coding: utf-8 -import click import math -import time import random +import time -try: - range_type = xrange -except NameError: - range_type = range +import click @click.group() @@ -28,7 +24,7 @@ def colordemo(): def pager(): """Demonstrates using the pager.""" lines = [] - for x in range_type(200): + for x in range(200): lines.append('%s. Hello World!' % click.style(str(x), fg='green')) click.echo_via_pager('\n'.join(lines)) @@ -38,7 +34,7 @@ def pager(): help='The number of items to process.') def progress(count): """Demonstrates the progress bar.""" - items = range_type(count) + items = range(count) def process_slowly(item): time.sleep(0.002 * random.random()) diff --git a/examples/validation/validation.py b/examples/validation/validation.py index 00fa0a600..5cfe5f67e 100644 --- a/examples/validation/validation.py +++ b/examples/validation/validation.py @@ -1,4 +1,5 @@ import click + try: from urllib import parse as urlparse except ImportError: diff --git a/src/click/__init__.py b/src/click/__init__.py index 02807b030..caddd63b2 100644 --- a/src/click/__init__.py +++ b/src/click/__init__.py @@ -1,91 +1,79 @@ -# -*- coding: utf-8 -*- """ Click is a simple Python module inspired by the stdlib optparse to make writing command line scripts fun. Unlike other modules, it's based around a simple API that does not come with too much magic and is composable. """ - -# Core classes -from .core import Context, BaseCommand, Command, MultiCommand, Group, \ - CommandCollection, Parameter, Option, Argument - -# Globals +from .core import Argument +from .core import BaseCommand +from .core import Command +from .core import CommandCollection +from .core import Context +from .core import Group +from .core import MultiCommand +from .core import Option +from .core import Parameter +from .decorators import argument +from .decorators import command +from .decorators import confirmation_option +from .decorators import group +from .decorators import help_option +from .decorators import make_pass_decorator +from .decorators import option +from .decorators import pass_context +from .decorators import pass_obj +from .decorators import password_option +from .decorators import version_option +from .exceptions import Abort +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import FileError +from .exceptions import MissingParameter +from .exceptions import NoSuchOption +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import wrap_text from .globals import get_current_context - -# Decorators -from .decorators import pass_context, pass_obj, make_pass_decorator, \ - command, group, argument, option, confirmation_option, \ - password_option, version_option, help_option - -# Types -from .types import ParamType, File, Path, Choice, IntRange, Tuple, \ - DateTime, STRING, INT, FLOAT, BOOL, UUID, UNPROCESSED, FloatRange - -# Utilities -from .utils import echo, get_binary_stream, get_text_stream, open_file, \ - format_filename, get_app_dir, get_os_args - -# Terminal functions -from .termui import prompt, confirm, get_terminal_size, echo_via_pager, \ - progressbar, clear, style, unstyle, secho, edit, launch, getchar, \ - pause - -# Exceptions -from .exceptions import ClickException, UsageError, BadParameter, \ - FileError, Abort, NoSuchOption, BadOptionUsage, BadArgumentUsage, \ - MissingParameter - -# Formatting -from .formatting import HelpFormatter, wrap_text - -# Parsing from .parser import OptionParser - - -__all__ = [ - # Core classes - 'Context', 'BaseCommand', 'Command', 'MultiCommand', 'Group', - 'CommandCollection', 'Parameter', 'Option', 'Argument', - - # Globals - 'get_current_context', - - # Decorators - 'pass_context', 'pass_obj', 'make_pass_decorator', 'command', 'group', - 'argument', 'option', 'confirmation_option', 'password_option', - 'version_option', 'help_option', - - # Types - 'ParamType', 'File', 'Path', 'Choice', 'IntRange', 'Tuple', - 'DateTime', 'STRING', 'INT', 'FLOAT', 'BOOL', 'UUID', 'UNPROCESSED', - 'FloatRange', - - # Utilities - 'echo', 'get_binary_stream', 'get_text_stream', 'open_file', - 'format_filename', 'get_app_dir', 'get_os_args', - - # Terminal functions - 'prompt', 'confirm', 'get_terminal_size', 'echo_via_pager', - 'progressbar', 'clear', 'style', 'unstyle', 'secho', 'edit', 'launch', - 'getchar', 'pause', - - # Exceptions - 'ClickException', 'UsageError', 'BadParameter', 'FileError', - 'Abort', 'NoSuchOption', 'BadOptionUsage', 'BadArgumentUsage', - 'MissingParameter', - - # Formatting - 'HelpFormatter', 'wrap_text', - - # Parsing - 'OptionParser', -] - +from .termui import clear +from .termui import confirm +from .termui import echo_via_pager +from .termui import edit +from .termui import get_terminal_size +from .termui import getchar +from .termui import launch +from .termui import pause +from .termui import progressbar +from .termui import prompt +from .termui import secho +from .termui import style +from .termui import unstyle +from .types import BOOL +from .types import Choice +from .types import DateTime +from .types import File +from .types import FLOAT +from .types import FloatRange +from .types import INT +from .types import IntRange +from .types import ParamType +from .types import Path +from .types import STRING +from .types import Tuple +from .types import UNPROCESSED +from .types import UUID +from .utils import echo +from .utils import format_filename +from .utils import get_app_dir +from .utils import get_binary_stream +from .utils import get_os_args +from .utils import get_text_stream +from .utils import open_file # Controls if click should emit the warning about the use of unicode # literals. disable_unicode_literals_warning = False - __version__ = '7.1.dev' diff --git a/src/click/_bashcomplete.py b/src/click/_bashcomplete.py index 9ba893c7e..ebcf44f33 100644 --- a/src/click/_bashcomplete.py +++ b/src/click/_bashcomplete.py @@ -2,10 +2,12 @@ import os import re -from .utils import echo +from .core import Argument +from .core import MultiCommand +from .core import Option from .parser import split_arg_string -from .core import MultiCommand, Option, Argument from .types import Choice +from .utils import echo try: from collections import abc diff --git a/src/click/_compat.py b/src/click/_compat.py index 676285448..7b7a55f69 100644 --- a/src/click/_compat.py +++ b/src/click/_compat.py @@ -1,11 +1,10 @@ -import re +import codecs import io import os +import re import sys -import codecs from weakref import WeakKeyDictionary - PY2 = sys.version_info[0] == 2 CYGWIN = sys.platform.startswith('cygwin') MSYS2 = sys.platform.startswith('win') and ('GCC' in sys.version) diff --git a/src/click/_termui_impl.py b/src/click/_termui_impl.py index 3fbf4779c..e6974a70b 100644 --- a/src/click/_termui_impl.py +++ b/src/click/_termui_impl.py @@ -4,18 +4,25 @@ import time of Click down, some infrequently used functionality is placed in this module and only imported as needed. """ - +import contextlib +import math import os import sys import time -import math -import contextlib -from ._compat import _default_text_stdout, range_type, isatty, \ - open_stream, shlex_quote, strip_ansi, term_len, get_best_encoding, WIN, \ - int_types, CYGWIN -from .utils import echo -from .exceptions import ClickException +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import int_types +from ._compat import isatty +from ._compat import open_stream +from ._compat import range_type +from ._compat import shlex_quote +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo if os.name == 'nt': BEFORE_BAR = '\r' diff --git a/src/click/_unicodefun.py b/src/click/_unicodefun.py index aeaa4084c..c0e9a27e5 100644 --- a/src/click/_unicodefun.py +++ b/src/click/_unicodefun.py @@ -1,10 +1,9 @@ +import codecs import os import sys -import codecs from ._compat import PY2 - # If someone wants to vendor click, we want to ensure the # correct package is discovered. Ideally we could use a # relative import here but unfortunately Python does not diff --git a/src/click/_winconsole.py b/src/click/_winconsole.py index 5458c3020..686a23975 100644 --- a/src/click/_winconsole.py +++ b/src/click/_winconsole.py @@ -7,25 +7,41 @@ # compared to the original patches as we do not need to patch # the entire interpreter but just work in our little world of # echo and prmopt. - +import ctypes import io import os import sys -import zlib import time -import ctypes +import zlib +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import windll +from ctypes import WinError +from ctypes import WINFUNCTYPE +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + import msvcrt -from ._compat import _NonClosingTextIOWrapper, text_type, PY2 -from ctypes import byref, POINTER, c_int, c_char, c_char_p, \ - c_void_p, py_object, c_ssize_t, c_ulong, windll, WINFUNCTYPE, \ - WinError + +from ._compat import _NonClosingTextIOWrapper +from ._compat import PY2 +from ._compat import text_type + try: from ctypes import pythonapi PyObject_GetBuffer = pythonapi.PyObject_GetBuffer PyBuffer_Release = pythonapi.PyBuffer_Release except ImportError: pythonapi = None -from ctypes.wintypes import DWORD, LPWSTR, LPCWSTR, HANDLE c_ssize_p = POINTER(c_ssize_t) diff --git a/src/click/core.py b/src/click/core.py index 7066cacbe..96c3ebc36 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -3,26 +3,41 @@ import os import sys from contextlib import contextmanager -from itertools import repeat from functools import update_wrapper +from itertools import repeat -from .types import convert_type, IntRange, BOOL -from .utils import PacifyFlushWrapper, make_str, make_default_short_help, \ - echo, get_os_args -from .exceptions import ClickException, UsageError, BadParameter, Abort, \ - MissingParameter, Exit -from .termui import prompt, confirm, style -from .formatting import HelpFormatter, join_options -from .parser import OptionParser, split_opt -from .globals import push_context, pop_context - -from ._compat import PY2, isidentifier, iteritems, string_types -from ._unicodefun import _check_for_unicode_literals, _verify_python3_env - +from ._compat import isidentifier +from ._compat import iteritems +from ._compat import PY2 +from ._compat import string_types +from ._unicodefun import _check_for_unicode_literals +from ._unicodefun import _verify_python3_env +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import OptionParser +from .parser import split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .types import BOOL +from .types import convert_type +from .types import IntRange +from .utils import echo +from .utils import get_os_args +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper _missing = object() - SUBCOMMAND_METAVAR = 'COMMAND [ARGS]...' SUBCOMMANDS_METAVAR = 'COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...' diff --git a/src/click/decorators.py b/src/click/decorators.py index 1f10e73c2..bb5a8bc36 100644 --- a/src/click/decorators.py +++ b/src/click/decorators.py @@ -1,12 +1,11 @@ -import sys import inspect - +import sys from functools import update_wrapper from ._compat import iteritems from ._unicodefun import _check_for_unicode_literals -from .utils import echo from .globals import get_current_context +from .utils import echo def pass_context(f): diff --git a/src/click/exceptions.py b/src/click/exceptions.py index 21db907a7..4aea4f864 100644 --- a/src/click/exceptions.py +++ b/src/click/exceptions.py @@ -1,4 +1,6 @@ -from ._compat import PY2, filename_to_ui, get_text_stderr +from ._compat import filename_to_ui +from ._compat import get_text_stderr +from ._compat import PY2 from .utils import echo diff --git a/src/click/formatting.py b/src/click/formatting.py index 4ae5f07ef..4426d9cfe 100644 --- a/src/click/formatting.py +++ b/src/click/formatting.py @@ -1,8 +1,8 @@ from contextlib import contextmanager -from .termui import get_terminal_size -from .parser import split_opt -from ._compat import term_len +from ._compat import term_len +from .parser import split_opt +from .termui import get_terminal_size # Can force a width. This is used by the test system FORCED_WIDTH = None diff --git a/src/click/globals.py b/src/click/globals.py index 1b85c76ee..e5b88d3c9 100644 --- a/src/click/globals.py +++ b/src/click/globals.py @@ -1,6 +1,5 @@ from threading import local - _local = local() diff --git a/src/click/parser.py b/src/click/parser.py index 6f2600fd2..c060313f5 100644 --- a/src/click/parser.py +++ b/src/click/parser.py @@ -18,11 +18,13 @@ Copyright 2001-2006 Gregory P. Ward. All rights reserved. Copyright 2002-2006 Python Software Foundation. All rights reserved. """ - import re from collections import deque -from .exceptions import UsageError, NoSuchOption, BadOptionUsage, \ - BadArgumentUsage + +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError def _unpack_args(args, nargs_spec): diff --git a/src/click/termui.py b/src/click/termui.py index fa2727a9b..5448b80d8 100644 --- a/src/click/termui.py +++ b/src/click/termui.py @@ -1,19 +1,27 @@ +import inspect import io +import itertools import os -import sys import struct -import inspect -import itertools +import sys -from ._compat import raw_input, text_type, string_types, \ - isatty, strip_ansi, get_winterm_size, DEFAULT_COLUMNS, WIN -from .utils import echo -from .exceptions import Abort, UsageError -from .types import convert_type, Choice, Path +from ._compat import DEFAULT_COLUMNS +from ._compat import get_winterm_size +from ._compat import isatty +from ._compat import raw_input +from ._compat import string_types +from ._compat import strip_ansi +from ._compat import text_type +from ._compat import WIN +from .exceptions import Abort +from .exceptions import UsageError from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import Path +from .utils import echo from .utils import LazyFile - # The prompt functions to use. The doc tools currently override these # functions to customize how they work. visible_prompt_func = raw_input diff --git a/src/click/testing.py b/src/click/testing.py index 343b00723..16cf42c08 100644 --- a/src/click/testing.py +++ b/src/click/testing.py @@ -1,12 +1,13 @@ +import contextlib import os -import sys +import shlex import shutil +import sys import tempfile -import contextlib -import shlex - -from ._compat import iteritems, PY2, string_types +from ._compat import iteritems +from ._compat import PY2 +from ._compat import string_types # If someone wants to vendor click, we want to ensure the # correct package is discovered. Ideally we could use a diff --git a/src/click/types.py b/src/click/types.py index f5d06a129..6bb69f07f 100644 --- a/src/click/types.py +++ b/src/click/types.py @@ -2,10 +2,16 @@ import stat from datetime import datetime -from ._compat import open_stream, text_type, filename_to_ui, \ - get_filesystem_encoding, get_streerror, _get_argv_encoding, PY2 +from ._compat import _get_argv_encoding +from ._compat import filename_to_ui +from ._compat import get_filesystem_encoding +from ._compat import get_streerror +from ._compat import open_stream +from ._compat import PY2 +from ._compat import text_type from .exceptions import BadParameter -from .utils import safecall, LazyFile +from .utils import LazyFile +from .utils import safecall class ParamType(object): diff --git a/src/click/utils.py b/src/click/utils.py index fc84369fc..3c436a4c2 100644 --- a/src/click/utils.py +++ b/src/click/utils.py @@ -1,19 +1,30 @@ import os import sys +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import filename_to_ui +from ._compat import get_filesystem_encoding +from ._compat import get_streerror +from ._compat import is_bytes +from ._compat import open_stream +from ._compat import PY2 +from ._compat import should_strip_ansi +from ._compat import string_types +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import text_type +from ._compat import WIN from .globals import resolve_color_default -from ._compat import text_type, open_stream, get_filesystem_encoding, \ - get_streerror, string_types, PY2, binary_streams, text_streams, \ - filename_to_ui, auto_wrap_for_ansi, strip_ansi, should_strip_ansi, \ - _default_text_stdout, _default_text_stderr, is_bytes, WIN - if not PY2: from ._compat import _find_binary_writer elif WIN: - from ._winconsole import _get_windows_argv, \ - _hash_py_argv, _initial_argv_hash - + from ._winconsole import _get_windows_argv + from ._winconsole import _hash_py_argv + from ._winconsole import _initial_argv_hash echo_native_types = string_types + (bytes, bytearray) diff --git a/tests/conftest.py b/tests/conftest.py index 3aac93359..05b59c1fd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ -from click.testing import CliRunner - import pytest +from click.testing import CliRunner + @pytest.fixture(scope='function') def runner(request): diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 65d9a1e03..f92a5607e 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -1,8 +1,11 @@ # -*- coding: utf-8 -*- +import sys + import pytest + import click -import sys -from click._compat import PY2, text_type +from click._compat import PY2 +from click._compat import text_type def test_nargs_star(runner): diff --git a/tests/test_bashcomplete.py b/tests/test_bashcomplete.py index 5eb4068aa..05557a01b 100644 --- a/tests/test_bashcomplete.py +++ b/tests/test_bashcomplete.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - import pytest import click diff --git a/tests/test_basic.py b/tests/test_basic.py index f800b4d20..939c2054d 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import os import uuid + import click diff --git a/tests/test_chain.py b/tests/test_chain.py index 56a60e3ce..282920cd4 100644 --- a/tests/test_chain.py +++ b/tests/test_chain.py @@ -1,7 +1,9 @@ import sys -import click + import pytest +import click + def debug(): click.echo('%s=%s' % ( diff --git a/tests/test_commands.py b/tests/test_commands.py index 8186add74..aa96e71e2 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -2,7 +2,6 @@ import re import click -import pytest def test_other_command_invoke(runner): diff --git a/tests/test_compat.py b/tests/test_compat.py index 674cded9a..152301d12 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -1,6 +1,8 @@ -import click import pytest -from click._compat import should_strip_ansi, WIN + +import click +from click._compat import should_strip_ansi +from click._compat import WIN def test_legacy_callbacks(runner): diff --git a/tests/test_imports.py b/tests/test_imports.py index 268c60688..00b604b43 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -1,6 +1,6 @@ -import sys import json import subprocess +import sys from click._compat import WIN diff --git a/tests/test_options.py b/tests/test_options.py index 4a13a0a14..f3156b009 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -import re import os -import click +import re + import pytest +import click from click._compat import text_type diff --git a/tests/test_termui.py b/tests/test_termui.py index c6e06b63b..098e335cb 100644 --- a/tests/test_termui.py +++ b/tests/test_termui.py @@ -1,9 +1,8 @@ # -*- coding: utf-8 -*- -import pytest - -import click import time +import pytest + import click._termui_impl from click._compat import WIN diff --git a/tests/test_testing.py b/tests/test_testing.py index 7b03947d0..7802bbf82 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -2,12 +2,12 @@ import sys import pytest -import click +import click +from click._compat import PY2 +from click._compat import WIN from click.testing import CliRunner -from click._compat import PY2, WIN - # Use the most reasonable io that users would use for the python version. if PY2: from cStringIO import StringIO as ReasonableBytesIO diff --git a/tests/test_utils.py b/tests/test_utils.py index 93f1ca087..6ea9f9452 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -4,10 +4,10 @@ import pytest -import click -import click.utils import click._termui_impl -from click._compat import WIN, PY2 +import click.utils +from click._compat import PY2 +from click._compat import WIN def test_echo(runner): From 93ba3ba112d2f8ba7bdd8b231e510f74dd0b037e Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 6 Mar 2020 13:50:04 -0800 Subject: [PATCH 04/10] apply black --- docs/conf.py | 4 +- examples/aliases/aliases.py | 46 +- examples/aliases/setup.py | 14 +- examples/bashcompletion/bashcompletion.py | 16 +- examples/bashcompletion/setup.py | 14 +- examples/colors/colors.py | 36 +- examples/colors/setup.py | 14 +- examples/complex/complex/cli.py | 26 +- examples/complex/complex/commands/cmd_init.py | 7 +- .../complex/complex/commands/cmd_status.py | 6 +- examples/complex/setup.py | 14 +- examples/imagepipe/imagepipe.py | 119 ++-- examples/imagepipe/setup.py | 15 +- examples/inout/inout.py | 4 +- examples/inout/setup.py | 14 +- examples/naval/naval.py | 60 +- examples/naval/setup.py | 14 +- examples/repo/repo.py | 115 ++-- examples/repo/setup.py | 14 +- examples/termui/setup.py | 14 +- examples/termui/termui.py | 118 ++-- examples/validation/setup.py | 14 +- examples/validation/validation.py | 37 +- setup.py | 2 +- src/click/__init__.py | 2 +- src/click/_bashcomplete.py | 115 ++-- src/click/_compat.py | 224 ++++---- src/click/_termui_impl.py | 196 ++++--- src/click/_textwrap.py | 3 +- src/click/_unicodefun.py | 93 ++-- src/click/_winconsole.py | 130 +++-- src/click/core.py | 521 +++++++++++------- src/click/decorators.py | 121 ++-- src/click/exceptions.py | 62 ++- src/click/formatting.py | 103 ++-- src/click/globals.py | 6 +- src/click/parser.py | 76 ++- src/click/termui.py | 206 ++++--- src/click/testing.py | 80 +-- src/click/types.py | 278 ++++++---- src/click/utils.py | 76 +-- tests/conftest.py | 2 +- tests/test_arguments.py | 221 ++++---- tests/test_bashcomplete.py | 417 ++++++++------ tests/test_basic.py | 424 +++++++------- tests/test_chain.py | 153 ++--- tests/test_commands.py | 168 +++--- tests/test_compat.py | 24 +- tests/test_context.py | 73 +-- tests/test_defaults.py | 24 +- tests/test_formatting.py | 264 ++++----- tests/test_imports.py | 45 +- tests/test_normalization.py | 20 +- tests/test_options.py | 412 +++++++------- tests/test_termui.py | 209 +++---- tests/test_testing.py | 136 ++--- tests/test_utils.py | 281 +++++----- 57 files changed, 3250 insertions(+), 2652 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a14b22d3f..f04804ca1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,6 +47,4 @@ # LaTeX ---------------------------------------------------------------- -latex_documents = [ - (master_doc, f"Click-{version}.tex", html_title, author, "manual") -] +latex_documents = [(master_doc, f"Click-{version}.tex", html_title, author, "manual")] diff --git a/examples/aliases/aliases.py b/examples/aliases/aliases.py index a377a58cf..8456a1b64 100644 --- a/examples/aliases/aliases.py +++ b/examples/aliases/aliases.py @@ -22,16 +22,16 @@ def read_config(self, filename): parser = configparser.RawConfigParser() parser.read([filename]) try: - self.aliases.update(parser.items('aliases')) + self.aliases.update(parser.items("aliases")) except configparser.NoSectionError: pass def write_config(self, filename): parser = configparser.RawConfigParser() - parser.add_section('aliases') + parser.add_section("aliases") for key, value in self.aliases.items(): - parser.set('aliases', key, value) - with open(filename, 'wb') as file: + parser.set("aliases", key, value) + with open(filename, "wb") as file: parser.write(file) @@ -62,13 +62,14 @@ def get_command(self, ctx, cmd_name): # allow automatic abbreviation of the command. "status" for # instance will match "st". We only allow that however if # there is only one command. - matches = [x for x in self.list_commands(ctx) - if x.lower().startswith(cmd_name.lower())] + matches = [ + x for x in self.list_commands(ctx) if x.lower().startswith(cmd_name.lower()) + ] if not matches: return None elif len(matches) == 1: return click.Group.get_command(self, ctx, matches[0]) - ctx.fail('Too many matches: %s' % ', '.join(sorted(matches))) + ctx.fail("Too many matches: %s" % ", ".join(sorted(matches))) def read_config(ctx, param, value): @@ -79,15 +80,19 @@ def read_config(ctx, param, value): """ cfg = ctx.ensure_object(Config) if value is None: - value = os.path.join(os.path.dirname(__file__), 'aliases.ini') + value = os.path.join(os.path.dirname(__file__), "aliases.ini") cfg.read_config(value) return value @click.command(cls=AliasedGroup) -@click.option('--config', type=click.Path(exists=True, dir_okay=False), - callback=read_config, expose_value=False, - help='The config file to use instead of the default.') +@click.option( + "--config", + type=click.Path(exists=True, dir_okay=False), + callback=read_config, + expose_value=False, + help="The config file to use instead of the default.", +) def cli(): """An example application that supports aliases.""" @@ -95,42 +100,43 @@ def cli(): @cli.command() def push(): """Pushes changes.""" - click.echo('Push') + click.echo("Push") @cli.command() def pull(): """Pulls changes.""" - click.echo('Pull') + click.echo("Pull") @cli.command() def clone(): """Clones a repository.""" - click.echo('Clone') + click.echo("Clone") @cli.command() def commit(): """Commits pending changes.""" - click.echo('Commit') + click.echo("Commit") @cli.command() @pass_config def status(config): """Shows the status.""" - click.echo('Status for %s' % config.path) + click.echo("Status for %s" % config.path) @cli.command() @pass_config -@click.argument("alias_", metavar='ALIAS', type=click.STRING) +@click.argument("alias_", metavar="ALIAS", type=click.STRING) @click.argument("cmd", type=click.STRING) -@click.option('--config_file', type=click.Path(exists=True, dir_okay=False), - default="aliases.ini") +@click.option( + "--config_file", type=click.Path(exists=True, dir_okay=False), default="aliases.ini" +) def alias(config, alias_, cmd, config_file): """Adds an alias to the specified configuration file.""" config.add_alias(alias_, cmd) config.write_config(config_file) - click.echo('Added %s as alias for %s' % (alias_, cmd)) + click.echo("Added %s as alias for %s" % (alias_, cmd)) diff --git a/examples/aliases/setup.py b/examples/aliases/setup.py index 8d1d6a406..2ea791ecb 100644 --- a/examples/aliases/setup.py +++ b/examples/aliases/setup.py @@ -1,15 +1,13 @@ from setuptools import setup setup( - name='click-example-aliases', - version='1.0', - py_modules=['aliases'], + name="click-example-aliases", + version="1.0", + py_modules=["aliases"], include_package_data=True, - install_requires=[ - 'click', - ], - entry_points=''' + install_requires=["click",], + entry_points=""" [console_scripts] aliases=aliases:cli - ''', + """, ) diff --git a/examples/bashcompletion/bashcompletion.py b/examples/bashcompletion/bashcompletion.py index 5dacae5d5..c43706998 100644 --- a/examples/bashcompletion/bashcompletion.py +++ b/examples/bashcompletion/bashcompletion.py @@ -15,14 +15,14 @@ def get_env_vars(ctx, args, incomplete): yield key -@cli.command(help='A command to print environment variables') +@cli.command(help="A command to print environment variables") @click.argument("envvar", type=click.STRING, autocompletion=get_env_vars) def cmd1(envvar): - click.echo('Environment variable: %s' % envvar) - click.echo('Value: %s' % os.environ[envvar]) + click.echo("Environment variable: %s" % envvar) + click.echo("Value: %s" % os.environ[envvar]) -@click.group(help='A group that holds a subcommand') +@click.group(help="A group that holds a subcommand") def group(): pass @@ -30,17 +30,15 @@ def group(): def list_users(ctx, args, incomplete): # You can generate completions with descriptions by returning # tuples in the form (completion, description). - users = [('bob', 'butcher'), - ('alice', 'baker'), - ('jerry', 'candlestick maker')] + users = [("bob", "butcher"), ("alice", "baker"), ("jerry", "candlestick maker")] # Ths will allow completion matches based on matches within the description string too! return [user for user in users if incomplete in user[0] or incomplete in user[1]] -@group.command(help='Choose a user') +@group.command(help="Choose a user") @click.argument("user", type=click.STRING, autocompletion=list_users) def subcmd(user): - click.echo('Chosen user is %s' % user) + click.echo("Chosen user is %s" % user) cli.add_command(group) diff --git a/examples/bashcompletion/setup.py b/examples/bashcompletion/setup.py index ad200818c..31e3cfa3c 100644 --- a/examples/bashcompletion/setup.py +++ b/examples/bashcompletion/setup.py @@ -1,15 +1,13 @@ from setuptools import setup setup( - name='click-example-bashcompletion', - version='1.0', - py_modules=['bashcompletion'], + name="click-example-bashcompletion", + version="1.0", + py_modules=["bashcompletion"], include_package_data=True, - install_requires=[ - 'click', - ], - entry_points=''' + install_requires=["click",], + entry_points=""" [console_scripts] bashcompletion=bashcompletion:cli - ''', + """, ) diff --git a/examples/colors/colors.py b/examples/colors/colors.py index 193b92712..4219c0f87 100644 --- a/examples/colors/colors.py +++ b/examples/colors/colors.py @@ -1,10 +1,24 @@ import click -all_colors = 'black', 'red', 'green', 'yellow', 'blue', 'magenta', \ - 'cyan', 'white', 'bright_black', 'bright_red', \ - 'bright_green', 'bright_yellow', 'bright_blue', \ - 'bright_magenta', 'bright_cyan', 'bright_white' +all_colors = ( + "black", + "red", + "green", + "yellow", + "blue", + "magenta", + "cyan", + "white", + "bright_black", + "bright_red", + "bright_green", + "bright_yellow", + "bright_blue", + "bright_magenta", + "bright_cyan", + "bright_white", +) @click.command() @@ -16,13 +30,13 @@ def cli(): Give it a try! """ for color in all_colors: - click.echo(click.style('I am colored %s' % color, fg=color)) + click.echo(click.style("I am colored %s" % color, fg=color)) for color in all_colors: - click.echo(click.style('I am colored %s and bold' % color, - fg=color, bold=True)) + click.echo(click.style("I am colored %s and bold" % color, fg=color, bold=True)) for color in all_colors: - click.echo(click.style('I am reverse colored %s' % color, fg=color, - reverse=True)) + click.echo( + click.style("I am reverse colored %s" % color, fg=color, reverse=True) + ) - click.echo(click.style('I am blinking', blink=True)) - click.echo(click.style('I am underlined', underline=True)) + click.echo(click.style("I am blinking", blink=True)) + click.echo(click.style("I am underlined", underline=True)) diff --git a/examples/colors/setup.py b/examples/colors/setup.py index 3f8e105fa..6d892ddcb 100644 --- a/examples/colors/setup.py +++ b/examples/colors/setup.py @@ -1,17 +1,17 @@ from setuptools import setup setup( - name='click-example-colors', - version='1.0', - py_modules=['colors'], + name="click-example-colors", + version="1.0", + py_modules=["colors"], include_package_data=True, install_requires=[ - 'click', + "click", # Colorama is only required for Windows. - 'colorama', + "colorama", ], - entry_points=''' + entry_points=""" [console_scripts] colors=colors:cli - ''', + """, ) diff --git a/examples/complex/complex/cli.py b/examples/complex/complex/cli.py index 3ef6faeeb..5e75f583f 100644 --- a/examples/complex/complex/cli.py +++ b/examples/complex/complex/cli.py @@ -4,11 +4,10 @@ import click -CONTEXT_SETTINGS = dict(auto_envvar_prefix='COMPLEX') +CONTEXT_SETTINGS = dict(auto_envvar_prefix="COMPLEX") class Environment(object): - def __init__(self): self.verbose = False self.home = os.getcwd() @@ -26,17 +25,14 @@ def vlog(self, msg, *args): pass_environment = click.make_pass_decorator(Environment, ensure=True) -cmd_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), - 'commands')) +cmd_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), "commands")) class ComplexCLI(click.MultiCommand): - def list_commands(self, ctx): rv = [] for filename in os.listdir(cmd_folder): - if filename.endswith('.py') and \ - filename.startswith('cmd_'): + if filename.endswith(".py") and filename.startswith("cmd_"): rv.append(filename[4:-3]) rv.sort() return rv @@ -44,20 +40,20 @@ def list_commands(self, ctx): def get_command(self, ctx, name): try: if sys.version_info[0] == 2: - name = name.encode('ascii', 'replace') - mod = __import__('complex.commands.cmd_' + name, - None, None, ['cli']) + name = name.encode("ascii", "replace") + mod = __import__("complex.commands.cmd_" + name, None, None, ["cli"]) except ImportError: return return mod.cli @click.command(cls=ComplexCLI, context_settings=CONTEXT_SETTINGS) -@click.option('--home', type=click.Path(exists=True, file_okay=False, - resolve_path=True), - help='Changes the folder to operate on.') -@click.option('-v', '--verbose', is_flag=True, - help='Enables verbose mode.') +@click.option( + "--home", + type=click.Path(exists=True, file_okay=False, resolve_path=True), + help="Changes the folder to operate on.", +) +@click.option("-v", "--verbose", is_flag=True, help="Enables verbose mode.") @pass_environment def cli(ctx, verbose, home): """A complex command line interface.""" diff --git a/examples/complex/complex/commands/cmd_init.py b/examples/complex/complex/commands/cmd_init.py index 5da8df24b..c2cf77093 100644 --- a/examples/complex/complex/commands/cmd_init.py +++ b/examples/complex/complex/commands/cmd_init.py @@ -3,12 +3,11 @@ import click -@click.command('init', short_help='Initializes a repo.') -@click.argument('path', required=False, type=click.Path(resolve_path=True)) +@click.command("init", short_help="Initializes a repo.") +@click.argument("path", required=False, type=click.Path(resolve_path=True)) @pass_environment def cli(ctx, path): """Initializes a repository.""" if path is None: path = ctx.home - ctx.log('Initialized the repository in %s', - click.format_filename(path)) + ctx.log("Initialized the repository in %s", click.format_filename(path)) diff --git a/examples/complex/complex/commands/cmd_status.py b/examples/complex/complex/commands/cmd_status.py index ac7976747..92275bb49 100644 --- a/examples/complex/complex/commands/cmd_status.py +++ b/examples/complex/complex/commands/cmd_status.py @@ -3,9 +3,9 @@ import click -@click.command('status', short_help='Shows file changes.') +@click.command("status", short_help="Shows file changes.") @pass_environment def cli(ctx): """Shows file changes in the current working directory.""" - ctx.log('Changed files: none') - ctx.vlog('bla bla bla, debug info') + ctx.log("Changed files: none") + ctx.vlog("bla bla bla, debug info") diff --git a/examples/complex/setup.py b/examples/complex/setup.py index dee002c13..f776e5e82 100644 --- a/examples/complex/setup.py +++ b/examples/complex/setup.py @@ -1,15 +1,13 @@ from setuptools import setup setup( - name='click-example-complex', - version='1.0', - packages=['complex', 'complex.commands'], + name="click-example-complex", + version="1.0", + packages=["complex", "complex.commands"], include_package_data=True, - install_requires=[ - 'click', - ], - entry_points=''' + install_requires=["click",], + entry_points=""" [console_scripts] complex=complex.cli:cli - ''', + """, ) diff --git a/examples/imagepipe/imagepipe.py b/examples/imagepipe/imagepipe.py index f6f04ef7f..924911654 100644 --- a/examples/imagepipe/imagepipe.py +++ b/examples/imagepipe/imagepipe.py @@ -43,10 +43,13 @@ def processor(f): """Helper decorator to rewrite a function so that it returns another function from it. """ + def new_func(*args, **kwargs): def processor(stream): return f(stream, *args, **kwargs) + return processor + return update_wrapper(new_func, f) @@ -54,12 +57,14 @@ def generator(f): """Similar to the :func:`processor` but passes through old values unchanged and does not pass through the values as parameter. """ + @processor def new_func(stream, *args, **kwargs): for item in stream: yield item for item in f(*args, **kwargs): yield item + return update_wrapper(new_func, f) @@ -68,9 +73,15 @@ def copy_filename(new, old): return new -@cli.command('open') -@click.option('-i', '--image', 'images', type=click.Path(), - multiple=True, help='The image file to open.') +@cli.command("open") +@click.option( + "-i", + "--image", + "images", + type=click.Path(), + multiple=True, + help="The image file to open.", +) @generator def open_cmd(images): """Loads one or multiple images for processing. The input parameter @@ -79,9 +90,9 @@ def open_cmd(images): for image in images: try: click.echo('Opening "%s"' % image) - if image == '-': + if image == "-": img = Image.open(click.get_binary_stdin()) - img.filename = '-' + img.filename = "-" else: img = Image.open(image) yield img @@ -89,10 +100,14 @@ def open_cmd(images): click.echo('Could not open image "%s": %s' % (image, e), err=True) -@cli.command('save') -@click.option('--filename', default='processed-%04d.png', type=click.Path(), - help='The format for the filename.', - show_default=True) +@cli.command("save") +@click.option( + "--filename", + default="processed-%04d.png", + type=click.Path(), + help="The format for the filename.", + show_default=True, +) @processor def save_cmd(images, filename): """Saves all processed images to a series of files.""" @@ -102,11 +117,10 @@ def save_cmd(images, filename): click.echo('Saving "%s" as "%s"' % (image.filename, fn)) yield image.save(fn) except Exception as e: - click.echo('Could not save image "%s": %s' % - (image.filename, e), err=True) + click.echo('Could not save image "%s": %s' % (image.filename, e), err=True) -@cli.command('display') +@cli.command("display") @processor def display_cmd(images): """Opens all images in an image viewer.""" @@ -116,9 +130,9 @@ def display_cmd(images): yield image -@cli.command('resize') -@click.option('-w', '--width', type=int, help='The new width of the image.') -@click.option('-h', '--height', type=int, help='The new height of the image.') +@cli.command("resize") +@click.option("-w", "--width", type=int, help="The new width of the image.") +@click.option("-h", "--height", type=int, help="The new height of the image.") @processor def resize_cmd(images, width, height): """Resizes an image by fitting it into the box without changing @@ -131,9 +145,10 @@ def resize_cmd(images, width, height): yield image -@cli.command('crop') -@click.option('-b', '--border', type=int, help='Crop the image from all ' - 'sides by this amount.') +@cli.command("crop") +@click.option( + "-b", "--border", type=int, help="Crop the image from all sides by this amount." +) @processor def crop_cmd(images, border): """Crops an image from all edges.""" @@ -153,11 +168,11 @@ def convert_rotation(ctx, param, value): if value is None: return value = value.lower() - if value in ('90', 'r', 'right'): + if value in ("90", "r", "right"): return (Image.ROTATE_90, 90) - if value in ('180', '-180'): + if value in ("180", "-180"): return (Image.ROTATE_180, 180) - if value in ('-90', '270', 'l', 'left'): + if value in ("-90", "270", "l", "left"): return (Image.ROTATE_270, 270) raise click.BadParameter('invalid rotation "%s"' % value) @@ -166,18 +181,18 @@ def convert_flip(ctx, param, value): if value is None: return value = value.lower() - if value in ('lr', 'leftright'): - return (Image.FLIP_LEFT_RIGHT, 'left to right') - if value in ('tb', 'topbottom', 'upsidedown', 'ud'): - return (Image.FLIP_LEFT_RIGHT, 'top to bottom') + if value in ("lr", "leftright"): + return (Image.FLIP_LEFT_RIGHT, "left to right") + if value in ("tb", "topbottom", "upsidedown", "ud"): + return (Image.FLIP_LEFT_RIGHT, "top to bottom") raise click.BadParameter('invalid flip "%s"' % value) -@cli.command('transpose') -@click.option('-r', '--rotate', callback=convert_rotation, - help='Rotates the image (in degrees)') -@click.option('-f', '--flip', callback=convert_flip, - help='Flips the image [LR / TB]') +@cli.command("transpose") +@click.option( + "-r", "--rotate", callback=convert_rotation, help="Rotates the image (in degrees)" +) +@click.option("-f", "--flip", callback=convert_flip, help="Flips the image [LR / TB]") @processor def transpose_cmd(images, rotate, flip): """Transposes an image by either rotating or flipping it.""" @@ -193,9 +208,8 @@ def transpose_cmd(images, rotate, flip): yield image -@cli.command('blur') -@click.option('-r', '--radius', default=2, show_default=True, - help='The blur radius.') +@cli.command("blur") +@click.option("-r", "--radius", default=2, show_default=True, help="The blur radius.") @processor def blur_cmd(images, radius): """Applies gaussian blur.""" @@ -205,21 +219,28 @@ def blur_cmd(images, radius): yield copy_filename(image.filter(blur), image) -@cli.command('smoothen') -@click.option('-i', '--iterations', default=1, show_default=True, - help='How many iterations of the smoothen filter to run.') +@cli.command("smoothen") +@click.option( + "-i", + "--iterations", + default=1, + show_default=True, + help="How many iterations of the smoothen filter to run.", +) @processor def smoothen_cmd(images, iterations): """Applies a smoothening filter.""" for image in images: - click.echo('Smoothening "%s" %d time%s' % - (image.filename, iterations, iterations != 1 and 's' or '',)) + click.echo( + 'Smoothening "%s" %d time%s' + % (image.filename, iterations, iterations != 1 and "s" or "",) + ) for x in xrange(iterations): image = copy_filename(image.filter(ImageFilter.BLUR), image) yield image -@cli.command('emboss') +@cli.command("emboss") @processor def emboss_cmd(images): """Embosses an image.""" @@ -228,9 +249,10 @@ def emboss_cmd(images): yield copy_filename(image.filter(ImageFilter.EMBOSS), image) -@cli.command('sharpen') -@click.option('-f', '--factor', default=2.0, - help='Sharpens the image.', show_default=True) +@cli.command("sharpen") +@click.option( + "-f", "--factor", default=2.0, help="Sharpens the image.", show_default=True +) @processor def sharpen_cmd(images, factor): """Sharpens an image.""" @@ -240,9 +262,9 @@ def sharpen_cmd(images, factor): yield copy_filename(enhancer.enhance(max(1.0, factor)), image) -@cli.command('paste') -@click.option('-l', '--left', default=0, help='Offset from left.') -@click.option('-r', '--right', default=0, help='Offset from right.') +@cli.command("paste") +@click.option("-l", "--left", default=0, help="Offset from left.") +@click.option("-r", "--right", default=0, help="Offset from right.") @processor def paste_cmd(images, left, right): """Pastes the second image on the first image and leaves the rest @@ -257,13 +279,12 @@ def paste_cmd(images, left, right): yield image return - click.echo('Paste "%s" on "%s"' % - (to_paste.filename, image.filename)) + click.echo('Paste "%s" on "%s"' % (to_paste.filename, image.filename)) mask = None - if to_paste.mode == 'RGBA' or 'transparency' in to_paste.info: + if to_paste.mode == "RGBA" or "transparency" in to_paste.info: mask = to_paste image.paste(to_paste, (left, right), mask) - image.filename += '+' + to_paste.filename + image.filename += "+" + to_paste.filename yield image for image in imageiter: diff --git a/examples/imagepipe/setup.py b/examples/imagepipe/setup.py index d2d8d9911..80d5c8549 100644 --- a/examples/imagepipe/setup.py +++ b/examples/imagepipe/setup.py @@ -1,16 +1,13 @@ from setuptools import setup setup( - name='click-example-imagepipe', - version='1.0', - py_modules=['imagepipe'], + name="click-example-imagepipe", + version="1.0", + py_modules=["imagepipe"], include_package_data=True, - install_requires=[ - 'click', - 'pillow', - ], - entry_points=''' + install_requires=["click", "pillow",], + entry_points=""" [console_scripts] imagepipe=imagepipe:cli - ''', + """, ) diff --git a/examples/inout/inout.py b/examples/inout/inout.py index b93f30662..854c84efa 100644 --- a/examples/inout/inout.py +++ b/examples/inout/inout.py @@ -2,8 +2,8 @@ @click.command() -@click.argument('input', type=click.File('rb'), nargs=-1) -@click.argument('output', type=click.File('wb')) +@click.argument("input", type=click.File("rb"), nargs=-1) +@click.argument("output", type=click.File("wb")) def cli(input, output): """This script works similar to the Unix `cat` command but it writes into a specific file (which could be the standard output as denoted by diff --git a/examples/inout/setup.py b/examples/inout/setup.py index 5c613646e..2f4e5a3b5 100644 --- a/examples/inout/setup.py +++ b/examples/inout/setup.py @@ -1,15 +1,13 @@ from setuptools import setup setup( - name='click-example-inout', - version='0.1', - py_modules=['inout'], + name="click-example-inout", + version="0.1", + py_modules=["inout"], include_package_data=True, - install_requires=[ - 'click', - ], - entry_points=''' + install_requires=["click",], + entry_points=""" [console_scripts] inout=inout:cli - ''', + """, ) diff --git a/examples/naval/naval.py b/examples/naval/naval.py index 2d173d84b..cf9e00c64 100644 --- a/examples/naval/naval.py +++ b/examples/naval/naval.py @@ -17,54 +17,56 @@ def ship(): """Manages ships.""" -@ship.command('new') -@click.argument('name') +@ship.command("new") +@click.argument("name") def ship_new(name): """Creates a new ship.""" - click.echo('Created ship %s' % name) + click.echo("Created ship %s" % name) -@ship.command('move') -@click.argument('ship') -@click.argument('x', type=float) -@click.argument('y', type=float) -@click.option('--speed', metavar='KN', default=10, - help='Speed in knots.') +@ship.command("move") +@click.argument("ship") +@click.argument("x", type=float) +@click.argument("y", type=float) +@click.option("--speed", metavar="KN", default=10, help="Speed in knots.") def ship_move(ship, x, y, speed): """Moves SHIP to the new location X,Y.""" - click.echo('Moving ship %s to %s,%s with speed %s' % (ship, x, y, speed)) + click.echo("Moving ship %s to %s,%s with speed %s" % (ship, x, y, speed)) -@ship.command('shoot') -@click.argument('ship') -@click.argument('x', type=float) -@click.argument('y', type=float) +@ship.command("shoot") +@click.argument("ship") +@click.argument("x", type=float) +@click.argument("y", type=float) def ship_shoot(ship, x, y): """Makes SHIP fire to X,Y.""" - click.echo('Ship %s fires to %s,%s' % (ship, x, y)) + click.echo("Ship %s fires to %s,%s" % (ship, x, y)) -@cli.group('mine') +@cli.group("mine") def mine(): """Manages mines.""" -@mine.command('set') -@click.argument('x', type=float) -@click.argument('y', type=float) -@click.option('ty', '--moored', flag_value='moored', - default=True, - help='Moored (anchored) mine. Default.') -@click.option('ty', '--drifting', flag_value='drifting', - help='Drifting mine.') +@mine.command("set") +@click.argument("x", type=float) +@click.argument("y", type=float) +@click.option( + "ty", + "--moored", + flag_value="moored", + default=True, + help="Moored (anchored) mine. Default.", +) +@click.option("ty", "--drifting", flag_value="drifting", help="Drifting mine.") def mine_set(x, y, ty): """Sets a mine at a specific coordinate.""" - click.echo('Set %s mine at %s,%s' % (ty, x, y)) + click.echo("Set %s mine at %s,%s" % (ty, x, y)) -@mine.command('remove') -@click.argument('x', type=float) -@click.argument('y', type=float) +@mine.command("remove") +@click.argument("x", type=float) +@click.argument("y", type=float) def mine_remove(x, y): """Removes a mine at a specific coordinate.""" - click.echo('Removed mine at %s,%s' % (x, y)) + click.echo("Removed mine at %s,%s" % (x, y)) diff --git a/examples/naval/setup.py b/examples/naval/setup.py index 124addf43..a3d93bb2e 100644 --- a/examples/naval/setup.py +++ b/examples/naval/setup.py @@ -1,15 +1,13 @@ from setuptools import setup setup( - name='click-example-naval', - version='2.0', - py_modules=['naval'], + name="click-example-naval", + version="2.0", + py_modules=["naval"], include_package_data=True, - install_requires=[ - 'click', - ], - entry_points=''' + install_requires=["click",], + entry_points=""" [console_scripts] naval=naval:cli - ''', + """, ) diff --git a/examples/repo/repo.py b/examples/repo/repo.py index c14a82792..b9bf2f0e7 100644 --- a/examples/repo/repo.py +++ b/examples/repo/repo.py @@ -6,7 +6,6 @@ class Repo(object): - def __init__(self, home): self.home = home self.config = {} @@ -15,23 +14,32 @@ def __init__(self, home): def set_config(self, key, value): self.config[key] = value if self.verbose: - click.echo(' config[%s] = %s' % (key, value), file=sys.stderr) + click.echo(" config[%s] = %s" % (key, value), file=sys.stderr) def __repr__(self): - return '' % self.home + return "" % self.home pass_repo = click.make_pass_decorator(Repo) @click.group() -@click.option('--repo-home', envvar='REPO_HOME', default='.repo', - metavar='PATH', help='Changes the repository folder location.') -@click.option('--config', nargs=2, multiple=True, - metavar='KEY VALUE', help='Overrides a config key/value pair.') -@click.option('--verbose', '-v', is_flag=True, - help='Enables verbose mode.') -@click.version_option('1.0') +@click.option( + "--repo-home", + envvar="REPO_HOME", + default=".repo", + metavar="PATH", + help="Changes the repository folder location.", +) +@click.option( + "--config", + nargs=2, + multiple=True, + metavar="KEY VALUE", + help="Overrides a config key/value pair.", +) +@click.option("--verbose", "-v", is_flag=True, help="Enables verbose mode.") +@click.version_option("1.0") @click.pass_context def cli(ctx, repo_home, config, verbose): """Repo is a command line tool that showcases how to build complex @@ -50,12 +58,16 @@ def cli(ctx, repo_home, config, verbose): @cli.command() -@click.argument('src') -@click.argument('dest', required=False) -@click.option('--shallow/--deep', default=False, - help='Makes a checkout shallow or deep. Deep by default.') -@click.option('--rev', '-r', default='HEAD', - help='Clone a specific revision instead of HEAD.') +@click.argument("src") +@click.argument("dest", required=False) +@click.option( + "--shallow/--deep", + default=False, + help="Makes a checkout shallow or deep. Deep by default.", +) +@click.option( + "--rev", "-r", default="HEAD", help="Clone a specific revision instead of HEAD." +) @pass_repo def clone(repo, src, dest, shallow, rev): """Clones a repository. @@ -65,12 +77,12 @@ def clone(repo, src, dest, shallow, rev): of SRC and create that folder. """ if dest is None: - dest = posixpath.split(src)[-1] or '.' - click.echo('Cloning repo %s to %s' % (src, os.path.abspath(dest))) + dest = posixpath.split(src)[-1] or "." + click.echo("Cloning repo %s to %s" % (src, os.path.abspath(dest))) repo.home = dest if shallow: - click.echo('Making shallow checkout') - click.echo('Checking out revision %s' % rev) + click.echo("Making shallow checkout") + click.echo("Checking out revision %s" % rev) @cli.command() @@ -81,33 +93,35 @@ def delete(repo): This will throw away the current repository. """ - click.echo('Destroying repo %s' % repo.home) - click.echo('Deleted!') + click.echo("Destroying repo %s" % repo.home) + click.echo("Deleted!") @cli.command() -@click.option('--username', prompt=True, - help='The developer\'s shown username.') -@click.option('--email', prompt='E-Mail', - help='The developer\'s email address') -@click.password_option(help='The login password.') +@click.option("--username", prompt=True, help="The developer's shown username.") +@click.option("--email", prompt="E-Mail", help="The developer's email address") +@click.password_option(help="The login password.") @pass_repo def setuser(repo, username, email, password): """Sets the user credentials. This will override the current user config. """ - repo.set_config('username', username) - repo.set_config('email', email) - repo.set_config('password', '*' * len(password)) - click.echo('Changed credentials.') + repo.set_config("username", username) + repo.set_config("email", email) + repo.set_config("password", "*" * len(password)) + click.echo("Changed credentials.") @cli.command() -@click.option('--message', '-m', multiple=True, - help='The commit message. If provided multiple times each ' - 'argument gets converted into a new line.') -@click.argument('files', nargs=-1, type=click.Path()) +@click.option( + "--message", + "-m", + multiple=True, + help="The commit message. If provided multiple times each " + "argument gets converted into a new line.", +) +@click.argument("files", nargs=-1, type=click.Path()) @pass_repo def commit(repo, files, message): """Commits outstanding changes. @@ -119,33 +133,34 @@ def commit(repo, files, message): will be committed. """ if not message: - marker = '# Files to be committed:' - hint = ['', '', marker, '#'] + marker = "# Files to be committed:" + hint = ["", "", marker, "#"] for file in files: - hint.append('# U %s' % file) - message = click.edit('\n'.join(hint)) + hint.append("# U %s" % file) + message = click.edit("\n".join(hint)) if message is None: - click.echo('Aborted!') + click.echo("Aborted!") return msg = message.split(marker)[0].rstrip() if not msg: - click.echo('Aborted! Empty commit message') + click.echo("Aborted! Empty commit message") return else: - msg = '\n'.join(message) - click.echo('Files to be committed: %s' % (files,)) - click.echo('Commit message:\n' + msg) + msg = "\n".join(message) + click.echo("Files to be committed: %s" % (files,)) + click.echo("Commit message:\n" + msg) -@cli.command(short_help='Copies files.') -@click.option('--force', is_flag=True, - help='forcibly copy over an existing managed file') -@click.argument('src', nargs=-1, type=click.Path()) -@click.argument('dst', type=click.Path()) +@cli.command(short_help="Copies files.") +@click.option( + "--force", is_flag=True, help="forcibly copy over an existing managed file" +) +@click.argument("src", nargs=-1, type=click.Path()) +@click.argument("dst", type=click.Path()) @pass_repo def copy(repo, src, dst, force): """Copies one or multiple files to a new location. This copies all files from SRC to DST. """ for fn in src: - click.echo('Copy from %s -> %s' % (fn, dst)) + click.echo("Copy from %s -> %s" % (fn, dst)) diff --git a/examples/repo/setup.py b/examples/repo/setup.py index 19aab7087..e0aa5b06e 100644 --- a/examples/repo/setup.py +++ b/examples/repo/setup.py @@ -1,15 +1,13 @@ from setuptools import setup setup( - name='click-example-repo', - version='0.1', - py_modules=['repo'], + name="click-example-repo", + version="0.1", + py_modules=["repo"], include_package_data=True, - install_requires=[ - 'click', - ], - entry_points=''' + install_requires=["click",], + entry_points=""" [console_scripts] repo=repo:cli - ''', + """, ) diff --git a/examples/termui/setup.py b/examples/termui/setup.py index 14558e85c..7791baec2 100644 --- a/examples/termui/setup.py +++ b/examples/termui/setup.py @@ -1,17 +1,17 @@ from setuptools import setup setup( - name='click-example-termui', - version='1.0', - py_modules=['termui'], + name="click-example-termui", + version="1.0", + py_modules=["termui"], include_package_data=True, install_requires=[ - 'click', + "click", # Colorama is only required for Windows. - 'colorama', + "colorama", ], - entry_points=''' + entry_points=""" [console_scripts] termui=termui:cli - ''', + """, ) diff --git a/examples/termui/termui.py b/examples/termui/termui.py index 08f6a04ee..2af114d4a 100644 --- a/examples/termui/termui.py +++ b/examples/termui/termui.py @@ -15,9 +15,9 @@ def cli(): @cli.command() def colordemo(): """Demonstrates ANSI color support.""" - for color in 'red', 'green', 'blue': - click.echo(click.style('I am colored %s' % color, fg=color)) - click.echo(click.style('I am background colored %s' % color, bg=color)) + for color in "red", "green", "blue": + click.echo(click.style("I am colored %s" % color, fg=color)) + click.echo(click.style("I am background colored %s" % color, bg=color)) @cli.command() @@ -25,13 +25,17 @@ def pager(): """Demonstrates using the pager.""" lines = [] for x in range(200): - lines.append('%s. Hello World!' % click.style(str(x), fg='green')) - click.echo_via_pager('\n'.join(lines)) + lines.append("%s. Hello World!" % click.style(str(x), fg="green")) + click.echo_via_pager("\n".join(lines)) @cli.command() -@click.option('--count', default=8000, type=click.IntRange(1, 100000), - help='The number of items to process.') +@click.option( + "--count", + default=8000, + type=click.IntRange(1, 100000), + help="The number of items to process.", +) def progress(count): """Demonstrates the progress bar.""" items = range(count) @@ -44,54 +48,68 @@ def filter(items): if random.random() > 0.3: yield item - with click.progressbar(items, label='Processing accounts', - fill_char=click.style('#', fg='green')) as bar: + with click.progressbar( + items, label="Processing accounts", fill_char=click.style("#", fg="green") + ) as bar: for item in bar: process_slowly(item) def show_item(item): if item is not None: - return 'Item #%d' % item - - with click.progressbar(filter(items), label='Committing transaction', - fill_char=click.style('#', fg='yellow'), - item_show_func=show_item) as bar: + return "Item #%d" % item + + with click.progressbar( + filter(items), + label="Committing transaction", + fill_char=click.style("#", fg="yellow"), + item_show_func=show_item, + ) as bar: for item in bar: process_slowly(item) - with click.progressbar(length=count, label='Counting', - bar_template='%(label)s %(bar)s | %(info)s', - fill_char=click.style(u'█', fg='cyan'), - empty_char=' ') as bar: + with click.progressbar( + length=count, + label="Counting", + bar_template="%(label)s %(bar)s | %(info)s", + fill_char=click.style(u"█", fg="cyan"), + empty_char=" ", + ) as bar: for item in bar: process_slowly(item) - with click.progressbar(length=count, width=0, show_percent=False, - show_eta=False, - fill_char=click.style('#', fg='magenta')) as bar: + with click.progressbar( + length=count, + width=0, + show_percent=False, + show_eta=False, + fill_char=click.style("#", fg="magenta"), + ) as bar: for item in bar: process_slowly(item) # 'Non-linear progress bar' - steps = [math.exp( x * 1. / 20) - 1 for x in range(20)] + steps = [math.exp(x * 1.0 / 20) - 1 for x in range(20)] count = int(sum(steps)) - with click.progressbar(length=count, show_percent=False, - label='Slowing progress bar', - fill_char=click.style(u'█', fg='green')) as bar: + with click.progressbar( + length=count, + show_percent=False, + label="Slowing progress bar", + fill_char=click.style(u"█", fg="green"), + ) as bar: for item in steps: time.sleep(item) bar.update(item) @cli.command() -@click.argument('url') +@click.argument("url") def open(url): """Opens a file or URL In the default application.""" click.launch(url) @cli.command() -@click.argument('url') +@click.argument("url") def locate(url): """Opens a file or URL In the default application.""" click.launch(url, locate=True) @@ -100,16 +118,16 @@ def locate(url): @cli.command() def edit(): """Opens an editor with some text in it.""" - MARKER = '# Everything below is ignored\n' - message = click.edit('\n\n' + MARKER) + MARKER = "# Everything below is ignored\n" + message = click.edit("\n\n" + MARKER) if message is not None: - msg = message.split(MARKER, 1)[0].rstrip('\n') + msg = message.split(MARKER, 1)[0].rstrip("\n") if not msg: - click.echo('Empty message!') + click.echo("Empty message!") else: - click.echo('Message:\n' + msg) + click.echo("Message:\n" + msg) else: - click.echo('You did not enter anything!') + click.echo("You did not enter anything!") @cli.command() @@ -127,26 +145,26 @@ def pause(): @cli.command() def menu(): """Shows a simple menu.""" - menu = 'main' + menu = "main" while 1: - if menu == 'main': - click.echo('Main menu:') - click.echo(' d: debug menu') - click.echo(' q: quit') + if menu == "main": + click.echo("Main menu:") + click.echo(" d: debug menu") + click.echo(" q: quit") char = click.getchar() - if char == 'd': - menu = 'debug' - elif char == 'q': - menu = 'quit' + if char == "d": + menu = "debug" + elif char == "q": + menu = "quit" else: - click.echo('Invalid input') - elif menu == 'debug': - click.echo('Debug menu') - click.echo(' b: back') + click.echo("Invalid input") + elif menu == "debug": + click.echo("Debug menu") + click.echo(" b: back") char = click.getchar() - if char == 'b': - menu = 'main' + if char == "b": + menu = "main" else: - click.echo('Invalid input') - elif menu == 'quit': + click.echo("Invalid input") + elif menu == "quit": return diff --git a/examples/validation/setup.py b/examples/validation/setup.py index 9491f709c..80f2bf02b 100644 --- a/examples/validation/setup.py +++ b/examples/validation/setup.py @@ -1,15 +1,13 @@ from setuptools import setup setup( - name='click-example-validation', - version='1.0', - py_modules=['validation'], + name="click-example-validation", + version="1.0", + py_modules=["validation"], include_package_data=True, - install_requires=[ - 'click', - ], - entry_points=''' + install_requires=["click",], + entry_points=""" [console_scripts] validation=validation:cli - ''', + """, ) diff --git a/examples/validation/validation.py b/examples/validation/validation.py index 5cfe5f67e..9abb125c7 100644 --- a/examples/validation/validation.py +++ b/examples/validation/validation.py @@ -8,27 +8,32 @@ def validate_count(ctx, param, value): if value < 0 or value % 2 != 0: - raise click.BadParameter('Should be a positive, even integer.') + raise click.BadParameter("Should be a positive, even integer.") return value class URL(click.ParamType): - name = 'url' + name = "url" def convert(self, value, param, ctx): if not isinstance(value, tuple): value = urlparse.urlparse(value) - if value.scheme not in ('http', 'https'): - self.fail('invalid URL scheme (%s). Only HTTP URLs are ' - 'allowed' % value.scheme, param, ctx) + if value.scheme not in ("http", "https"): + self.fail( + "invalid URL scheme (%s). Only HTTP URLs are " + "allowed" % value.scheme, + param, + ctx, + ) return value @click.command() -@click.option('--count', default=2, callback=validate_count, - help='A positive even number.') -@click.option('--foo', help='A mysterious parameter.') -@click.option('--url', help='A URL', type=URL()) +@click.option( + "--count", default=2, callback=validate_count, help="A positive even number." +) +@click.option("--foo", help="A mysterious parameter.") +@click.option("--url", help="A URL", type=URL()) @click.version_option() def cli(count, foo, url): """Validation. @@ -37,9 +42,11 @@ def cli(count, foo, url): through callbacks, through a custom type as well as by validating manually in the function. """ - if foo is not None and foo != 'wat': - raise click.BadParameter('If a value is provided it needs to be the ' - 'value "wat".', param_hint=['--foo']) - click.echo('count: %s' % count) - click.echo('foo: %s' % foo) - click.echo('url: %s' % repr(url)) + if foo is not None and foo != "wat": + raise click.BadParameter( + 'If a value is provided it needs to be the value "wat".', + param_hint=["--foo"], + ) + click.echo("count: %s" % count) + click.echo("foo: %s" % foo) + click.echo("url: %s" % repr(url)) diff --git a/setup.py b/setup.py index ab2cffc68..a7af7f102 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ readme = f.read() with io.open("src/click/__init__.py", "rt", encoding="utf8") as f: - version = re.search(r"__version__ = '(.*?)'", f.read()).group(1) + version = re.search(r'__version__ = "(.*?)"', f.read()).group(1) setup( name="click", diff --git a/src/click/__init__.py b/src/click/__init__.py index caddd63b2..ac53908b4 100644 --- a/src/click/__init__.py +++ b/src/click/__init__.py @@ -76,4 +76,4 @@ # literals. disable_unicode_literals_warning = False -__version__ = '7.1.dev' +__version__ = "7.1.dev" diff --git a/src/click/_bashcomplete.py b/src/click/_bashcomplete.py index ebcf44f33..bbd45a06c 100644 --- a/src/click/_bashcomplete.py +++ b/src/click/_bashcomplete.py @@ -14,10 +14,10 @@ except ImportError: import collections as abc -WORDBREAK = '=' +WORDBREAK = "=" # Note, only BASH version 4.4 and later have the nosort option. -COMPLETION_SCRIPT_BASH = ''' +COMPLETION_SCRIPT_BASH = """ %(complete_func)s() { local IFS=$'\n' COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\ @@ -38,9 +38,9 @@ } %(complete_func)setup -''' +""" -COMPLETION_SCRIPT_ZSH = ''' +COMPLETION_SCRIPT_ZSH = """ #compdef %(script_names)s %(complete_func)s() { @@ -73,11 +73,11 @@ } compdef %(complete_func)s %(script_names)s -''' +""" -COMPLETION_SCRIPT_FISH = ''' +COMPLETION_SCRIPT_FISH = """ complete --no-files --command %(script_names)s --arguments "(env %(autocomplete_var)s=complete_fish COMP_WORDS=(commandline -cp) COMP_CWORD=(commandline -t) %(script_names)s)" -''' +""" _completion_scripts = { "bash": COMPLETION_SCRIPT_BASH, @@ -85,17 +85,20 @@ "fish": COMPLETION_SCRIPT_FISH, } -_invalid_ident_char_re = re.compile(r'[^a-zA-Z0-9_]') +_invalid_ident_char_re = re.compile(r"[^a-zA-Z0-9_]") def get_completion_script(prog_name, complete_var, shell): - cf_name = _invalid_ident_char_re.sub('', prog_name.replace('-', '_')) + cf_name = _invalid_ident_char_re.sub("", prog_name.replace("-", "_")) script = _completion_scripts.get(shell, COMPLETION_SCRIPT_BASH) - return (script % { - 'complete_func': '_%s_completion' % cf_name, - 'script_names': prog_name, - 'autocomplete_var': complete_var, - }).strip() + ';' + return ( + script + % { + "complete_func": "_%s_completion" % cf_name, + "script_names": prog_name, + "autocomplete_var": complete_var, + } + ).strip() + ";" def resolve_ctx(cli, prog_name, args): @@ -114,8 +117,9 @@ def resolve_ctx(cli, prog_name, args): cmd_name, cmd, args = ctx.command.resolve_command(ctx, args) if cmd is None: return ctx - ctx = cmd.make_context(cmd_name, args, parent=ctx, - resilient_parsing=True) + ctx = cmd.make_context( + cmd_name, args, parent=ctx, resilient_parsing=True + ) args = ctx.protected_args + ctx.args else: # Walk chained subcommand contexts saving the last one. @@ -123,10 +127,14 @@ def resolve_ctx(cli, prog_name, args): cmd_name, cmd, args = ctx.command.resolve_command(ctx, args) if cmd is None: return ctx - sub_ctx = cmd.make_context(cmd_name, args, parent=ctx, - allow_extra_args=True, - allow_interspersed_args=False, - resilient_parsing=True) + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) args = sub_ctx.args ctx = sub_ctx args = sub_ctx.protected_args + sub_ctx.args @@ -140,7 +148,7 @@ def start_of_option(param_str): :param param_str: param_str to check :return: whether or not this is the start of an option declaration (i.e. starts "-" or "--") """ - return param_str and param_str[:1] == '-' + return param_str and param_str[:1] == "-" def is_incomplete_option(all_args, cmd_param): @@ -156,7 +164,9 @@ def is_incomplete_option(all_args, cmd_param): if cmd_param.is_flag: return False last_option = None - for index, arg_str in enumerate(reversed([arg for arg in all_args if arg != WORDBREAK])): + for index, arg_str in enumerate( + reversed([arg for arg in all_args if arg != WORDBREAK]) + ): if index + 1 > cmd_param.nargs: break if start_of_option(arg_str): @@ -179,8 +189,11 @@ def is_incomplete_argument(current_params, cmd_param): return True if cmd_param.nargs == -1: return True - if isinstance(current_param_values, abc.Iterable) \ - and cmd_param.nargs > 1 and len(current_param_values) < cmd_param.nargs: + if ( + isinstance(current_param_values, abc.Iterable) + and cmd_param.nargs > 1 + and len(current_param_values) < cmd_param.nargs + ): return True return False @@ -196,14 +209,16 @@ def get_user_autocompletions(ctx, args, incomplete, cmd_param): results = [] if isinstance(cmd_param.type, Choice): # Choices don't support descriptions. - results = [(c, None) - for c in cmd_param.type.choices if str(c).startswith(incomplete)] + results = [ + (c, None) for c in cmd_param.type.choices if str(c).startswith(incomplete) + ] elif cmd_param.autocompletion is not None: - dynamic_completions = cmd_param.autocompletion(ctx=ctx, - args=args, - incomplete=incomplete) - results = [c if isinstance(c, tuple) else (c, None) - for c in dynamic_completions] + dynamic_completions = cmd_param.autocompletion( + ctx=ctx, args=args, incomplete=incomplete + ) + results = [ + c if isinstance(c, tuple) else (c, None) for c in dynamic_completions + ] return results @@ -224,15 +239,24 @@ def add_subcommand_completions(ctx, incomplete, completions_out): # Add subcommand completions. if isinstance(ctx.command, MultiCommand): completions_out.extend( - [(c.name, c.get_short_help_str()) for c in get_visible_commands_starting_with(ctx, incomplete)]) + [ + (c.name, c.get_short_help_str()) + for c in get_visible_commands_starting_with(ctx, incomplete) + ] + ) # Walk up the context list and add any other completion possibilities from chained commands while ctx.parent is not None: ctx = ctx.parent if isinstance(ctx.command, MultiCommand) and ctx.command.chain: - remaining_commands = [c for c in get_visible_commands_starting_with(ctx, incomplete) - if c.name not in ctx.protected_args] - completions_out.extend([(c.name, c.get_short_help_str()) for c in remaining_commands]) + remaining_commands = [ + c + for c in get_visible_commands_starting_with(ctx, incomplete) + if c.name not in ctx.protected_args + ] + completions_out.extend( + [(c.name, c.get_short_help_str()) for c in remaining_commands] + ) def get_choices(cli, prog_name, args, incomplete): @@ -258,16 +282,21 @@ def get_choices(cli, prog_name, args, incomplete): all_args.append(partition_incomplete[0]) incomplete = partition_incomplete[2] elif incomplete == WORDBREAK: - incomplete = '' + incomplete = "" completions = [] if not has_double_dash and start_of_option(incomplete): # completions for partial options for param in ctx.command.params: if isinstance(param, Option) and not param.hidden: - param_opts = [param_opt for param_opt in param.opts + - param.secondary_opts if param_opt not in all_args or param.multiple] - completions.extend([(o, param.help) for o in param_opts if o.startswith(incomplete)]) + param_opts = [ + param_opt + for param_opt in param.opts + param.secondary_opts + if param_opt not in all_args or param.multiple + ] + completions.extend( + [(o, param.help) for o in param_opts if o.startswith(incomplete)] + ) return completions # completion for option values from user supplied values for param in ctx.command.params: @@ -284,13 +313,13 @@ def get_choices(cli, prog_name, args, incomplete): def do_complete(cli, prog_name, include_descriptions): - cwords = split_arg_string(os.environ['COMP_WORDS']) - cword = int(os.environ['COMP_CWORD']) + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) args = cwords[1:cword] try: incomplete = cwords[cword] except IndexError: - incomplete = '' + incomplete = "" for item in get_choices(cli, prog_name, args, incomplete): echo(item[0]) @@ -298,7 +327,7 @@ def do_complete(cli, prog_name, include_descriptions): # ZSH has trouble dealing with empty array parameters when # returned from commands, use '_' to indicate no description # is present. - echo(item[1] if item[1] else '_') + echo(item[1] if item[1] else "_") return True diff --git a/src/click/_compat.py b/src/click/_compat.py index 7b7a55f69..6431fc2fd 100644 --- a/src/click/_compat.py +++ b/src/click/_compat.py @@ -6,12 +6,13 @@ from weakref import WeakKeyDictionary PY2 = sys.version_info[0] == 2 -CYGWIN = sys.platform.startswith('cygwin') -MSYS2 = sys.platform.startswith('win') and ('GCC' in sys.version) +CYGWIN = sys.platform.startswith("cygwin") +MSYS2 = sys.platform.startswith("win") and ("GCC" in sys.version) # Determine local App Engine environment, per Google's own suggestion -APP_ENGINE = ('APPENGINE_RUNTIME' in os.environ and - 'Development/' in os.environ.get('SERVER_SOFTWARE', '')) -WIN = sys.platform.startswith('win') and not APP_ENGINE and not MSYS2 +APP_ENGINE = "APPENGINE_RUNTIME" in os.environ and "Development/" in os.environ.get( + "SERVER_SOFTWARE", "" +) +WIN = sys.platform.startswith("win") and not APP_ENGINE and not MSYS2 DEFAULT_COLUMNS = 80 @@ -22,46 +23,57 @@ def get_filesystem_encoding(): return sys.getfilesystemencoding() or sys.getdefaultencoding() -def _make_text_stream(stream, encoding, errors, - force_readable=False, force_writable=False): +def _make_text_stream( + stream, encoding, errors, force_readable=False, force_writable=False +): if encoding is None: encoding = get_best_encoding(stream) if errors is None: - errors = 'replace' - return _NonClosingTextIOWrapper(stream, encoding, errors, - line_buffering=True, - force_readable=force_readable, - force_writable=force_writable) + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) def is_ascii_encoding(encoding): """Checks if a given encoding is ascii.""" try: - return codecs.lookup(encoding).name == 'ascii' + return codecs.lookup(encoding).name == "ascii" except LookupError: return False def get_best_encoding(stream): """Returns the default stream encoding if not found.""" - rv = getattr(stream, 'encoding', None) or sys.getdefaultencoding() + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() if is_ascii_encoding(rv): - return 'utf-8' + return "utf-8" return rv class _NonClosingTextIOWrapper(io.TextIOWrapper): - - def __init__(self, stream, encoding, errors, - force_readable=False, force_writable=False, **extra): - self._stream = stream = _FixupStream(stream, force_readable, - force_writable) + def __init__( + self, + stream, + encoding, + errors, + force_readable=False, + force_writable=False, + **extra + ): + self._stream = stream = _FixupStream(stream, force_readable, force_writable) io.TextIOWrapper.__init__(self, stream, encoding, errors, **extra) # The io module is a place where the Python 3 text behavior # was forced upon Python 2, so we need to unbreak # it to look like Python 2. if PY2: + def write(self, x): if isinstance(x, str) or is_bytes(x): try: @@ -105,7 +117,7 @@ def __getattr__(self, name): return getattr(self._stream, name) def read1(self, size): - f = getattr(self._stream, 'read1', None) + f = getattr(self._stream, "read1", None) if f is not None: return f(size) # We only dispatch to readline instead of read in Python 2 as we @@ -118,7 +130,7 @@ def read1(self, size): def readable(self): if self._force_readable: return True - x = getattr(self._stream, 'readable', None) + x = getattr(self._stream, "readable", None) if x is not None: return x() try: @@ -130,20 +142,20 @@ def readable(self): def writable(self): if self._force_writable: return True - x = getattr(self._stream, 'writable', None) + x = getattr(self._stream, "writable", None) if x is not None: return x() try: - self._stream.write('') + self._stream.write("") except Exception: try: - self._stream.write(b'') + self._stream.write(b"") except Exception: return False return True def seekable(self): - x = getattr(self._stream, 'seekable', None) + x = getattr(self._stream, "seekable", None) if x is not None: return x() try: @@ -166,7 +178,7 @@ def seekable(self): def is_bytes(x): return isinstance(x, (buffer, bytearray)) - _identifier_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$') + _identifier_re = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$") # For Windows, we need to force stdout/stdin/stderr to binary if it's # fetched for that. This obviously is not the most correct way to do @@ -194,6 +206,7 @@ def set_binary_mode(f): except ImportError: pass else: + def set_binary_mode(f): try: fileno = f.fileno() @@ -208,6 +221,7 @@ def set_binary_mode(f): except ImportError: pass else: + def set_binary_mode(f): try: fileno = f.fileno() @@ -225,42 +239,42 @@ def get_binary_stdin(): return set_binary_mode(sys.stdin) def get_binary_stdout(): - _wrap_std_stream('stdout') + _wrap_std_stream("stdout") return set_binary_mode(sys.stdout) def get_binary_stderr(): - _wrap_std_stream('stderr') + _wrap_std_stream("stderr") return set_binary_mode(sys.stderr) def get_text_stdin(encoding=None, errors=None): rv = _get_windows_console_stream(sys.stdin, encoding, errors) if rv is not None: return rv - return _make_text_stream(sys.stdin, encoding, errors, - force_readable=True) + return _make_text_stream(sys.stdin, encoding, errors, force_readable=True) def get_text_stdout(encoding=None, errors=None): - _wrap_std_stream('stdout') + _wrap_std_stream("stdout") rv = _get_windows_console_stream(sys.stdout, encoding, errors) if rv is not None: return rv - return _make_text_stream(sys.stdout, encoding, errors, - force_writable=True) + return _make_text_stream(sys.stdout, encoding, errors, force_writable=True) def get_text_stderr(encoding=None, errors=None): - _wrap_std_stream('stderr') + _wrap_std_stream("stderr") rv = _get_windows_console_stream(sys.stderr, encoding, errors) if rv is not None: return rv - return _make_text_stream(sys.stderr, encoding, errors, - force_writable=True) + return _make_text_stream(sys.stderr, encoding, errors, force_writable=True) def filename_to_ui(value): if isinstance(value, bytes): - value = value.decode(get_filesystem_encoding(), 'replace') + value = value.decode(get_filesystem_encoding(), "replace") return value + + else: import io + text_type = str raw_input = input string_types = (str,) @@ -284,10 +298,10 @@ def _is_binary_reader(stream, default=False): def _is_binary_writer(stream, default=False): try: - stream.write(b'') + stream.write(b"") except Exception: try: - stream.write('') + stream.write("") return False except Exception: pass @@ -302,7 +316,7 @@ def _find_binary_reader(stream): if _is_binary_reader(stream, False): return stream - buf = getattr(stream, 'buffer', None) + buf = getattr(stream, "buffer", None) # Same situation here; this time we assume that the buffer is # actually binary in case it's closed. @@ -317,7 +331,7 @@ def _find_binary_writer(stream): if _is_binary_writer(stream, False): return stream - buf = getattr(stream, 'buffer', None) + buf = getattr(stream, "buffer", None) # Same situation here; this time we assume that the buffer is # actually binary in case it's closed. @@ -330,7 +344,7 @@ def _stream_is_misconfigured(stream): # to ASCII. This appears to happen in certain unittest # environments. It's not quite clear what the correct behavior is # but this at least will force Click to recover somehow. - return is_ascii_encoding(getattr(stream, 'encoding', None) or 'ascii') + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") def _is_compat_stream_attr(stream, attr, value): """A stream attribute is compatible if it is equal to the @@ -344,10 +358,9 @@ def _is_compatible_text_stream(stream, encoding, errors): """Check if a stream's encoding and errors attributes are compatible with the desired values. """ - return ( - _is_compat_stream_attr(stream, "encoding", encoding) - and _is_compat_stream_attr(stream, "errors", errors) - ) + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) def _force_correct_text_stream( text_stream, @@ -363,12 +376,8 @@ def _force_correct_text_stream( else: # If the stream looks compatible, and won't default to a # misconfigured ascii encoding, return it as-is. - if ( - _is_compatible_text_stream(text_stream, encoding, errors) - and not ( - encoding is None - and _stream_is_misconfigured(text_stream) - ) + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) ): return text_stream @@ -418,56 +427,61 @@ def _force_correct_text_writer(text_writer, encoding, errors, force_writable=Fal def get_binary_stdin(): reader = _find_binary_reader(sys.stdin) if reader is None: - raise RuntimeError('Was not able to determine binary ' - 'stream for sys.stdin.') + raise RuntimeError( + "Was not able to determine binary stream for sys.stdin." + ) return reader def get_binary_stdout(): writer = _find_binary_writer(sys.stdout) if writer is None: - raise RuntimeError('Was not able to determine binary ' - 'stream for sys.stdout.') + raise RuntimeError( + "Was not able to determine binary stream for sys.stdout." + ) return writer def get_binary_stderr(): writer = _find_binary_writer(sys.stderr) if writer is None: - raise RuntimeError('Was not able to determine binary ' - 'stream for sys.stderr.') + raise RuntimeError( + "Was not able to determine binary stream for sys.stderr." + ) return writer def get_text_stdin(encoding=None, errors=None): rv = _get_windows_console_stream(sys.stdin, encoding, errors) if rv is not None: return rv - return _force_correct_text_reader(sys.stdin, encoding, errors, - force_readable=True) + return _force_correct_text_reader( + sys.stdin, encoding, errors, force_readable=True + ) def get_text_stdout(encoding=None, errors=None): rv = _get_windows_console_stream(sys.stdout, encoding, errors) if rv is not None: return rv - return _force_correct_text_writer(sys.stdout, encoding, errors, - force_writable=True) + return _force_correct_text_writer( + sys.stdout, encoding, errors, force_writable=True + ) def get_text_stderr(encoding=None, errors=None): rv = _get_windows_console_stream(sys.stderr, encoding, errors) if rv is not None: return rv - return _force_correct_text_writer(sys.stderr, encoding, errors, - force_writable=True) + return _force_correct_text_writer( + sys.stderr, encoding, errors, force_writable=True + ) def filename_to_ui(value): if isinstance(value, bytes): - value = value.decode(get_filesystem_encoding(), 'replace') + value = value.decode(get_filesystem_encoding(), "replace") else: - value = value.encode('utf-8', 'surrogateescape') \ - .decode('utf-8', 'replace') + value = value.encode("utf-8", "surrogateescape").decode("utf-8", "replace") return value def get_streerror(e, default=None): - if hasattr(e, 'strerror'): + if hasattr(e, "strerror"): msg = e.strerror else: if default is not None: @@ -475,7 +489,7 @@ def get_streerror(e, default=None): else: msg = str(e) if isinstance(msg, bytes): - msg = msg.decode('utf-8', 'replace') + msg = msg.decode("utf-8", "replace") return msg @@ -501,13 +515,13 @@ def _wrap_io_open(file, mode, encoding, errors): return _make_text_stream(f, **kwargs) -def open_stream(filename, mode='r', encoding=None, errors='strict', atomic=False): +def open_stream(filename, mode="r", encoding=None, errors="strict", atomic=False): binary = "b" in mode # Standard streams first. These are simple because they don't need # special handling for the atomic flag. It's entirely ignored. - if filename == '-': - if any(m in mode for m in ['w', 'a', 'x']): + if filename == "-": + if any(m in mode for m in ["w", "a", "x"]): if binary: return get_binary_stdout(), False return get_text_stdout(encoding=encoding, errors=errors), False @@ -520,17 +534,17 @@ def open_stream(filename, mode='r', encoding=None, errors='strict', atomic=False return _wrap_io_open(filename, mode, encoding, errors), True # Some usability stuff for atomic writes - if 'a' in mode: + if "a" in mode: raise ValueError( - 'Appending to an existing file is not supported, because that ' - 'would involve an expensive `copy`-operation to a temporary ' - 'file. Open the file in normal `w`-mode and copy explicitly ' - 'if that\'s what you\'re after.' + "Appending to an existing file is not supported, because that " + "would involve an expensive `copy`-operation to a temporary " + "file. Open the file in normal `w`-mode and copy explicitly " + "if that's what you're after." ) - if 'x' in mode: - raise ValueError('Use the `overwrite`-parameter instead.') - if 'w' not in mode: - raise ValueError('Atomic writes only make sense with `w`-mode.') + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") # Atomic writes are more complicated. They work by opening a file # as a proxy in the same folder and then using the fdopen @@ -547,12 +561,12 @@ def open_stream(filename, mode='r', encoding=None, errors='strict', atomic=False flags = os.O_RDWR | os.O_CREAT | os.O_EXCL if binary: - flags |= getattr(os, 'O_BINARY', 0) + flags |= getattr(os, "O_BINARY", 0) while True: tmp_filename = os.path.join( os.path.dirname(filename), - '.__atomic-write%08x' % (random.randrange(1 << 32),), + ".__atomic-write%08x" % (random.randrange(1 << 32),), ) try: fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) @@ -575,7 +589,7 @@ def open_stream(filename, mode='r', encoding=None, errors='strict', atomic=False # Used in a destructor call, needs extra protection from interpreter cleanup. -if hasattr(os, 'replace'): +if hasattr(os, "replace"): _replace = os.replace _can_replace = True else: @@ -584,7 +598,6 @@ def open_stream(filename, mode='r', encoding=None, errors='strict', atomic=False class _AtomicFile(object): - def __init__(self, f, tmp_filename, real_filename): self._f = f self._tmp_filename = tmp_filename @@ -626,7 +639,7 @@ def __repr__(self): def strip_ansi(value): - return _ansi_re.sub('', value) + return _ansi_re.sub("", value) def _is_jupyter_kernel_output(stream): @@ -660,16 +673,18 @@ def should_strip_ansi(stream=None, color=None): def _get_argv_encoding(): import locale + return locale.getpreferredencoding() if PY2: - def raw_input(prompt=''): + + def raw_input(prompt=""): sys.stderr.flush() if prompt: stdout = _default_text_stdout() stdout.write(prompt) stdin = _default_text_stdin() - return stdin.readline().rstrip('\r\n') + return stdin.readline().rstrip("\r\n") try: import colorama @@ -711,11 +726,15 @@ def _safe_write(s): def get_winterm_size(): win = colorama.win32.GetConsoleScreenBufferInfo( - colorama.win32.STDOUT).srWindow + colorama.win32.STDOUT + ).srWindow return win.Right - win.Left, win.Bottom - win.Top + + else: + def _get_argv_encoding(): - return getattr(sys.stdin, 'encoding', None) or get_filesystem_encoding() + return getattr(sys.stdin, "encoding", None) or get_filesystem_encoding() _get_windows_console_stream = lambda *x: None _wrap_std_stream = lambda *x: None @@ -734,6 +753,7 @@ def isatty(stream): def _make_cached_stream_func(src_func, wrapper_func): cache = WeakKeyDictionary() + def func(): stream = src_func() try: @@ -749,25 +769,23 @@ def func(): except Exception: pass return rv + return func -_default_text_stdin = _make_cached_stream_func( - lambda: sys.stdin, get_text_stdin) -_default_text_stdout = _make_cached_stream_func( - lambda: sys.stdout, get_text_stdout) -_default_text_stderr = _make_cached_stream_func( - lambda: sys.stderr, get_text_stderr) +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) binary_streams = { - 'stdin': get_binary_stdin, - 'stdout': get_binary_stdout, - 'stderr': get_binary_stderr, + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, } text_streams = { - 'stdin': get_text_stdin, - 'stdout': get_text_stdout, - 'stderr': get_text_stderr, + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, } diff --git a/src/click/_termui_impl.py b/src/click/_termui_impl.py index e6974a70b..784c64b2d 100644 --- a/src/click/_termui_impl.py +++ b/src/click/_termui_impl.py @@ -24,12 +24,12 @@ from .exceptions import ClickException from .utils import echo -if os.name == 'nt': - BEFORE_BAR = '\r' - AFTER_BAR = '\n' +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" else: - BEFORE_BAR = '\r\033[?25l' - AFTER_BAR = '\033[?25h\n' + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" def _length_hint(obj): @@ -45,19 +45,29 @@ def _length_hint(obj): hint = get_hint(obj) except TypeError: return None - if hint is NotImplemented or \ - not isinstance(hint, int_types) or \ - hint < 0: + if hint is NotImplemented or not isinstance(hint, int_types) or hint < 0: return None return hint class ProgressBar(object): - - def __init__(self, iterable, length=None, fill_char='#', empty_char=' ', - bar_template='%(bar)s', info_sep=' ', show_eta=True, - show_percent=None, show_pos=False, item_show_func=None, - label=None, file=None, color=None, width=30): + def __init__( + self, + iterable, + length=None, + fill_char="#", + empty_char=" ", + bar_template="%(bar)s", + info_sep=" ", + show_eta=True, + show_percent=None, + show_pos=False, + item_show_func=None, + label=None, + file=None, + color=None, + width=30, + ): self.fill_char = fill_char self.empty_char = empty_char self.bar_template = bar_template @@ -66,7 +76,7 @@ def __init__(self, iterable, length=None, fill_char='#', empty_char=' ', self.show_percent = show_percent self.show_pos = show_pos self.item_show_func = item_show_func - self.label = label or '' + self.label = label or "" if file is None: file = _default_text_stdout() self.file = file @@ -78,7 +88,7 @@ def __init__(self, iterable, length=None, fill_char='#', empty_char=' ', length = _length_hint(iterable) if iterable is None: if length is None: - raise TypeError('iterable or length is required') + raise TypeError("iterable or length is required") iterable = range_type(length) self.iter = iter(iterable) self.length = length @@ -105,7 +115,7 @@ def __exit__(self, exc_type, exc_value, tb): def __iter__(self): if not self.entered: - raise RuntimeError('You need to use progress bars in a with block.') + raise RuntimeError("You need to use progress bars in a with block.") self.render_progress() return self.generator() @@ -158,19 +168,19 @@ def format_eta(self): t //= 24 if t > 0: days = t - return '%dd %02d:%02d:%02d' % (days, hours, minutes, seconds) + return "%dd %02d:%02d:%02d" % (days, hours, minutes, seconds) else: - return '%02d:%02d:%02d' % (hours, minutes, seconds) - return '' + return "%02d:%02d:%02d" % (hours, minutes, seconds) + return "" def format_pos(self): pos = str(self.pos) if self.length_known: - pos += '/%s' % self.length + pos += "/%s" % self.length return pos def format_pct(self): - return ('% 4d%%' % int(self.pct * 100))[1:] + return ("% 4d%%" % int(self.pct * 100))[1:] def format_bar(self): if self.length_known: @@ -182,9 +192,13 @@ def format_bar(self): else: bar = list(self.empty_char * (self.width or 1)) if self.time_per_iteration != 0: - bar[int((math.cos(self.pos * self.time_per_iteration) - / 2.0 + 0.5) * self.width)] = self.fill_char - bar = ''.join(bar) + bar[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(bar) return bar def format_progress_line(self): @@ -205,11 +219,14 @@ def format_progress_line(self): if item_info is not None: info_bits.append(item_info) - return (self.bar_template % { - 'label': self.label, - 'bar': self.format_bar(), - 'info': self.info_sep.join(info_bits) - }).rstrip() + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() def render_progress(self): from .termui import get_terminal_size @@ -226,7 +243,7 @@ def render_progress(self): new_width = max(0, get_terminal_size()[0] - clutter_length) if new_width < old_width: buf.append(BEFORE_BAR) - buf.append(' ' * self.max_width) + buf.append(" " * self.max_width) self.max_width = new_width self.width = new_width @@ -241,8 +258,8 @@ def render_progress(self): self.max_width = line_len buf.append(line) - buf.append(' ' * (clear_width - line_len)) - line = ''.join(buf) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) # Render the line only if it changed. if line != self._last_line and not self.is_fast(): @@ -294,7 +311,7 @@ def generator(self): # `self.generator()` repeatedly, and this must remain safe in # order for that interface to work. if not self.entered: - raise RuntimeError('You need to use progress bars in a with block.') + raise RuntimeError("You need to use progress bars in a with block.") if self.is_hidden: for rv in self.iter: @@ -313,24 +330,25 @@ def pager(generator, color=None): stdout = _default_text_stdout() if not isatty(sys.stdin) or not isatty(stdout): return _nullpager(stdout, generator, color) - pager_cmd = (os.environ.get('PAGER', None) or '').strip() + pager_cmd = (os.environ.get("PAGER", None) or "").strip() if pager_cmd: if WIN: return _tempfilepager(generator, pager_cmd, color) return _pipepager(generator, pager_cmd, color) - if os.environ.get('TERM') in ('dumb', 'emacs'): + if os.environ.get("TERM") in ("dumb", "emacs"): return _nullpager(stdout, generator, color) - if WIN or sys.platform.startswith('os2'): - return _tempfilepager(generator, 'more <', color) - if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: - return _pipepager(generator, 'less', color) + if WIN or sys.platform.startswith("os2"): + return _tempfilepager(generator, "more <", color) + if hasattr(os, "system") and os.system("(less) 2>/dev/null") == 0: + return _pipepager(generator, "less", color) import tempfile + fd, filename = tempfile.mkstemp() os.close(fd) try: - if hasattr(os, 'system') and os.system("more %s" % shlex_quote(filename)) == 0: - return _pipepager(generator, 'more', color) + if hasattr(os, "system") and os.system("more %s" % shlex_quote(filename)) == 0: + return _pipepager(generator, "more", color) return _nullpager(stdout, generator, color) finally: os.unlink(filename) @@ -341,28 +359,28 @@ def _pipepager(generator, cmd, color): pager through this might support colors. """ import subprocess + env = dict(os.environ) # If we're piping to less we might support colors under the # condition that - cmd_detail = cmd.rsplit('/', 1)[-1].split() - if color is None and cmd_detail[0] == 'less': - less_flags = os.environ.get('LESS', '') + ' '.join(cmd_detail[1:]) + cmd_detail = cmd.rsplit("/", 1)[-1].split() + if color is None and cmd_detail[0] == "less": + less_flags = os.environ.get("LESS", "") + " ".join(cmd_detail[1:]) if not less_flags: - env['LESS'] = '-R' + env["LESS"] = "-R" color = True - elif 'r' in less_flags or 'R' in less_flags: + elif "r" in less_flags or "R" in less_flags: color = True - c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, - env=env) + c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, env=env) encoding = get_best_encoding(c.stdin) try: for text in generator: if not color: text = strip_ansi(text) - c.stdin.write(text.encode(encoding, 'replace')) + c.stdin.write(text.encode(encoding, "replace")) except (IOError, KeyboardInterrupt): pass else: @@ -388,13 +406,14 @@ def _pipepager(generator, cmd, color): def _tempfilepager(generator, cmd, color): """Page through text by invoking a program on a temporary file.""" import tempfile + filename = tempfile.mktemp() # TODO: This never terminates if the passed generator never terminates. text = "".join(generator) if not color: text = strip_ansi(text) encoding = get_best_encoding(sys.stdout) - with open_stream(filename, 'wb')[0] as f: + with open_stream(filename, "wb")[0] as f: f.write(text.encode(encoding)) try: os.system("%s %s" % (shlex_quote(cmd), shlex_quote(filename))) @@ -411,9 +430,7 @@ def _nullpager(stream, generator, color): class Editor(object): - - def __init__(self, editor=None, env=None, require_save=True, - extension='.txt'): + def __init__(self, editor=None, env=None, require_save=True, extension=".txt"): self.editor = editor self.env = env self.require_save = require_save @@ -422,19 +439,20 @@ def __init__(self, editor=None, env=None, require_save=True, def get_editor(self): if self.editor is not None: return self.editor - for key in 'VISUAL', 'EDITOR': + for key in "VISUAL", "EDITOR": rv = os.environ.get(key) if rv: return rv if WIN: - return 'notepad' - for editor in 'sensible-editor', 'vim', 'nano': - if os.system('which %s >/dev/null 2>&1' % editor) == 0: + return "notepad" + for editor in "sensible-editor", "vim", "nano": + if os.system("which %s >/dev/null 2>&1" % editor) == 0: return editor - return 'vi' + return "vi" def edit_file(self, filename): import subprocess + editor = self.get_editor() if self.env: environ = os.environ.copy() @@ -445,47 +463,46 @@ def edit_file(self, filename): c = subprocess.Popen( "%s %s" % (shlex_quote(editor), shlex_quote(filename)), env=environ, - shell=True + shell=True, ) exit_code = c.wait() if exit_code != 0: - raise ClickException('%s: Editing failed!' % editor) + raise ClickException("%s: Editing failed!" % editor) except OSError as e: - raise ClickException('%s: Editing failed: %s' % (editor, e)) + raise ClickException("%s: Editing failed: %s" % (editor, e)) def edit(self, text): import tempfile - text = text or '' - if text and not text.endswith('\n'): - text += '\n' + text = text or "" + if text and not text.endswith("\n"): + text += "\n" - fd, name = tempfile.mkstemp(prefix='editor-', suffix=self.extension) + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) try: if WIN: - encoding = 'utf-8-sig' - text = text.replace('\n', '\r\n') + encoding = "utf-8-sig" + text = text.replace("\n", "\r\n") else: - encoding = 'utf-8' + encoding = "utf-8" text = text.encode(encoding) - f = os.fdopen(fd, 'wb') + f = os.fdopen(fd, "wb") f.write(text) f.close() timestamp = os.path.getmtime(name) self.edit_file(name) - if self.require_save \ - and os.path.getmtime(name) == timestamp: + if self.require_save and os.path.getmtime(name) == timestamp: return None - f = open(name, 'rb') + f = open(name, "rb") try: rv = f.read() finally: f.close() - return rv.decode('utf-8-sig').replace('\r\n', '\n') + return rv.decode("utf-8-sig").replace("\r\n", "\n") finally: os.unlink(name) @@ -498,18 +515,18 @@ def _unquote_file(url): import urllib except ImportError: import urllib - if url.startswith('file://'): + if url.startswith("file://"): url = urllib.unquote(url[7:]) return url - if sys.platform == 'darwin': - args = ['open'] + if sys.platform == "darwin": + args = ["open"] if wait: - args.append('-W') + args.append("-W") if locate: - args.append('-R') + args.append("-R") args.append(_unquote_file(url)) - null = open('/dev/null', 'w') + null = open("/dev/null", "w") try: return subprocess.Popen(args, stderr=null).wait() finally: @@ -531,27 +548,28 @@ def _unquote_file(url): try: if locate: - url = os.path.dirname(_unquote_file(url)) or '.' + url = os.path.dirname(_unquote_file(url)) or "." else: url = _unquote_file(url) - c = subprocess.Popen(['xdg-open', url]) + c = subprocess.Popen(["xdg-open", url]) if wait: return c.wait() return 0 except OSError: - if url.startswith(('http://', 'https://')) and not locate and not wait: + if url.startswith(("http://", "https://")) and not locate and not wait: import webbrowser + webbrowser.open(url) return 0 return 1 def _translate_ch_to_exc(ch): - if ch == u'\x03': + if ch == u"\x03": raise KeyboardInterrupt() - if ch == u'\x04' and not WIN: # Unix-like, Ctrl+D + if ch == u"\x04" and not WIN: # Unix-like, Ctrl+D raise EOFError() - if ch == u'\x1a' and WIN: # Windows, Ctrl+Z + if ch == u"\x1a" and WIN: # Windows, Ctrl+Z raise EOFError() @@ -598,12 +616,14 @@ def getchar(echo): func = msvcrt.getwch rv = func() - if rv in (u'\x00', u'\xe0'): + if rv in (u"\x00", u"\xe0"): # \x00 and \xe0 are control characters that indicate special key, # see above. rv += func() _translate_ch_to_exc(rv) return rv + + else: import tty import termios @@ -611,7 +631,7 @@ def getchar(echo): @contextlib.contextmanager def raw_terminal(): if not isatty(sys.stdin): - f = open('/dev/tty') + f = open("/dev/tty") fd = f.fileno() else: fd = sys.stdin.fileno() @@ -632,7 +652,7 @@ def raw_terminal(): def getchar(echo): with raw_terminal() as fd: ch = os.read(fd, 32) - ch = ch.decode(get_best_encoding(sys.stdin), 'replace') + ch = ch.decode(get_best_encoding(sys.stdin), "replace") if echo and isatty(sys.stdout): sys.stdout.write(ch) _translate_ch_to_exc(ch) diff --git a/src/click/_textwrap.py b/src/click/_textwrap.py index 7e776031e..6959087b7 100644 --- a/src/click/_textwrap.py +++ b/src/click/_textwrap.py @@ -3,7 +3,6 @@ class TextWrapper(textwrap.TextWrapper): - def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): space_left = max(width - cur_len, 1) @@ -35,4 +34,4 @@ def indent_only(self, text): if idx > 0: indent = self.subsequent_indent rv.append(indent + line) - return '\n'.join(rv) + return "\n".join(rv) diff --git a/src/click/_unicodefun.py b/src/click/_unicodefun.py index c0e9a27e5..4d0a169e3 100644 --- a/src/click/_unicodefun.py +++ b/src/click/_unicodefun.py @@ -8,17 +8,18 @@ # correct package is discovered. Ideally we could use a # relative import here but unfortunately Python does not # support that. -click = sys.modules[__name__.rsplit('.', 1)[0]] +click = sys.modules[__name__.rsplit(".", 1)[0]] def _find_unicode_literals_frame(): import __future__ - if not hasattr(sys, '_getframe'): # not all Python implementations have it + + if not hasattr(sys, "_getframe"): # not all Python implementations have it return 0 frm = sys._getframe(1) idx = 1 while frm is not None: - if frm.f_globals.get('__name__', '').startswith('click.'): + if frm.f_globals.get("__name__", "").startswith("click."): frm = frm.f_back idx += 1 elif frm.f_code.co_flags & __future__.unicode_literals.compiler_flag: @@ -37,13 +38,18 @@ def _check_for_unicode_literals(): if bad_frame <= 0: return from warnings import warn - warn(Warning('Click detected the use of the unicode_literals ' - '__future__ import. This is heavily discouraged ' - 'because it can introduce subtle bugs in your ' - 'code. You should instead use explicit u"" literals ' - 'for your unicode strings. For more information see ' - 'https://click.palletsprojects.com/en/7.x/python3/'), - stacklevel=bad_frame) + + warn( + Warning( + "Click detected the use of the unicode_literals " + "__future__ import. This is heavily discouraged " + "because it can introduce subtle bugs in your " + 'code. You should instead use explicit u"" literals ' + "for your unicode strings. For more information see " + "https://click.palletsprojects.com/en/7.x/python3/" + ), + stacklevel=bad_frame, + ) def _verify_python3_env(): @@ -52,73 +58,76 @@ def _verify_python3_env(): return try: import locale + fs_enc = codecs.lookup(locale.getpreferredencoding()).name except Exception: - fs_enc = 'ascii' - if fs_enc != 'ascii': + fs_enc = "ascii" + if fs_enc != "ascii": return - extra = '' - if os.name == 'posix': + extra = "" + if os.name == "posix": import subprocess + try: - rv = subprocess.Popen(['locale', '-a'], stdout=subprocess.PIPE, - stderr=subprocess.PIPE).communicate()[0] + rv = subprocess.Popen( + ["locale", "-a"], stdout=subprocess.PIPE, stderr=subprocess.PIPE + ).communicate()[0] except OSError: - rv = b'' + rv = b"" good_locales = set() has_c_utf8 = False # Make sure we're operating on text here. if isinstance(rv, bytes): - rv = rv.decode('ascii', 'replace') + rv = rv.decode("ascii", "replace") for line in rv.splitlines(): locale = line.strip() - if locale.lower().endswith(('.utf-8', '.utf8')): + if locale.lower().endswith((".utf-8", ".utf8")): good_locales.add(locale) - if locale.lower() in ('c.utf8', 'c.utf-8'): + if locale.lower() in ("c.utf8", "c.utf-8"): has_c_utf8 = True - extra += '\n\n' + extra += "\n\n" if not good_locales: extra += ( - 'Additional information: on this system no suitable UTF-8\n' - 'locales were discovered. This most likely requires resolving\n' - 'by reconfiguring the locale system.' + "Additional information: on this system no suitable UTF-8\n" + "locales were discovered. This most likely requires resolving\n" + "by reconfiguring the locale system." ) elif has_c_utf8: extra += ( - 'This system supports the C.UTF-8 locale which is recommended.\n' - 'You might be able to resolve your issue by exporting the\n' - 'following environment variables:\n\n' - ' export LC_ALL=C.UTF-8\n' - ' export LANG=C.UTF-8' + "This system supports the C.UTF-8 locale which is recommended.\n" + "You might be able to resolve your issue by exporting the\n" + "following environment variables:\n\n" + " export LC_ALL=C.UTF-8\n" + " export LANG=C.UTF-8" ) else: extra += ( - 'This system lists a couple of UTF-8 supporting locales that\n' - 'you can pick from. The following suitable locales were\n' - 'discovered: %s' - ) % ', '.join(sorted(good_locales)) + "This system lists a couple of UTF-8 supporting locales that\n" + "you can pick from. The following suitable locales were\n" + "discovered: %s" + ) % ", ".join(sorted(good_locales)) bad_locale = None - for locale in os.environ.get('LC_ALL'), os.environ.get('LANG'): - if locale and locale.lower().endswith(('.utf-8', '.utf8')): + for locale in os.environ.get("LC_ALL"), os.environ.get("LANG"): + if locale and locale.lower().endswith((".utf-8", ".utf8")): bad_locale = locale if locale is not None: break if bad_locale is not None: extra += ( - '\n\nClick discovered that you exported a UTF-8 locale\n' - 'but the locale system could not pick up from it because\n' + "\n\nClick discovered that you exported a UTF-8 locale\n" + "but the locale system could not pick up from it because\n" 'it does not exist. The exported locale is "%s" but it\n' - 'is not supported' + "is not supported" ) % bad_locale raise RuntimeError( - 'Click will abort further execution because Python 3 was' - ' configured to use ASCII as encoding for the environment.' - ' Consult https://click.palletsprojects.com/en/7.x/python3/ for' - ' mitigation steps.' + extra + "Click will abort further execution because Python 3 was" + " configured to use ASCII as encoding for the environment." + " Consult https://click.palletsprojects.com/en/7.x/python3/ for" + " mitigation steps." + extra ) diff --git a/src/click/_winconsole.py b/src/click/_winconsole.py index 686a23975..ca43502e5 100644 --- a/src/click/_winconsole.py +++ b/src/click/_winconsole.py @@ -38,6 +38,7 @@ try: from ctypes import pythonapi + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer PyBuffer_Release = pythonapi.PyBuffer_Release except ImportError: @@ -52,13 +53,13 @@ WriteConsoleW = kernel32.WriteConsoleW GetConsoleMode = kernel32.GetConsoleMode GetLastError = kernel32.GetLastError -GetCommandLineW = WINFUNCTYPE(LPWSTR)( - ('GetCommandLineW', windll.kernel32)) -CommandLineToArgvW = WINFUNCTYPE( - POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( - ('CommandLineToArgvW', windll.shell32)) +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) LocalFree = WINFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)( - ('LocalFree', windll.kernel32)) + ("LocalFree", windll.kernel32) +) STDIN_HANDLE = GetStdHandle(-10) @@ -77,27 +78,27 @@ STDOUT_FILENO = 1 STDERR_FILENO = 2 -EOF = b'\x1a' +EOF = b"\x1a" MAX_BYTES_WRITTEN = 32767 class Py_buffer(ctypes.Structure): _fields_ = [ - ('buf', c_void_p), - ('obj', py_object), - ('len', c_ssize_t), - ('itemsize', c_ssize_t), - ('readonly', c_int), - ('ndim', c_int), - ('format', c_char_p), - ('shape', c_ssize_p), - ('strides', c_ssize_p), - ('suboffsets', c_ssize_p), - ('internal', c_void_p) + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), ] if PY2: - _fields_.insert(-1, ('smalltable', c_ssize_t * 2)) + _fields_.insert(-1, ("smalltable", c_ssize_t * 2)) # On PyPy we cannot get buffers so our ability to operate here is @@ -105,6 +106,7 @@ class Py_buffer(ctypes.Structure): if pythonapi is None: get_buffer = None else: + def get_buffer(obj, writable=False): buf = Py_buffer() flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE @@ -117,7 +119,6 @@ def get_buffer(obj, writable=False): class _WindowsConsoleRawIOBase(io.RawIOBase): - def __init__(self, handle): self.handle = handle @@ -127,7 +128,6 @@ def isatty(self): class _WindowsConsoleReader(_WindowsConsoleRawIOBase): - def readable(self): return True @@ -136,20 +136,26 @@ def readinto(self, b): if not bytes_to_be_read: return 0 elif bytes_to_be_read % 2: - raise ValueError('cannot read odd number of bytes from ' - 'UTF-16-LE encoded console') + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) buffer = get_buffer(b, writable=True) code_units_to_be_read = bytes_to_be_read // 2 code_units_read = c_ulong() - rv = ReadConsoleW(HANDLE(self.handle), buffer, code_units_to_be_read, - byref(code_units_read), None) + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) if GetLastError() == ERROR_OPERATION_ABORTED: # wait for KeyboardInterrupt time.sleep(0.1) if not rv: - raise OSError('Windows error: %s' % GetLastError()) + raise OSError("Windows error: %s" % GetLastError()) if buffer[0] == EOF: return 0 @@ -157,27 +163,30 @@ def readinto(self, b): class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): - def writable(self): return True @staticmethod def _get_error_message(errno): if errno == ERROR_SUCCESS: - return 'ERROR_SUCCESS' + return "ERROR_SUCCESS" elif errno == ERROR_NOT_ENOUGH_MEMORY: - return 'ERROR_NOT_ENOUGH_MEMORY' - return 'Windows error %s' % errno + return "ERROR_NOT_ENOUGH_MEMORY" + return "Windows error %s" % errno def write(self, b): bytes_to_be_written = len(b) buf = get_buffer(b) - code_units_to_be_written = min(bytes_to_be_written, - MAX_BYTES_WRITTEN) // 2 + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 code_units_written = c_ulong() - WriteConsoleW(HANDLE(self.handle), buf, code_units_to_be_written, - byref(code_units_written), None) + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) bytes_written = 2 * code_units_written.value if bytes_written == 0 and bytes_to_be_written > 0: @@ -186,7 +195,6 @@ def write(self, b): class ConsoleStream(object): - def __init__(self, text_stream, byte_stream): self._text_stream = text_stream self.buffer = byte_stream @@ -215,10 +223,7 @@ def isatty(self): return self.buffer.isatty() def __repr__(self): - return '' % ( - self.name, - self.encoding, - ) + return "" % (self.name, self.encoding,) class WindowsChunkedWriter(object): @@ -227,6 +232,7 @@ class WindowsChunkedWriter(object): attribute access apart from method 'write()' which we wrap to write in limited chunks due to a Windows limitation on binary console streams. """ + def __init__(self, wrapped): # double-underscore everything to prevent clashes with names of # attributes on the wrapped stream object. @@ -241,7 +247,7 @@ def write(self, text): while written < total_to_write: to_write = min(total_to_write - written, MAX_BYTES_WRITTEN) - self.__wrapped.write(text[written:written+to_write]) + self.__wrapped.write(text[written : written + to_write]) written += to_write @@ -250,7 +256,11 @@ def write(self, text): def _wrap_std_stream(name): # Python 2 & Windows 7 and below - if PY2 and sys.getwindowsversion()[:2] <= (6, 1) and name not in _wrapped_std_streams: + if ( + PY2 + and sys.getwindowsversion()[:2] <= (6, 1) + and name not in _wrapped_std_streams + ): setattr(sys, name, WindowsChunkedWriter(getattr(sys, name))) _wrapped_std_streams.add(name) @@ -258,27 +268,37 @@ def _wrap_std_stream(name): def _get_text_stdin(buffer_stream): text_stream = _NonClosingTextIOWrapper( io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), - 'utf-16-le', 'strict', line_buffering=True) + "utf-16-le", + "strict", + line_buffering=True, + ) return ConsoleStream(text_stream, buffer_stream) def _get_text_stdout(buffer_stream): text_stream = _NonClosingTextIOWrapper( io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), - 'utf-16-le', 'strict', line_buffering=True) + "utf-16-le", + "strict", + line_buffering=True, + ) return ConsoleStream(text_stream, buffer_stream) def _get_text_stderr(buffer_stream): text_stream = _NonClosingTextIOWrapper( io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), - 'utf-16-le', 'strict', line_buffering=True) + "utf-16-le", + "strict", + line_buffering=True, + ) return ConsoleStream(text_stream, buffer_stream) if PY2: + def _hash_py_argv(): - return zlib.crc32('\x00'.join(sys.argv[1:])) + return zlib.crc32("\x00".join(sys.argv[1:])) _initial_argv_hash = _hash_py_argv() @@ -293,14 +313,14 @@ def _get_windows_argv(): LocalFree(argv_unicode) del argv_unicode - if not hasattr(sys, 'frozen'): + if not hasattr(sys, "frozen"): argv = argv[1:] while len(argv) > 0: arg = argv[0] - if not arg.startswith('-') or arg == '-': + if not arg.startswith("-") or arg == "-": break argv = argv[1:] - if arg.startswith(('-c', '-m')): + if arg.startswith(("-c", "-m")): break return argv[1:] @@ -314,7 +334,7 @@ def _get_windows_argv(): def _is_console(f): - if not hasattr(f, 'fileno'): + if not hasattr(f, "fileno"): return False try: @@ -327,14 +347,16 @@ def _is_console(f): def _get_windows_console_stream(f, encoding, errors): - if get_buffer is not None and \ - encoding in ('utf-16-le', None) \ - and errors in ('strict', None) and \ - _is_console(f): + if ( + get_buffer is not None + and encoding in ("utf-16-le", None) + and errors in ("strict", None) + and _is_console(f) + ): func = _stream_factories.get(f.fileno()) if func is not None: if not PY2: - f = getattr(f, 'buffer', None) + f = getattr(f, "buffer", None) if f is None: return None else: diff --git a/src/click/core.py b/src/click/core.py index 96c3ebc36..ea46e8687 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -38,17 +38,18 @@ _missing = object() -SUBCOMMAND_METAVAR = 'COMMAND [ARGS]...' -SUBCOMMANDS_METAVAR = 'COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...' +SUBCOMMAND_METAVAR = "COMMAND [ARGS]..." +SUBCOMMANDS_METAVAR = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." -DEPRECATED_HELP_NOTICE = ' (DEPRECATED)' -DEPRECATED_INVOKE_NOTICE = 'DeprecationWarning: ' + \ - 'The command %(name)s is deprecated.' +DEPRECATED_HELP_NOTICE = " (DEPRECATED)" +DEPRECATED_INVOKE_NOTICE = ( + "DeprecationWarning: " + "The command %(name)s is deprecated." +) def _maybe_show_deprecated_notice(cmd): if cmd.deprecated: - echo(style(DEPRECATED_INVOKE_NOTICE % {'name': cmd.name}, fg='red'), err=True) + echo(style(DEPRECATED_INVOKE_NOTICE % {"name": cmd.name}, fg="red"), err=True) def fast_exit(code): @@ -63,12 +64,13 @@ def fast_exit(code): def _bashcomplete(cmd, prog_name, complete_var=None): """Internal handler for the bash completion support.""" if complete_var is None: - complete_var = '_%s_COMPLETE' % (prog_name.replace('-', '_')).upper() + complete_var = "_%s_COMPLETE" % (prog_name.replace("-", "_")).upper() complete_instr = os.environ.get(complete_var) if not complete_instr: return from ._bashcomplete import bashcomplete + if bashcomplete(cmd, prog_name, complete_var, complete_instr): fast_exit(1) @@ -77,19 +79,30 @@ def _check_multicommand(base_command, cmd_name, cmd, register=False): if not base_command.chain or not isinstance(cmd, MultiCommand): return if register: - hint = 'It is not possible to add multi commands as children to ' \ - 'another multi command that is in chain mode' + hint = ( + "It is not possible to add multi commands as children to " + "another multi command that is in chain mode" + ) else: - hint = 'Found a multi command as subcommand to a multi command ' \ - 'that is in chain mode. This is not supported' - raise RuntimeError('%s. Command "%s" is set to chain and "%s" was ' - 'added as subcommand but it in itself is a ' - 'multi command. ("%s" is a %s within a chained ' - '%s named "%s").' % ( - hint, base_command.name, cmd_name, - cmd_name, cmd.__class__.__name__, - base_command.__class__.__name__, - base_command.name)) + hint = ( + "Found a multi command as subcommand to a multi command " + "that is in chain mode. This is not supported" + ) + raise RuntimeError( + '%s. Command "%s" is set to chain and "%s" was ' + "added as subcommand but it in itself is a " + 'multi command. ("%s" is a %s within a chained ' + '%s named "%s").' + % ( + hint, + base_command.name, + cmd_name, + cmd_name, + cmd.__class__.__name__, + base_command.__class__.__name__, + base_command.name, + ) + ) def batch(iterable, batch_size): @@ -97,16 +110,21 @@ def batch(iterable, batch_size): def invoke_param_callback(callback, ctx, param, value): - code = getattr(callback, '__code__', None) - args = getattr(code, 'co_argcount', 3) + code = getattr(callback, "__code__", None) + args = getattr(code, "co_argcount", 3) if args < 3: # This will become a warning in Click 3.0: from warnings import warn - warn(Warning('Invoked legacy parameter callback "%s". The new ' - 'signature for such callbacks starting with ' - 'click 2.0 is (ctx, param, value).' - % callback), stacklevel=3) + + warn( + Warning( + 'Invoked legacy parameter callback "%s". The new ' + "signature for such callbacks starting with " + "click 2.0 is (ctx, param, value)." % callback + ), + stacklevel=3, + ) return callback(ctx, value) return callback(ctx, param, value) @@ -135,11 +153,12 @@ def iter_params_for_processing(invocation_order, declaration_order): for processing and an iterable of parameters that exist, this returns a list in the correct order as they should be processed. """ + def sort_key(item): try: idx = invocation_order.index(item) except ValueError: - idx = float('inf') + idx = float("inf") return (not item.is_eager, idx) return sorted(declaration_order, key=sort_key) @@ -231,13 +250,25 @@ class Context(object): this command-level setting overrides it. """ - def __init__(self, command, parent=None, info_name=None, obj=None, - auto_envvar_prefix=None, default_map=None, - terminal_width=None, max_content_width=None, - resilient_parsing=False, allow_extra_args=None, - allow_interspersed_args=None, - ignore_unknown_options=None, help_option_names=None, - token_normalize_func=None, color=None, show_default=None): + def __init__( + self, + command, + parent=None, + info_name=None, + obj=None, + auto_envvar_prefix=None, + default_map=None, + terminal_width=None, + max_content_width=None, + resilient_parsing=False, + allow_extra_args=None, + allow_interspersed_args=None, + ignore_unknown_options=None, + help_option_names=None, + token_normalize_func=None, + color=None, + show_default=None, + ): #: the parent context or `None` if none exists. self.parent = parent #: the :class:`Command` for this context. @@ -258,12 +289,14 @@ def __init__(self, command, parent=None, info_name=None, obj=None, obj = parent.obj #: the user object stored. self.obj = obj - self._meta = getattr(parent, 'meta', {}) + self._meta = getattr(parent, "meta", {}) #: A dictionary (-like object) with defaults for parameters. - if default_map is None \ - and parent is not None \ - and parent.default_map is not None: + if ( + default_map is None + and parent is not None + and parent.default_map is not None + ): default_map = parent.default_map.get(info_name) self.default_map = default_map @@ -322,7 +355,7 @@ def __init__(self, command, parent=None, info_name=None, obj=None, if parent is not None: help_option_names = parent.help_option_names else: - help_option_names = ['--help'] + help_option_names = ["--help"] #: The names for the help options. self.help_option_names = help_option_names @@ -343,11 +376,15 @@ def __init__(self, command, parent=None, info_name=None, obj=None, # the command on this level has a name, we can expand the envvar # prefix automatically. if auto_envvar_prefix is None: - if parent is not None \ - and parent.auto_envvar_prefix is not None and \ - self.info_name is not None: - auto_envvar_prefix = '%s_%s' % (parent.auto_envvar_prefix, - self.info_name.upper()) + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = "%s_%s" % ( + parent.auto_envvar_prefix, + self.info_name.upper(), + ) else: auto_envvar_prefix = auto_envvar_prefix.upper() if auto_envvar_prefix is not None: @@ -444,8 +481,9 @@ def get_language(): def make_formatter(self): """Creates the formatter for the help and usage output.""" - return HelpFormatter(width=self.terminal_width, - max_width=self.max_content_width) + return HelpFormatter( + width=self.terminal_width, max_width=self.max_content_width + ) def call_on_close(self, f): """This decorator remembers a function as callback that should be @@ -471,11 +509,11 @@ def command_path(self): information on the help page. It's automatically created by combining the info names of the chain of contexts to the root. """ - rv = '' + rv = "" if self.info_name is not None: rv = self.info_name if self.parent is not None: - rv = self.parent.command_path + ' ' + rv + rv = self.parent.command_path + " " + rv return rv.lstrip() def find_root(self): @@ -567,8 +605,9 @@ def invoke(*args, **kwargs): callback = other_cmd.callback ctx = Context(other_cmd, info_name=other_cmd.name, parent=self) if callback is None: - raise TypeError('The given command does not have a ' - 'callback that can be invoked.') + raise TypeError( + "The given command does not have a callback that can be invoked." + ) for param in other_cmd.params: if param.name not in kwargs and param.expose_value: @@ -589,7 +628,7 @@ def forward(*args, **kwargs): # It's also possible to invoke another command which might or # might not have a callback. if not isinstance(cmd, Command): - raise TypeError('Callback is not a command.') + raise TypeError("Callback is not a command.") for param in self.params: if param not in kwargs: @@ -619,6 +658,7 @@ class BaseCommand(object): :param context_settings: an optional dictionary with defaults that are passed to the context object. """ + #: the default for the :attr:`Context.allow_extra_args` flag. allow_extra_args = False #: the default for the :attr:`Context.allow_interspersed_args` flag. @@ -641,10 +681,10 @@ def __repr__(self): return "<%s %s>" % (self.__class__.__name__, self.name) def get_usage(self, ctx): - raise NotImplementedError('Base commands cannot get usage') + raise NotImplementedError("Base commands cannot get usage") def get_help(self, ctx): - raise NotImplementedError('Base commands cannot get help') + raise NotImplementedError("Base commands cannot get help") def make_context(self, info_name, args, parent=None, **extra): """This function when given an info name and arguments will kick @@ -674,17 +714,24 @@ def parse_args(self, ctx, args): and parses the arguments, then modifies the context as necessary. This is automatically invoked by :meth:`make_context`. """ - raise NotImplementedError('Base commands do not know how to parse ' - 'arguments.') + raise NotImplementedError( + "Base commands do not know how to parse arguments." + ) def invoke(self, ctx): """Given a context, this invokes the command. The default implementation is raising a not implemented error. """ - raise NotImplementedError('Base commands are not invokable by default') - - def main(self, args=None, prog_name=None, complete_var=None, - standalone_mode=True, **extra): + raise NotImplementedError("Base commands are not invokable by default") + + def main( + self, + args=None, + prog_name=None, + complete_var=None, + standalone_mode=True, + **extra + ): """This is the way to invoke a script with all the bells and whistles as a command line application. This will always terminate the application after a call. If this is not wanted, ``SystemExit`` @@ -731,8 +778,7 @@ def main(self, args=None, prog_name=None, complete_var=None, args = list(args) if prog_name is None: - prog_name = make_str(os.path.basename( - sys.argv and sys.argv[0] or __file__)) + prog_name = make_str(os.path.basename(sys.argv and sys.argv[0] or __file__)) # Hook for the Bash completion. This only activates if the Bash # completion is actually enabled, otherwise this is quite a fast @@ -784,7 +830,7 @@ def main(self, args=None, prog_name=None, complete_var=None, except Abort: if not standalone_mode: raise - echo('Aborted!', file=sys.stderr) + echo("Aborted!", file=sys.stderr) sys.exit(1) def __call__(self, *args, **kwargs): @@ -825,10 +871,21 @@ class Command(BaseCommand): the command is deprecated. """ - def __init__(self, name, context_settings=None, callback=None, - params=None, help=None, epilog=None, short_help=None, - options_metavar='[OPTIONS]', add_help_option=True, - no_args_is_help=False, hidden=False, deprecated=False): + def __init__( + self, + name, + context_settings=None, + callback=None, + params=None, + help=None, + epilog=None, + short_help=None, + options_metavar="[OPTIONS]", + add_help_option=True, + no_args_is_help=False, + hidden=False, + deprecated=False, + ): BaseCommand.__init__(self, name, context_settings) #: the callback to execute when the command fires. This might be #: `None` in which case nothing happens. @@ -839,8 +896,8 @@ def __init__(self, name, context_settings=None, callback=None, self.params = params or [] # if a form feed (page break) is found in the help text, truncate help # text to the content preceding the first form feed - if help and '\f' in help: - help = help.split('\f', 1)[0] + if help and "\f" in help: + help = help.split("\f", 1)[0] self.help = help self.epilog = epilog self.options_metavar = options_metavar @@ -857,7 +914,7 @@ def get_usage(self, ctx): """ formatter = ctx.make_formatter() self.format_usage(ctx, formatter) - return formatter.getvalue().rstrip('\n') + return formatter.getvalue().rstrip("\n") def get_params(self, ctx): rv = self.params @@ -872,7 +929,7 @@ def format_usage(self, ctx, formatter): This is a low-level method called by :meth:`get_usage`. """ pieces = self.collect_usage_pieces(ctx) - formatter.write_usage(ctx.command_path, ' '.join(pieces)) + formatter.write_usage(ctx.command_path, " ".join(pieces)) def collect_usage_pieces(self, ctx): """Returns all the pieces that go into the usage line and returns @@ -901,10 +958,15 @@ def show_help(ctx, param, value): if value and not ctx.resilient_parsing: echo(ctx.get_help(), color=ctx.color) ctx.exit() - return Option(help_options, is_flag=True, - is_eager=True, expose_value=False, - callback=show_help, - help='Show this message and exit.') + + return Option( + help_options, + is_flag=True, + is_eager=True, + expose_value=False, + callback=show_help, + help="Show this message and exit.", + ) def make_parser(self, ctx): """Creates the underlying option parser for this command.""" @@ -920,11 +982,16 @@ def get_help(self, ctx): """ formatter = ctx.make_formatter() self.format_help(ctx, formatter) - return formatter.getvalue().rstrip('\n') + return formatter.getvalue().rstrip("\n") def get_short_help_str(self, limit=45): """Gets short help for the command or makes it by shortening the long help string.""" - return self.short_help or self.help and make_default_short_help(self.help, limit) or '' + return ( + self.short_help + or self.help + and make_default_short_help(self.help, limit) + or "" + ) def format_help(self, ctx, formatter): """Writes the help into the formatter if it exists. @@ -966,7 +1033,7 @@ def format_options(self, ctx, formatter): opts.append(rv) if opts: - with formatter.section('Options'): + with formatter.section("Options"): formatter.write_dl(opts) def format_epilog(self, ctx, formatter): @@ -984,14 +1051,14 @@ def parse_args(self, ctx, args): parser = self.make_parser(ctx) opts, args, param_order = parser.parse_args(args=args) - for param in iter_params_for_processing( - param_order, self.get_params(ctx)): + for param in iter_params_for_processing(param_order, self.get_params(ctx)): value, args = param.handle_parse_result(ctx, opts, args) if args and not ctx.allow_extra_args and not ctx.resilient_parsing: - ctx.fail('Got unexpected extra argument%s (%s)' - % (len(args) != 1 and 's' or '', - ' '.join(map(make_str, args)))) + ctx.fail( + "Got unexpected extra argument%s (%s)" + % (len(args) != 1 and "s" or "", " ".join(map(make_str, args))) + ) ctx.args = args return args @@ -1028,12 +1095,20 @@ class MultiCommand(Command): :param result_callback: the result callback to attach to this multi command. """ + allow_extra_args = True allow_interspersed_args = False - def __init__(self, name=None, invoke_without_command=False, - no_args_is_help=None, subcommand_metavar=None, - chain=False, result_callback=None, **attrs): + def __init__( + self, + name=None, + invoke_without_command=False, + no_args_is_help=None, + subcommand_metavar=None, + chain=False, + result_callback=None, + **attrs + ): Command.__init__(self, name, **attrs) if no_args_is_help is None: no_args_is_help = not invoke_without_command @@ -1053,8 +1128,10 @@ def __init__(self, name=None, invoke_without_command=False, if self.chain: for param in self.params: if isinstance(param, Argument) and not param.required: - raise RuntimeError('Multi commands in chain mode cannot ' - 'have optional arguments.') + raise RuntimeError( + "Multi commands in chain mode cannot " + "have optional arguments." + ) def collect_usage_pieces(self, ctx): rv = Command.collect_usage_pieces(self, ctx) @@ -1090,16 +1167,19 @@ def process_result(result, input): :param replace: if set to `True` an already existing result callback will be removed. """ + def decorator(f): old_callback = self.result_callback if old_callback is None or replace: self.result_callback = f return f + def function(__value, *args, **kwargs): - return f(old_callback(__value, *args, **kwargs), - *args, **kwargs) + return f(old_callback(__value, *args, **kwargs), *args, **kwargs) + self.result_callback = rv = update_wrapper(function, f) return rv + return decorator def format_commands(self, ctx, formatter): @@ -1127,7 +1207,7 @@ def format_commands(self, ctx, formatter): rows.append((subcommand, help)) if rows: - with formatter.section('Commands'): + with formatter.section("Commands"): formatter.write_dl(rows) def parse_args(self, ctx, args): @@ -1147,8 +1227,7 @@ def parse_args(self, ctx, args): def invoke(self, ctx): def _process_result(value): if self.result_callback is not None: - value = ctx.invoke(self.result_callback, value, - **ctx.params) + value = ctx.invoke(self.result_callback, value, **ctx.params) return value if not ctx.protected_args: @@ -1164,7 +1243,7 @@ def _process_result(value): with ctx: Command.invoke(self, ctx) return _process_result([]) - ctx.fail('Missing command.') + ctx.fail("Missing command.") # Fetch args back out args = ctx.protected_args + ctx.args @@ -1191,7 +1270,7 @@ def _process_result(value): # set to ``*`` to inform the command that subcommands are executed # but nothing else. with ctx: - ctx.invoked_subcommand = args and '*' or None + ctx.invoked_subcommand = args and "*" or None Command.invoke(self, ctx) # Otherwise we make every single context and invoke them in a @@ -1200,9 +1279,13 @@ def _process_result(value): contexts = [] while args: cmd_name, cmd, args = self.resolve_command(ctx, args) - sub_ctx = cmd.make_context(cmd_name, args, parent=ctx, - allow_extra_args=True, - allow_interspersed_args=False) + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) contexts.append(sub_ctx) args, sub_ctx.args = sub_ctx.args, [] @@ -1269,7 +1352,7 @@ def add_command(self, cmd, name=None): """ name = name or cmd.name if name is None: - raise TypeError('Command has no name.') + raise TypeError("Command has no name.") _check_multicommand(self, name, cmd, register=True) self.commands[name] = cmd @@ -1279,10 +1362,12 @@ def command(self, *args, **kwargs): immediately registers the created command with this instance by calling into :meth:`add_command`. """ + def decorator(f): cmd = command(*args, **kwargs)(f) self.add_command(cmd) return cmd + return decorator def group(self, *args, **kwargs): @@ -1291,10 +1376,12 @@ def group(self, *args, **kwargs): immediately registers the created command with this instance by calling into :meth:`add_command`. """ + def decorator(f): cmd = group(*args, **kwargs)(f) self.add_command(cmd) return cmd + return decorator def get_command(self, ctx, cmd_name): @@ -1381,14 +1468,25 @@ class Parameter(object): parameter. The old callback format will still work, but it will raise a warning to give you a chance to migrate the code easier. """ - param_type_name = 'parameter' - - def __init__(self, param_decls=None, type=None, required=False, - default=None, callback=None, nargs=None, metavar=None, - expose_value=True, is_eager=False, envvar=None, - autocompletion=None): - self.name, self.opts, self.secondary_opts = \ - self._parse_decls(param_decls or (), expose_value) + param_type_name = "parameter" + + def __init__( + self, + param_decls=None, + type=None, + required=False, + default=None, + callback=None, + nargs=None, + metavar=None, + expose_value=True, + is_eager=False, + envvar=None, + autocompletion=None, + ): + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) self.type = convert_type(type, default) @@ -1428,7 +1526,7 @@ def make_metavar(self): if metavar is None: metavar = self.type.name.upper() if self.nargs != 1: - metavar += '...' + metavar += "..." return metavar def get_default(self, ctx): @@ -1458,10 +1556,12 @@ def type_cast_value(self, ctx, value): """ if self.type.is_composite: if self.nargs <= 1: - raise TypeError('Attempted to invoke composite type ' - 'but nargs has been set to %s. This is ' - 'not supported; nargs needs to be set to ' - 'a fixed value > 1.' % self.nargs) + raise TypeError( + "Attempted to invoke composite type " + "but nargs has been set to %s. This is " + "not supported; nargs needs to be set to " + "a fixed value > 1." % self.nargs + ) if self.multiple: return tuple(self.type(x or (), self, ctx) for x in value or ()) return self.type(value or (), self, ctx) @@ -1470,6 +1570,7 @@ def _convert(value, level): if level == 0: return self.type(value, self, ctx) return tuple(_convert(x, level - 1) for x in value or ()) + return _convert(value, (self.nargs != 1) + bool(self.multiple)) def process_value(self, ctx, value): @@ -1532,8 +1633,7 @@ def handle_parse_result(self, ctx, opts, args): value = None if self.callback is not None: try: - value = invoke_param_callback( - self.callback, ctx, self, value) + value = invoke_param_callback(self.callback, ctx, self, value) except Exception: if not ctx.resilient_parsing: raise @@ -1553,7 +1653,7 @@ def get_error_hint(self, ctx): indicate which param caused the error. """ hint_list = self.opts or [self.human_readable_name] - return ' / '.join('"%s"' % x for x in hint_list) + return " / ".join('"%s"' % x for x in hint_list) class Option(Parameter): @@ -1594,19 +1694,33 @@ class Option(Parameter): :param help: the help string. :param hidden: hide this option from help outputs. """ - param_type_name = 'option' - - def __init__(self, param_decls=None, show_default=False, - prompt=False, confirmation_prompt=False, - hide_input=False, is_flag=None, flag_value=None, - multiple=False, count=False, allow_from_autoenv=True, - type=None, help=None, hidden=False, show_choices=True, - show_envvar=False, **attrs): - default_is_missing = attrs.get('default', _missing) is _missing + + param_type_name = "option" + + def __init__( + self, + param_decls=None, + show_default=False, + prompt=False, + confirmation_prompt=False, + hide_input=False, + is_flag=None, + flag_value=None, + multiple=False, + count=False, + allow_from_autoenv=True, + type=None, + help=None, + hidden=False, + show_choices=True, + show_envvar=False, + **attrs + ): + default_is_missing = attrs.get("default", _missing) is _missing Parameter.__init__(self, param_decls, type=type, **attrs) if prompt is True: - prompt_text = self.name.replace('_', ' ').capitalize() + prompt_text = self.name.replace("_", " ").capitalize() elif prompt is False: prompt_text = None else: @@ -1628,8 +1742,7 @@ def __init__(self, param_decls=None, show_default=False, flag_value = not self.default self.is_flag = is_flag self.flag_value = flag_value - if self.is_flag and isinstance(self.flag_value, bool) \ - and type in [None, bool]: + if self.is_flag and isinstance(self.flag_value, bool) and type in [None, bool]: self.type = BOOL self.is_bool_flag = True else: @@ -1653,22 +1766,24 @@ def __init__(self, param_decls=None, show_default=False, # Sanity check for stuff we don't support if __debug__: if self.nargs < 0: - raise TypeError('Options cannot have nargs < 0') + raise TypeError("Options cannot have nargs < 0") if self.prompt and self.is_flag and not self.is_bool_flag: - raise TypeError('Cannot prompt for flags that are not bools.') + raise TypeError("Cannot prompt for flags that are not bools.") if not self.is_bool_flag and self.secondary_opts: - raise TypeError('Got secondary option for non boolean flag.') - if self.is_bool_flag and self.hide_input \ - and self.prompt is not None: - raise TypeError('Hidden input does not work with boolean ' - 'flag prompts.') + raise TypeError("Got secondary option for non boolean flag.") + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "Hidden input does not work with boolean flag prompts." + ) if self.count: if self.multiple: - raise TypeError('Options cannot be multiple and count ' - 'at the same time.') + raise TypeError( + "Options cannot be multiple and count at the same time." + ) elif self.is_flag: - raise TypeError('Options cannot be count and flags at ' - 'the same time.') + raise TypeError( + "Options cannot be count and flags at the same time." + ) def _parse_decls(self, decls, expose_value): opts = [] @@ -1679,10 +1794,10 @@ def _parse_decls(self, decls, expose_value): for decl in decls: if isidentifier(decl): if name is not None: - raise TypeError('Name defined twice') + raise TypeError("Name defined twice") name = decl else: - split_char = decl[:1] == '/' and ';' or '/' + split_char = decl[:1] == "/" and ";" or "/" if split_char in decl: first, second = decl.split(split_char, 1) first = first.rstrip() @@ -1698,49 +1813,53 @@ def _parse_decls(self, decls, expose_value): if name is None and possible_names: possible_names.sort(key=lambda x: -len(x[0])) # group long options first - name = possible_names[0][1].replace('-', '_').lower() + name = possible_names[0][1].replace("-", "_").lower() if not isidentifier(name): name = None if name is None: if not expose_value: return None, opts, secondary_opts - raise TypeError('Could not determine name for option') + raise TypeError("Could not determine name for option") if not opts and not secondary_opts: - raise TypeError('No options defined but a name was passed (%s). ' - 'Did you mean to declare an argument instead ' - 'of an option?' % name) + raise TypeError( + "No options defined but a name was passed (%s). " + "Did you mean to declare an argument instead " + "of an option?" % name + ) return name, opts, secondary_opts def add_to_parser(self, parser, ctx): kwargs = { - 'dest': self.name, - 'nargs': self.nargs, - 'obj': self, + "dest": self.name, + "nargs": self.nargs, + "obj": self, } if self.multiple: - action = 'append' + action = "append" elif self.count: - action = 'count' + action = "count" else: - action = 'store' + action = "store" if self.is_flag: - kwargs.pop('nargs', None) + kwargs.pop("nargs", None) if self.is_bool_flag and self.secondary_opts: - parser.add_option(self.opts, action=action + '_const', - const=True, **kwargs) - parser.add_option(self.secondary_opts, action=action + - '_const', const=False, **kwargs) + parser.add_option( + self.opts, action=action + "_const", const=True, **kwargs + ) + parser.add_option( + self.secondary_opts, action=action + "_const", const=False, **kwargs + ) else: - parser.add_option(self.opts, action=action + '_const', - const=self.flag_value, - **kwargs) + parser.add_option( + self.opts, action=action + "_const", const=self.flag_value, **kwargs + ) else: - kwargs['action'] = action + kwargs["action"] = action parser.add_option(self.opts, **kwargs) def get_help_record(self, ctx): @@ -1753,44 +1872,46 @@ def _write_opts(opts): if any_slashes: any_prefix_is_slash[:] = [True] if not self.is_flag and not self.count: - rv += ' ' + self.make_metavar() + rv += " " + self.make_metavar() return rv rv = [_write_opts(self.opts)] if self.secondary_opts: rv.append(_write_opts(self.secondary_opts)) - help = self.help or '' + help = self.help or "" extra = [] if self.show_envvar: envvar = self.envvar if envvar is None: - if self.allow_from_autoenv and \ - ctx.auto_envvar_prefix is not None: - envvar = '%s_%s' % (ctx.auto_envvar_prefix, self.name.upper()) + if self.allow_from_autoenv and ctx.auto_envvar_prefix is not None: + envvar = "%s_%s" % (ctx.auto_envvar_prefix, self.name.upper()) if envvar is not None: - extra.append('env var: %s' % ( - ', '.join('%s' % d for d in envvar) - if isinstance(envvar, (list, tuple)) - else envvar, )) - if self.default is not None and \ - (self.show_default or ctx.show_default): + extra.append( + "env var: %s" + % ( + ", ".join("%s" % d for d in envvar) + if isinstance(envvar, (list, tuple)) + else envvar, + ) + ) + if self.default is not None and (self.show_default or ctx.show_default): if isinstance(self.show_default, string_types): - default_string = '({})'.format(self.show_default) + default_string = "({})".format(self.show_default) elif isinstance(self.default, (list, tuple)): - default_string = ', '.join('%s' % d for d in self.default) + default_string = ", ".join("%s" % d for d in self.default) elif inspect.isfunction(self.default): default_string = "(dynamic)" else: default_string = self.default - extra.append('default: {}'.format(default_string)) + extra.append("default: {}".format(default_string)) if self.required: - extra.append('required') + extra.append("required") if extra: - help = '%s[%s]' % (help and help + ' ' or '', '; '.join(extra)) + help = "%s[%s]" % (help and help + " " or "", "; ".join(extra)) - return ((any_prefix_is_slash and '; ' or ' / ').join(rv), help) + return ((any_prefix_is_slash and "; " or " / ").join(rv), help) def get_default(self, ctx): # If we're a non boolean flag our default is more complex because @@ -1818,18 +1939,22 @@ def prompt_for_value(self, ctx): if self.is_bool_flag: return confirm(self.prompt, default) - return prompt(self.prompt, default=default, type=self.type, - hide_input=self.hide_input, show_choices=self.show_choices, - confirmation_prompt=self.confirmation_prompt, - value_proc=lambda x: self.process_value(ctx, x)) + return prompt( + self.prompt, + default=default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + ) def resolve_envvar_value(self, ctx): rv = Parameter.resolve_envvar_value(self, ctx) if rv is not None: return rv - if self.allow_from_autoenv and \ - ctx.auto_envvar_prefix is not None: - envvar = '%s_%s' % (ctx.auto_envvar_prefix, self.name.upper()) + if self.allow_from_autoenv and ctx.auto_envvar_prefix is not None: + envvar = "%s_%s" % (ctx.auto_envvar_prefix, self.name.upper()) return os.environ.get(envvar) def value_from_envvar(self, ctx): @@ -1844,8 +1969,7 @@ def value_from_envvar(self, ctx): return rv def full_process_value(self, ctx, value): - if value is None and self.prompt is not None \ - and not ctx.resilient_parsing: + if value is None and self.prompt is not None and not ctx.resilient_parsing: return self.prompt_for_value(ctx) return Parameter.full_process_value(self, ctx, value) @@ -1857,18 +1981,20 @@ class Argument(Parameter): All parameters are passed onwards to the parameter constructor. """ - param_type_name = 'argument' + + param_type_name = "argument" def __init__(self, param_decls, required=None, **attrs): if required is None: - if attrs.get('default') is not None: + if attrs.get("default") is not None: required = False else: - required = attrs.get('nargs', 1) > 0 + required = attrs.get("nargs", 1) > 0 Parameter.__init__(self, param_decls, required=required, **attrs) if self.default is not None and self.nargs < 0: - raise TypeError('nargs=-1 in combination with a default value ' - 'is not supported.') + raise TypeError( + "nargs=-1 in combination with a default value is not supported." + ) @property def human_readable_name(self): @@ -1883,22 +2009,24 @@ def make_metavar(self): if not var: var = self.name.upper() if not self.required: - var = '[%s]' % var + var = "[%s]" % var if self.nargs != 1: - var += '...' + var += "..." return var def _parse_decls(self, decls, expose_value): if not decls: if not expose_value: return None, [], [] - raise TypeError('Could not determine name for argument') + raise TypeError("Could not determine name for argument") if len(decls) == 1: name = arg = decls[0] - name = name.replace('-', '_').lower() + name = name.replace("-", "_").lower() else: - raise TypeError('Arguments take exactly one ' - 'parameter declaration, got %d' % len(decls)) + raise TypeError( + "Arguments take exactly one " + "parameter declaration, got %d" % len(decls) + ) return name, [arg], [] def get_usage_pieces(self, ctx): @@ -1908,8 +2036,7 @@ def get_error_hint(self, ctx): return '"%s"' % self.make_metavar() def add_to_parser(self, parser, ctx): - parser.add_argument(dest=self.name, nargs=self.nargs, - obj=self) + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) # Circular dependency between decorators and core diff --git a/src/click/decorators.py b/src/click/decorators.py index bb5a8bc36..5f556e2c9 100644 --- a/src/click/decorators.py +++ b/src/click/decorators.py @@ -12,8 +12,10 @@ def pass_context(f): """Marks a callback as wanting to receive the current context object as first argument. """ + def new_func(*args, **kwargs): return f(get_current_context(), *args, **kwargs) + return update_wrapper(new_func, f) @@ -22,8 +24,10 @@ def pass_obj(f): context onwards (:attr:`Context.obj`). This is useful if that object represents the state of a nested system. """ + def new_func(*args, **kwargs): return f(get_current_context().obj, *args, **kwargs) + return update_wrapper(new_func, f) @@ -49,6 +53,7 @@ def new_func(ctx, *args, **kwargs): :param ensure: if set to `True`, a new object will be created and remembered on the context if it's not there yet. """ + def decorator(f): def new_func(*args, **kwargs): ctx = get_current_context() @@ -57,35 +62,41 @@ def new_func(*args, **kwargs): else: obj = ctx.find_object(object_type) if obj is None: - raise RuntimeError('Managed to invoke callback without a ' - 'context object of type %r existing' - % object_type.__name__) + raise RuntimeError( + "Managed to invoke callback without a " + "context object of type %r existing" % object_type.__name__ + ) return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator def _make_command(f, name, attrs, cls): if isinstance(f, Command): - raise TypeError('Attempted to convert a callback into a ' - 'command twice.') + raise TypeError("Attempted to convert a callback into a command twice.") try: params = f.__click_params__ params.reverse() del f.__click_params__ except AttributeError: params = [] - help = attrs.get('help') + help = attrs.get("help") if help is None: help = inspect.getdoc(f) if isinstance(help, bytes): - help = help.decode('utf-8') + help = help.decode("utf-8") else: help = inspect.cleandoc(help) - attrs['help'] = help + attrs["help"] = help _check_for_unicode_literals() - return cls(name=name or f.__name__.lower().replace('_', '-'), - callback=f, params=params, **attrs) + return cls( + name=name or f.__name__.lower().replace("_", "-"), + callback=f, + params=params, + **attrs + ) def command(name=None, cls=None, **attrs): @@ -110,10 +121,12 @@ def command(name=None, cls=None, **attrs): """ if cls is None: cls = Command + def decorator(f): cmd = _make_command(f, name, attrs, cls) cmd.__doc__ = f.__doc__ return cmd + return decorator @@ -122,7 +135,7 @@ def group(name=None, **attrs): works otherwise the same as :func:`command` just that the `cls` parameter is set to :class:`Group`. """ - attrs.setdefault('cls', Group) + attrs.setdefault("cls", Group) return command(name, **attrs) @@ -130,7 +143,7 @@ def _param_memo(f, param): if isinstance(f, Command): f.params.append(param) else: - if not hasattr(f, '__click_params__'): + if not hasattr(f, "__click_params__"): f.__click_params__ = [] f.__click_params__.append(param) @@ -145,10 +158,12 @@ def argument(*param_decls, **attrs): :param cls: the argument class to instantiate. This defaults to :class:`Argument`. """ + def decorator(f): - ArgumentClass = attrs.pop('cls', Argument) + ArgumentClass = attrs.pop("cls", Argument) _param_memo(f, ArgumentClass(param_decls, **attrs)) return f + return decorator @@ -162,15 +177,17 @@ def option(*param_decls, **attrs): :param cls: the option class to instantiate. This defaults to :class:`Option`. """ + def decorator(f): # Issue 926, copy attrs, so pre-defined options can re-use the same cls= option_attrs = attrs.copy() - if 'help' in option_attrs: - option_attrs['help'] = inspect.cleandoc(option_attrs['help']) - OptionClass = option_attrs.pop('cls', Option) + if "help" in option_attrs: + option_attrs["help"] = inspect.cleandoc(option_attrs["help"]) + OptionClass = option_attrs.pop("cls", Option) _param_memo(f, OptionClass(param_decls, **option_attrs)) return f + return decorator @@ -191,16 +208,19 @@ def callback(ctx, param, value): def dropdb(): pass """ + def decorator(f): def callback(ctx, param, value): if not value: ctx.abort() - attrs.setdefault('is_flag', True) - attrs.setdefault('callback', callback) - attrs.setdefault('expose_value', False) - attrs.setdefault('prompt', 'Do you want to continue?') - attrs.setdefault('help', 'Confirm the action without prompting.') - return option(*(param_decls or ('--yes',)), **attrs)(f) + + attrs.setdefault("is_flag", True) + attrs.setdefault("callback", callback) + attrs.setdefault("expose_value", False) + attrs.setdefault("prompt", "Do you want to continue?") + attrs.setdefault("help", "Confirm the action without prompting.") + return option(*(param_decls or ("--yes",)), **attrs)(f) + return decorator @@ -216,11 +236,13 @@ def password_option(*param_decls, **attrs): def changeadmin(password): pass """ + def decorator(f): - attrs.setdefault('prompt', True) - attrs.setdefault('confirmation_prompt', True) - attrs.setdefault('hide_input', True) - return option(*(param_decls or ('--password',)), **attrs)(f) + attrs.setdefault("prompt", True) + attrs.setdefault("confirmation_prompt", True) + attrs.setdefault("hide_input", True) + return option(*(param_decls or ("--password",)), **attrs)(f) + return decorator @@ -237,14 +259,14 @@ def version_option(version=None, *param_decls, **attrs): :param others: everything else is forwarded to :func:`option`. """ if version is None: - if hasattr(sys, '_getframe'): - module = sys._getframe(1).f_globals.get('__name__') + if hasattr(sys, "_getframe"): + module = sys._getframe(1).f_globals.get("__name__") else: - module = '' + module = "" def decorator(f): - prog_name = attrs.pop('prog_name', None) - message = attrs.pop('message', '%(prog)s, version %(version)s') + prog_name = attrs.pop("prog_name", None) + message = attrs.pop("message", "%(prog)s, version %(version)s") def callback(ctx, param, value): if not value or ctx.resilient_parsing: @@ -260,25 +282,23 @@ def callback(ctx, param, value): pass else: for dist in pkg_resources.working_set: - scripts = dist.get_entry_map().get('console_scripts') or {} + scripts = dist.get_entry_map().get("console_scripts") or {} for script_name, entry_point in iteritems(scripts): if entry_point.module_name == module: ver = dist.version break if ver is None: - raise RuntimeError('Could not determine version') - echo(message % { - 'prog': prog, - 'version': ver, - }, color=ctx.color) + raise RuntimeError("Could not determine version") + echo(message % {"prog": prog, "version": ver,}, color=ctx.color) ctx.exit() - attrs.setdefault('is_flag', True) - attrs.setdefault('expose_value', False) - attrs.setdefault('is_eager', True) - attrs.setdefault('help', 'Show the version and exit.') - attrs['callback'] = callback - return option(*(param_decls or ('--version',)), **attrs)(f) + attrs.setdefault("is_flag", True) + attrs.setdefault("expose_value", False) + attrs.setdefault("is_eager", True) + attrs.setdefault("help", "Show the version and exit.") + attrs["callback"] = callback + return option(*(param_decls or ("--version",)), **attrs)(f) + return decorator @@ -292,17 +312,20 @@ def help_option(*param_decls, **attrs): All arguments are forwarded to :func:`option`. """ + def decorator(f): def callback(ctx, param, value): if value and not ctx.resilient_parsing: echo(ctx.get_help(), color=ctx.color) ctx.exit() - attrs.setdefault('is_flag', True) - attrs.setdefault('expose_value', False) - attrs.setdefault('help', 'Show this message and exit.') - attrs.setdefault('is_eager', True) - attrs['callback'] = callback - return option(*(param_decls or ('--help',)), **attrs)(f) + + attrs.setdefault("is_flag", True) + attrs.setdefault("expose_value", False) + attrs.setdefault("help", "Show this message and exit.") + attrs.setdefault("is_eager", True) + attrs["callback"] = callback + return option(*(param_decls or ("--help",)), **attrs)(f) + return decorator diff --git a/src/click/exceptions.py b/src/click/exceptions.py index 4aea4f864..a06400b13 100644 --- a/src/click/exceptions.py +++ b/src/click/exceptions.py @@ -6,7 +6,7 @@ def _join_param_hints(param_hint): if isinstance(param_hint, (tuple, list)): - return ' / '.join('"%s"' % x for x in param_hint) + return " / ".join('"%s"' % x for x in param_hint) return param_hint @@ -20,7 +20,7 @@ def __init__(self, message): ctor_msg = message if PY2: if ctor_msg is not None: - ctor_msg = ctor_msg.encode('utf-8') + ctor_msg = ctor_msg.encode("utf-8") Exception.__init__(self, ctor_msg) self.message = message @@ -34,12 +34,12 @@ def __str__(self): __unicode__ = __str__ def __str__(self): - return self.message.encode('utf-8') + return self.message.encode("utf-8") def show(self, file=None): if file is None: file = get_text_stderr() - echo('Error: %s' % self.format_message(), file=file) + echo("Error: %s" % self.format_message(), file=file) class UsageError(ClickException): @@ -50,6 +50,7 @@ class UsageError(ClickException): :param ctx: optionally the context that caused this error. Click will fill in the context automatically in some situations. """ + exit_code = 2 def __init__(self, message, ctx=None): @@ -61,15 +62,16 @@ def show(self, file=None): if file is None: file = get_text_stderr() color = None - hint = '' - if (self.cmd is not None and - self.cmd.get_help_option(self.ctx) is not None): - hint = ('Try "%s %s" for help.\n' - % (self.ctx.command_path, self.ctx.help_option_names[0])) + hint = "" + if self.cmd is not None and self.cmd.get_help_option(self.ctx) is not None: + hint = 'Try "%s %s" for help.\n' % ( + self.ctx.command_path, + self.ctx.help_option_names[0], + ) if self.ctx is not None: color = self.ctx.color - echo(self.ctx.get_usage() + '\n%s' % hint, file=file, color=color) - echo('Error: %s' % self.format_message(), file=file, color=color) + echo(self.ctx.get_usage() + "\n%s" % hint, file=file, color=color) + echo("Error: %s" % self.format_message(), file=file, color=color) class BadParameter(UsageError): @@ -90,8 +92,7 @@ class BadParameter(UsageError): each item is quoted and separated. """ - def __init__(self, message, ctx=None, param=None, - param_hint=None): + def __init__(self, message, ctx=None, param=None, param_hint=None): UsageError.__init__(self, message, ctx) self.param = param self.param_hint = param_hint @@ -102,10 +103,10 @@ def format_message(self): elif self.param is not None: param_hint = self.param.get_error_hint(self.ctx) else: - return 'Invalid value: %s' % self.message + return "Invalid value: %s" % self.message param_hint = _join_param_hints(param_hint) - return 'Invalid value for %s: %s' % (param_hint, self.message) + return "Invalid value for %s: %s" % (param_hint, self.message) class MissingParameter(BadParameter): @@ -120,8 +121,9 @@ class MissingParameter(BadParameter): ``'option'`` or ``'argument'``. """ - def __init__(self, message=None, ctx=None, param=None, - param_hint=None, param_type=None): + def __init__( + self, message=None, ctx=None, param=None, param_hint=None, param_type=None + ): BadParameter.__init__(self, message, ctx, param, param_hint) self.param_type = param_type @@ -143,15 +145,15 @@ def format_message(self): msg_extra = self.param.type.get_missing_message(self.param) if msg_extra: if msg: - msg += '. ' + msg_extra + msg += ". " + msg_extra else: msg = msg_extra - return 'Missing %s%s%s%s' % ( + return "Missing %s%s%s%s" % ( param_type, - param_hint and ' %s' % param_hint or '', - msg and '. ' or '.', - msg or '', + param_hint and " %s" % param_hint or "", + msg and ". " or ".", + msg or "", ) def __str__(self): @@ -175,10 +177,9 @@ class NoSuchOption(UsageError): .. versionadded:: 4.0 """ - def __init__(self, option_name, message=None, possibilities=None, - ctx=None): + def __init__(self, option_name, message=None, possibilities=None, ctx=None): if message is None: - message = 'no such option: %s' % option_name + message = "no such option: %s" % option_name UsageError.__init__(self, message, ctx) self.option_name = option_name self.possibilities = possibilities @@ -187,11 +188,11 @@ def format_message(self): bits = [self.message] if self.possibilities: if len(self.possibilities) == 1: - bits.append('Did you mean %s?' % self.possibilities[0]) + bits.append("Did you mean %s?" % self.possibilities[0]) else: possibilities = sorted(self.possibilities) - bits.append('(Possible options: %s)' % ', '.join(possibilities)) - return ' '.join(bits) + bits.append("(Possible options: %s)" % ", ".join(possibilities)) + return " ".join(bits) class BadOptionUsage(UsageError): @@ -227,13 +228,13 @@ class FileError(ClickException): def __init__(self, filename, hint=None): ui_filename = filename_to_ui(filename) if hint is None: - hint = 'unknown error' + hint = "unknown error" ClickException.__init__(self, hint) self.ui_filename = ui_filename self.filename = filename def format_message(self): - return 'Could not open file %s: %s' % (self.ui_filename, self.message) + return "Could not open file %s: %s" % (self.ui_filename, self.message) class Abort(RuntimeError): @@ -246,5 +247,6 @@ class Exit(RuntimeError): :param code: the status code to exit with. """ + def __init__(self, code=0): self.exit_code = code diff --git a/src/click/formatting.py b/src/click/formatting.py index 4426d9cfe..de90d6bde 100644 --- a/src/click/formatting.py +++ b/src/click/formatting.py @@ -19,11 +19,12 @@ def measure_table(rows): def iter_rows(rows, col_count): for row in rows: row = tuple(row) - yield row + ('',) * (col_count - len(row)) + yield row + ("",) * (col_count - len(row)) -def wrap_text(text, width=78, initial_indent='', subsequent_indent='', - preserve_paragraphs=False): +def wrap_text( + text, width=78, initial_indent="", subsequent_indent="", preserve_paragraphs=False +): """A helper function that intelligently wraps text. By default, it assumes that it operates on a single paragraph of text but if the `preserve_paragraphs` parameter is provided it will intelligently @@ -43,10 +44,14 @@ def wrap_text(text, width=78, initial_indent='', subsequent_indent='', intelligently handle paragraphs. """ from ._textwrap import TextWrapper + text = text.expandtabs() - wrapper = TextWrapper(width, initial_indent=initial_indent, - subsequent_indent=subsequent_indent, - replace_whitespace=False) + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) if not preserve_paragraphs: return wrapper.fill(text) @@ -57,10 +62,10 @@ def wrap_text(text, width=78, initial_indent='', subsequent_indent='', def _flush_par(): if not buf: return - if buf[0].strip() == '\b': - p.append((indent or 0, True, '\n'.join(buf[1:]))) + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) else: - p.append((indent or 0, False, ' '.join(buf))) + p.append((indent or 0, False, " ".join(buf))) del buf[:] for line in text.splitlines(): @@ -77,13 +82,13 @@ def _flush_par(): rv = [] for indent, raw, text in p: - with wrapper.extra_indent(' ' * indent): + with wrapper.extra_indent(" " * indent): if raw: rv.append(wrapper.indent_only(text)) else: rv.append(wrapper.fill(text)) - return '\n\n'.join(rv) + return "\n\n".join(rv) class HelpFormatter(object): @@ -122,53 +127,65 @@ def dedent(self): """Decreases the indentation.""" self.current_indent -= self.indent_increment - def write_usage(self, prog, args='', prefix='Usage: '): + def write_usage(self, prog, args="", prefix="Usage: "): """Writes a usage line into the buffer. :param prog: the program name. :param args: whitespace separated list of arguments. :param prefix: the prefix for the first line. """ - usage_prefix = '%*s%s ' % (self.current_indent, prefix, prog) + usage_prefix = "%*s%s " % (self.current_indent, prefix, prog) text_width = self.width - self.current_indent if text_width >= (term_len(usage_prefix) + 20): # The arguments will fit to the right of the prefix. - indent = ' ' * term_len(usage_prefix) - self.write(wrap_text(args, text_width, - initial_indent=usage_prefix, - subsequent_indent=indent)) + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) else: # The prefix is too long, put the arguments on the next line. self.write(usage_prefix) - self.write('\n') - indent = ' ' * (max(self.current_indent, term_len(prefix)) + 4) - self.write(wrap_text(args, text_width, - initial_indent=indent, - subsequent_indent=indent)) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) - self.write('\n') + self.write("\n") def write_heading(self, heading): """Writes a heading into the buffer.""" - self.write('%*s%s:\n' % (self.current_indent, '', heading)) + self.write("%*s%s:\n" % (self.current_indent, "", heading)) def write_paragraph(self): """Writes a paragraph into the buffer.""" if self.buffer: - self.write('\n') + self.write("\n") def write_text(self, text): """Writes re-indented text into the buffer. This rewraps and preserves paragraphs. """ text_width = max(self.width - self.current_indent, 11) - indent = ' ' * self.current_indent - self.write(wrap_text(text, text_width, - initial_indent=indent, - subsequent_indent=indent, - preserve_paragraphs=True)) - self.write('\n') + indent = " " * self.current_indent + self.write( + wrap_text( + text, + text_width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") def write_dl(self, rows, col_max=30, col_spacing=2): """Writes a definition list into the buffer. This is how options @@ -182,36 +199,36 @@ def write_dl(self, rows, col_max=30, col_spacing=2): rows = list(rows) widths = measure_table(rows) if len(widths) != 2: - raise TypeError('Expected two columns for definition list') + raise TypeError("Expected two columns for definition list") first_col = min(widths[0], col_max) + col_spacing for first, second in iter_rows(rows, len(widths)): - self.write('%*s%s' % (self.current_indent, '', first)) + self.write("%*s%s" % (self.current_indent, "", first)) if not second: - self.write('\n') + self.write("\n") continue if term_len(first) <= first_col - col_spacing: - self.write(' ' * (first_col - term_len(first))) + self.write(" " * (first_col - term_len(first))) else: - self.write('\n') - self.write(' ' * (first_col + self.current_indent)) + self.write("\n") + self.write(" " * (first_col + self.current_indent)) text_width = max(self.width - first_col - 2, 10) wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) lines = wrapped_text.splitlines() if lines: - self.write(lines[0] + '\n') + self.write(lines[0] + "\n") for line in lines[1:]: - self.write('%*s%s\n' % (first_col + self.current_indent, '', line)) + self.write("%*s%s\n" % (first_col + self.current_indent, "", line)) if len(lines) > 1: # separate long help from next option self.write("\n") else: - self.write('\n') + self.write("\n") @contextmanager def section(self, name): @@ -239,7 +256,7 @@ def indentation(self): def getvalue(self): """Returns the buffer contents.""" - return ''.join(self.buffer) + return "".join(self.buffer) def join_options(options): @@ -252,11 +269,11 @@ def join_options(options): any_prefix_is_slash = False for opt in options: prefix = split_opt(opt)[0] - if prefix == '/': + if prefix == "/": any_prefix_is_slash = True rv.append((len(prefix), opt)) rv.sort(key=lambda x: x[0]) - rv = ', '.join(x[1] for x in rv) + rv = ", ".join(x[1] for x in rv) return rv, any_prefix_is_slash diff --git a/src/click/globals.py b/src/click/globals.py index e5b88d3c9..a0157fce5 100644 --- a/src/click/globals.py +++ b/src/click/globals.py @@ -19,15 +19,15 @@ def get_current_context(silent=False): :exc:`RuntimeError`. """ try: - return getattr(_local, 'stack')[-1] + return getattr(_local, "stack")[-1] except (AttributeError, IndexError): if not silent: - raise RuntimeError('There is no active click context.') + raise RuntimeError("There is no active click context.") def push_context(ctx): """Pushes a new context to the current stack.""" - _local.__dict__.setdefault('stack', []).append(ctx) + _local.__dict__.setdefault("stack", []).append(ctx) def pop_context(): diff --git a/src/click/parser.py b/src/click/parser.py index c060313f5..16a41eb33 100644 --- a/src/click/parser.py +++ b/src/click/parser.py @@ -64,7 +64,7 @@ def _fetch(c): rv.append(tuple(x)) elif nargs < 0: if spos is not None: - raise TypeError('Cannot have two nargs < 0') + raise TypeError("Cannot have two nargs < 0") spos = len(rv) rv.append(None) @@ -73,21 +73,21 @@ def _fetch(c): if spos is not None: rv[spos] = tuple(args) args = [] - rv[spos + 1:] = reversed(rv[spos + 1:]) + rv[spos + 1 :] = reversed(rv[spos + 1 :]) return tuple(rv), list(args) def _error_opt_args(nargs, opt): if nargs == 1: - raise BadOptionUsage(opt, '%s option requires an argument' % opt) - raise BadOptionUsage(opt, '%s option requires %d arguments' % (opt, nargs)) + raise BadOptionUsage(opt, "%s option requires an argument" % opt) + raise BadOptionUsage(opt, "%s option requires %d arguments" % (opt, nargs)) def split_opt(opt): first = opt[:1] if first.isalnum(): - return '', opt + return "", opt if opt[1:2] == first: return opt[:2], opt[2:] return first, opt[1:] @@ -103,13 +103,14 @@ def normalize_opt(opt, ctx): def split_arg_string(string): """Given an argument string this attempts to split it into small parts.""" rv = [] - for match in re.finditer(r"('([^'\\]*(?:\\.[^'\\]*)*)'" - r'|"([^"\\]*(?:\\.[^"\\]*)*)"' - r'|\S+)\s*', string, re.S): + for match in re.finditer( + r"('([^'\\]*(?:\\.[^'\\]*)*)'|\"([^\"\\]*(?:\\.[^\"\\]*)*)\"|\S+)\s*", + string, + re.S, + ): arg = match.group().strip() - if arg[:1] == arg[-1:] and arg[:1] in '"\'': - arg = arg[1:-1].encode('ascii', 'backslashreplace') \ - .decode('unicode-escape') + if arg[:1] == arg[-1:] and arg[:1] in "\"'": + arg = arg[1:-1].encode("ascii", "backslashreplace").decode("unicode-escape") try: arg = type(string)(arg) except UnicodeError: @@ -119,7 +120,6 @@ def split_arg_string(string): class Option(object): - def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None): self._short_opts = [] self._long_opts = [] @@ -128,8 +128,7 @@ def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None): for opt in opts: prefix, value = split_opt(opt) if not prefix: - raise ValueError('Invalid start character for option (%s)' - % opt) + raise ValueError("Invalid start character for option (%s)" % opt) self.prefixes.add(prefix[0]) if len(prefix) == 1 and len(value) == 1: self._short_opts.append(opt) @@ -138,7 +137,7 @@ def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None): self.prefixes.add(prefix) if action is None: - action = 'store' + action = "store" self.dest = dest self.action = action @@ -148,26 +147,25 @@ def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None): @property def takes_value(self): - return self.action in ('store', 'append') + return self.action in ("store", "append") def process(self, value, state): - if self.action == 'store': + if self.action == "store": state.opts[self.dest] = value - elif self.action == 'store_const': + elif self.action == "store_const": state.opts[self.dest] = self.const - elif self.action == 'append': + elif self.action == "append": state.opts.setdefault(self.dest, []).append(value) - elif self.action == 'append_const': + elif self.action == "append_const": state.opts.setdefault(self.dest, []).append(self.const) - elif self.action == 'count': + elif self.action == "count": state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 else: - raise ValueError('unknown action %r' % self.action) + raise ValueError("unknown action %r" % self.action) state.order.append(self.obj) class Argument(object): - def __init__(self, dest, nargs=1, obj=None): self.dest = dest self.nargs = nargs @@ -179,14 +177,14 @@ def process(self, value, state): if holes == len(value): value = None elif holes != 0: - raise BadArgumentUsage('argument %s takes %d values' - % (self.dest, self.nargs)) + raise BadArgumentUsage( + "argument %s takes %d values" % (self.dest, self.nargs) + ) state.opts[self.dest] = value state.order.append(self.obj) class ParsingState(object): - def __init__(self, rargs): self.opts = {} self.largs = [] @@ -227,11 +225,10 @@ def __init__(self, ctx=None): self.ignore_unknown_options = ctx.ignore_unknown_options self._short_opt = {} self._long_opt = {} - self._opt_prefixes = set(['-', '--']) + self._opt_prefixes = set(["-", "--"]) self._args = [] - def add_option(self, opts, dest, action=None, nargs=1, const=None, - obj=None): + def add_option(self, opts, dest, action=None, nargs=1, const=None, obj=None): """Adds a new option named `dest` to the parser. The destination is not inferred (unlike with optparse) and needs to be explicitly provided. Action can be any of ``store``, ``store_const``, @@ -243,8 +240,7 @@ def add_option(self, opts, dest, action=None, nargs=1, const=None, if obj is None: obj = dest opts = [normalize_opt(opt, self.ctx) for opt in opts] - option = Option(opts, dest, action=action, nargs=nargs, - const=const, obj=obj) + option = Option(opts, dest, action=action, nargs=nargs, const=const, obj=obj) self._opt_prefixes.update(option.prefixes) for opt in option._short_opts: self._short_opt[opt] = option @@ -278,8 +274,9 @@ def parse_args(self, args): return state.opts, state.largs, state.order def _process_args_for_args(self, state): - pargs, args = _unpack_args(state.largs + state.rargs, - [x.nargs for x in self._args]) + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) for idx, arg in enumerate(self._args): arg.process(pargs[idx], state) @@ -293,7 +290,7 @@ def _process_args_for_options(self, state): arglen = len(arg) # Double dashes always handled explicitly regardless of what # prefixes are valid. - if arg == '--': + if arg == "--": return elif arg[:1] in self._opt_prefixes and arglen > 1: self._process_opts(arg, state) @@ -325,8 +322,7 @@ def _process_args_for_options(self, state): def _match_long_opt(self, opt, explicit_value, state): if opt not in self._long_opt: - possibilities = [word for word in self._long_opt - if word.startswith(opt)] + possibilities = [word for word in self._long_opt if word.startswith(opt)] raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) option = self._long_opt[opt] @@ -348,7 +344,7 @@ def _match_long_opt(self, opt, explicit_value, state): del state.rargs[:nargs] elif explicit_value is not None: - raise BadOptionUsage(opt, '%s option does not take a value' % opt) + raise BadOptionUsage(opt, "%s option does not take a value" % opt) else: value = None @@ -400,15 +396,15 @@ def _match_short_opt(self, arg, state): # to the state as new larg. This way there is basic combinatorics # that can be achieved while still ignoring unknown arguments. if self.ignore_unknown_options and unknown_options: - state.largs.append(prefix + ''.join(unknown_options)) + state.largs.append(prefix + "".join(unknown_options)) def _process_opts(self, arg, state): explicit_value = None # Long option handling happens in two parts. The first part is # supporting explicitly attached values. In any case, we will try # to long match the option first. - if '=' in arg: - long_opt, explicit_value = arg.split('=', 1) + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) else: long_opt = arg norm_long_opt = normalize_opt(long_opt, self.ctx) diff --git a/src/click/termui.py b/src/click/termui.py index 5448b80d8..eb1baa819 100644 --- a/src/click/termui.py +++ b/src/click/termui.py @@ -27,38 +27,41 @@ visible_prompt_func = raw_input _ansi_colors = { - 'black': 30, - 'red': 31, - 'green': 32, - 'yellow': 33, - 'blue': 34, - 'magenta': 35, - 'cyan': 36, - 'white': 37, - 'reset': 39, - 'bright_black': 90, - 'bright_red': 91, - 'bright_green': 92, - 'bright_yellow': 93, - 'bright_blue': 94, - 'bright_magenta': 95, - 'bright_cyan': 96, - 'bright_white': 97, + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, } -_ansi_reset_all = '\033[0m' +_ansi_reset_all = "\033[0m" def hidden_prompt_func(prompt): import getpass + return getpass.getpass(prompt) -def _build_prompt(text, suffix, show_default=False, default=None, show_choices=True, type=None): +def _build_prompt( + text, suffix, show_default=False, default=None, show_choices=True, type=None +): prompt = text if type is not None and show_choices and isinstance(type, Choice): - prompt += ' (' + ", ".join(map(str, type.choices)) + ')' + prompt += " (" + ", ".join(map(str, type.choices)) + ")" if default is not None and show_default: - prompt = '%s [%s]' % (prompt, _format_default(default)) + prompt = "%s [%s]" % (prompt, _format_default(default)) return prompt + suffix @@ -69,9 +72,18 @@ def _format_default(default): return default -def prompt(text, default=None, hide_input=False, confirmation_prompt=False, - type=None, value_proc=None, prompt_suffix=': ', show_default=True, - err=False, show_choices=True): +def prompt( + text, + default=None, + hide_input=False, + confirmation_prompt=False, + type=None, + value_proc=None, + prompt_suffix=": ", + show_default=True, + err=False, + show_choices=True, +): """Prompts a user for input. This is a convenience function that can be used to prompt a user for input later. @@ -114,7 +126,7 @@ def prompt_func(text): # Write the prompt separately so that we get nice # coloring through colorama on Windows echo(text, nl=False, err=err) - return f('') + return f("") except (KeyboardInterrupt, EOFError): # getpass doesn't print a newline if the user aborts input with ^C. # Allegedly this behavior is inherited from getpass(3). @@ -126,7 +138,9 @@ def prompt_func(text): if value_proc is None: value_proc = convert_type(type, default) - prompt = _build_prompt(text, prompt_suffix, show_default, default, show_choices, type) + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) while 1: while 1: @@ -142,21 +156,22 @@ def prompt_func(text): try: result = value_proc(value) except UsageError as e: - echo('Error: %s' % e.message, err=err) + echo("Error: %s" % e.message, err=err) continue if not confirmation_prompt: return result while 1: - value2 = prompt_func('Repeat for confirmation: ') + value2 = prompt_func("Repeat for confirmation: ") if value2: break if value == value2: return result - echo('Error: the two entered values do not match', err=err) + echo("Error: the two entered values do not match", err=err) -def confirm(text, default=False, abort=False, prompt_suffix=': ', - show_default=True, err=False): +def confirm( + text, default=False, abort=False, prompt_suffix=": ", show_default=True, err=False +): """Prompts for confirmation (yes/no question). If the user aborts the input by sending a interrupt signal this @@ -174,24 +189,25 @@ def confirm(text, default=False, abort=False, prompt_suffix=': ', :param err: if set to true the file defaults to ``stderr`` instead of ``stdout``, the same as with echo. """ - prompt = _build_prompt(text, prompt_suffix, show_default, - default and 'Y/n' or 'y/N') + prompt = _build_prompt( + text, prompt_suffix, show_default, default and "Y/n" or "y/N" + ) while 1: try: # Write the prompt separately so that we get nice # coloring through colorama on Windows echo(prompt, nl=False, err=err) - value = visible_prompt_func('').lower().strip() + value = visible_prompt_func("").lower().strip() except (KeyboardInterrupt, EOFError): raise Abort() - if value in ('y', 'yes'): + if value in ("y", "yes"): rv = True - elif value in ('n', 'no'): + elif value in ("n", "no"): rv = False - elif value == '': + elif value == "": rv = default else: - echo('Error: invalid input', err=err) + echo("Error: invalid input", err=err) continue break if abort and not rv: @@ -206,7 +222,8 @@ def get_terminal_size(): # If shutil has get_terminal_size() (Python 3.3 and later) use that if sys.version_info >= (3, 3): import shutil - shutil_get_terminal_size = getattr(shutil, 'get_terminal_size', None) + + shutil_get_terminal_size = getattr(shutil, "get_terminal_size", None) if shutil_get_terminal_size: sz = shutil_get_terminal_size() return sz.columns, sz.lines @@ -224,8 +241,8 @@ def ioctl_gwinsz(fd): try: import fcntl import termios - cr = struct.unpack( - 'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) + + cr = struct.unpack("hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, "1234")) except Exception: return return cr @@ -241,8 +258,7 @@ def ioctl_gwinsz(fd): except Exception: pass if not cr or not cr[0] or not cr[1]: - cr = (os.environ.get('LINES', 25), - os.environ.get('COLUMNS', DEFAULT_COLUMNS)) + cr = (os.environ.get("LINES", 25), os.environ.get("COLUMNS", DEFAULT_COLUMNS)) return int(cr[1]), int(cr[0]) @@ -268,18 +284,29 @@ def echo_via_pager(text_or_generator, color=None): i = iter(text_or_generator) # convert every element of i to a text type if necessary - text_generator = (el if isinstance(el, string_types) else text_type(el) - for el in i) + text_generator = (el if isinstance(el, string_types) else text_type(el) for el in i) from ._termui_impl import pager + return pager(itertools.chain(text_generator, "\n"), color) -def progressbar(iterable=None, length=None, label=None, show_eta=True, - show_percent=None, show_pos=False, - item_show_func=None, fill_char='#', empty_char='-', - bar_template='%(label)s [%(bar)s] %(info)s', - info_sep=' ', width=36, file=None, color=None): +def progressbar( + iterable=None, + length=None, + label=None, + show_eta=True, + show_percent=None, + show_pos=False, + item_show_func=None, + fill_char="#", + empty_char="-", + bar_template="%(label)s [%(bar)s] %(info)s", + info_sep=" ", + width=36, + file=None, + color=None, +): """This function creates an iterable context manager that can be used to iterate over something while showing a progress bar. It will either iterate over the `iterable` or `length` items (that are counted @@ -365,13 +392,24 @@ def progressbar(iterable=None, length=None, label=None, show_eta=True, which is not the case by default. """ from ._termui_impl import ProgressBar + color = resolve_color_default(color) - return ProgressBar(iterable=iterable, length=length, show_eta=show_eta, - show_percent=show_percent, show_pos=show_pos, - item_show_func=item_show_func, fill_char=fill_char, - empty_char=empty_char, bar_template=bar_template, - info_sep=info_sep, file=file, label=label, - width=width, color=color) + return ProgressBar( + iterable=iterable, + length=length, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + ) def clear(): @@ -387,13 +425,22 @@ def clear(): # clear the screen by shelling out. Otherwise we can use an escape # sequence. if WIN: - os.system('cls') + os.system("cls") else: - sys.stdout.write('\033[2J\033[1;1H') - - -def style(text, fg=None, bg=None, bold=None, dim=None, underline=None, - blink=None, reverse=None, reset=True): + sys.stdout.write("\033[2J\033[1;1H") + + +def style( + text, + fg=None, + bg=None, + bold=None, + dim=None, + underline=None, + blink=None, + reverse=None, + reset=True, +): """Styles a text with ANSI styles and returns the new string. By default the styling is self contained which means that at the end of the string a reset code is issued. This can be prevented by @@ -448,28 +495,28 @@ def style(text, fg=None, bg=None, bold=None, dim=None, underline=None, bits = [] if fg: try: - bits.append('\033[%dm' % (_ansi_colors[fg])) + bits.append("\033[%dm" % (_ansi_colors[fg])) except KeyError: - raise TypeError('Unknown color %r' % fg) + raise TypeError("Unknown color %r" % fg) if bg: try: - bits.append('\033[%dm' % (_ansi_colors[bg] + 10)) + bits.append("\033[%dm" % (_ansi_colors[bg] + 10)) except KeyError: - raise TypeError('Unknown color %r' % bg) + raise TypeError("Unknown color %r" % bg) if bold is not None: - bits.append('\033[%dm' % (1 if bold else 22)) + bits.append("\033[%dm" % (1 if bold else 22)) if dim is not None: - bits.append('\033[%dm' % (2 if dim else 22)) + bits.append("\033[%dm" % (2 if dim else 22)) if underline is not None: - bits.append('\033[%dm' % (4 if underline else 24)) + bits.append("\033[%dm" % (4 if underline else 24)) if blink is not None: - bits.append('\033[%dm' % (5 if blink else 25)) + bits.append("\033[%dm" % (5 if blink else 25)) if reverse is not None: - bits.append('\033[%dm' % (7 if reverse else 27)) + bits.append("\033[%dm" % (7 if reverse else 27)) bits.append(text) if reset: bits.append(_ansi_reset_all) - return ''.join(bits) + return "".join(bits) def unstyle(text): @@ -501,8 +548,9 @@ def secho(message=None, file=None, nl=True, err=False, color=None, **styles): return echo(message, file=file, nl=nl, err=err, color=color) -def edit(text=None, editor=None, env=None, require_save=True, - extension='.txt', filename=None): +def edit( + text=None, editor=None, env=None, require_save=True, extension=".txt", filename=None +): r"""Edits the given text in the defined editor. If an editor is given (should be the full path to the executable but the regular operating system search path is used for finding the executable) it overrides @@ -531,8 +579,10 @@ def edit(text=None, editor=None, env=None, require_save=True, file as an indirection in that case. """ from ._termui_impl import Editor - editor = Editor(editor=editor, env=env, require_save=require_save, - extension=extension) + + editor = Editor( + editor=editor, env=env, require_save=require_save, extension=extension + ) if filename is None: return editor.edit(text) editor.edit_file(filename) @@ -561,6 +611,7 @@ def launch(url, wait=False, locate=False): the filesystem. """ from ._termui_impl import open_url + return open_url(url, wait=wait, locate=locate) @@ -597,10 +648,11 @@ def getchar(echo=False): def raw_terminal(): from ._termui_impl import raw_terminal as f + return f() -def pause(info='Press any key to continue ...', err=False): +def pause(info="Press any key to continue ...", err=False): """This command stops execution and waits for the user to press any key to continue. This is similar to the Windows batch "pause" command. If the program is not run through a terminal, this command diff --git a/src/click/testing.py b/src/click/testing.py index 16cf42c08..c50418fac 100644 --- a/src/click/testing.py +++ b/src/click/testing.py @@ -13,7 +13,7 @@ # correct package is discovered. Ideally we could use a # relative import here but unfortunately Python does not # support that. -clickpkg = sys.modules[__name__.rsplit('.', 1)[0]] +clickpkg = sys.modules[__name__.rsplit(".", 1)[0]] if PY2: @@ -24,7 +24,6 @@ class EchoingStdin(object): - def __init__(self, input, output): self._input = input self._output = output @@ -54,16 +53,16 @@ def __repr__(self): def make_input_stream(input, charset): # Is already an input stream. - if hasattr(input, 'read'): + if hasattr(input, "read"): if PY2: return input rv = _find_binary_reader(input) if rv is not None: return rv - raise TypeError('Could not find binary reader for input stream.') + raise TypeError("Could not find binary reader for input stream.") if input is None: - input = b'' + input = b"" elif not isinstance(input, bytes): input = input.encode(charset) if PY2: @@ -74,8 +73,9 @@ def make_input_stream(input, charset): class Result(object): """Holds the captured result of an invoked CLI script.""" - def __init__(self, runner, stdout_bytes, stderr_bytes, exit_code, - exception, exc_info=None): + def __init__( + self, runner, stdout_bytes, stderr_bytes, exit_code, exception, exc_info=None + ): #: The runner that created the result self.runner = runner #: The standard output as bytes. @@ -97,22 +97,23 @@ def output(self): @property def stdout(self): """The standard output as unicode string.""" - return self.stdout_bytes.decode(self.runner.charset, 'replace') \ - .replace('\r\n', '\n') + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) @property def stderr(self): """The standard error as unicode string.""" if self.stderr_bytes is None: raise ValueError("stderr not separately captured") - return self.stderr_bytes.decode(self.runner.charset, 'replace') \ - .replace('\r\n', '\n') - + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) def __repr__(self): - return '<%s %s>' % ( + return "<%s %s>" % ( type(self).__name__, - self.exception and repr(self.exception) or 'okay', + self.exception and repr(self.exception) or "okay", ) @@ -137,10 +138,9 @@ class CliRunner(object): independently """ - def __init__(self, charset=None, env=None, echo_stdin=False, - mix_stderr=True): + def __init__(self, charset=None, env=None, echo_stdin=False, mix_stderr=True): if charset is None: - charset = 'utf-8' + charset = "utf-8" self.charset = charset self.env = env or {} self.echo_stdin = echo_stdin @@ -151,7 +151,7 @@ def get_default_prog_name(self, cli): for it. The default is the `name` attribute or ``"root"`` if not set. """ - return cli.name or 'root' + return cli.name or "root" def make_env(self, overrides=None): """Returns the environment overrides for invoking a script.""" @@ -201,12 +201,10 @@ def isolation(self, input=None, env=None, color=False): if self.echo_stdin: input = EchoingStdin(input, bytes_output) input = io.TextIOWrapper(input, encoding=self.charset) - sys.stdout = io.TextIOWrapper( - bytes_output, encoding=self.charset) + sys.stdout = io.TextIOWrapper(bytes_output, encoding=self.charset) if not self.mix_stderr: bytes_error = io.BytesIO() - sys.stderr = io.TextIOWrapper( - bytes_error, encoding=self.charset) + sys.stderr = io.TextIOWrapper(bytes_error, encoding=self.charset) if self.mix_stderr: sys.stderr = sys.stdout @@ -214,16 +212,16 @@ def isolation(self, input=None, env=None, color=False): sys.stdin = input def visible_input(prompt=None): - sys.stdout.write(prompt or '') - val = input.readline().rstrip('\r\n') - sys.stdout.write(val + '\n') + sys.stdout.write(prompt or "") + val = input.readline().rstrip("\r\n") + sys.stdout.write(val + "\n") sys.stdout.flush() return val def hidden_input(prompt=None): - sys.stdout.write((prompt or '') + '\n') + sys.stdout.write((prompt or "") + "\n") sys.stdout.flush() - return input.readline().rstrip('\r\n') + return input.readline().rstrip("\r\n") def _getchar(echo): char = sys.stdin.read(1) @@ -278,8 +276,16 @@ def should_strip_ansi(stream=None, color=None): clickpkg.utils.should_strip_ansi = old_should_strip_ansi clickpkg.formatting.FORCED_WIDTH = old_forced_width - def invoke(self, cli, args=None, input=None, env=None, - catch_exceptions=True, color=False, **extra): + def invoke( + self, + cli, + args=None, + input=None, + env=None, + catch_exceptions=True, + color=False, + **extra + ): """Invokes a command in an isolated environment. The arguments are forwarded directly to the command line script, the `extra` keyword arguments are passed to the :meth:`~clickpkg.Command.main` function of @@ -336,7 +342,7 @@ def invoke(self, cli, args=None, input=None, env=None, if not isinstance(exit_code, int): sys.stdout.write(str(exit_code)) - sys.stdout.write('\n') + sys.stdout.write("\n") exit_code = 1 except Exception as e: @@ -353,12 +359,14 @@ def invoke(self, cli, args=None, input=None, env=None, else: stderr = outstreams[1].getvalue() - return Result(runner=self, - stdout_bytes=stdout, - stderr_bytes=stderr, - exit_code=exit_code, - exception=exception, - exc_info=exc_info) + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, + ) @contextlib.contextmanager def isolated_filesystem(self): diff --git a/src/click/types.py b/src/click/types.py index 6bb69f07f..1d3d5b319 100644 --- a/src/click/types.py +++ b/src/click/types.py @@ -27,6 +27,7 @@ class ParamType(object): This can be the case when the object is used with prompt inputs. """ + is_composite = False #: the descriptive name of this type @@ -68,7 +69,7 @@ def split_envvar_value(self, rv): then leading and trailing whitespace is ignored. Otherwise, leading and trailing splitters usually lead to empty items being included. """ - return (rv or '').split(self.envvar_list_splitter) + return (rv or "").split(self.envvar_list_splitter) def fail(self, message, param=None, ctx=None): """Helper method to fail with an invalid value message.""" @@ -84,7 +85,6 @@ def arity(self): class FuncParamType(ParamType): - def __init__(self, func): self.name = func.__name__ self.func = func @@ -96,22 +96,22 @@ def convert(self, value, param, ctx): try: value = text_type(value) except UnicodeError: - value = str(value).decode('utf-8', 'replace') + value = str(value).decode("utf-8", "replace") self.fail(value, param, ctx) class UnprocessedParamType(ParamType): - name = 'text' + name = "text" def convert(self, value, param, ctx): return value def __repr__(self): - return 'UNPROCESSED' + return "UNPROCESSED" class StringParamType(ParamType): - name = 'text' + name = "text" def convert(self, value, param, ctx): if isinstance(value, bytes): @@ -124,14 +124,14 @@ def convert(self, value, param, ctx): try: value = value.decode(fs_enc) except UnicodeError: - value = value.decode('utf-8', 'replace') + value = value.decode("utf-8", "replace") else: - value = value.decode('utf-8', 'replace') + value = value.decode("utf-8", "replace") return value return value def __repr__(self): - return 'STRING' + return "STRING" class Choice(ParamType): @@ -151,17 +151,17 @@ class Choice(ParamType): insensitive. Defaults to true. """ - name = 'choice' + name = "choice" def __init__(self, choices, case_sensitive=True): self.choices = choices self.case_sensitive = case_sensitive def get_metavar(self, param): - return '[%s]' % '|'.join(self.choices) + return "[%s]" % "|".join(self.choices) def get_missing_message(self, param): - return 'Choose from:\n\t%s.' % ',\n\t'.join(self.choices) + return "Choose from:\n\t%s." % ",\n\t".join(self.choices) def convert(self, value, param, ctx): # Match through normalization and case sensitivity @@ -193,11 +193,14 @@ def convert(self, value, param, ctx): if normed_value in normed_choices: return normed_choices[normed_value] - self.fail('invalid choice: %s. (choose from %s)' % - (value, ', '.join(self.choices)), param, ctx) + self.fail( + "invalid choice: %s. (choose from %s)" % (value, ", ".join(self.choices)), + param, + ctx, + ) def __repr__(self): - return 'Choice(%r)' % list(self.choices) + return "Choice(%r)" % list(self.choices) class DateTime(ParamType): @@ -220,17 +223,14 @@ class DateTime(ParamType): ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, ``'%Y-%m-%d %H:%M:%S'``. """ - name = 'datetime' + + name = "datetime" def __init__(self, formats=None): - self.formats = formats or [ - '%Y-%m-%d', - '%Y-%m-%dT%H:%M:%S', - '%Y-%m-%d %H:%M:%S' - ] + self.formats = formats or ["%Y-%m-%d", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"] def get_metavar(self, param): - return '[{}]'.format('|'.join(self.formats)) + return "[{}]".format("|".join(self.formats)) def _try_to_convert_date(self, value, format): try: @@ -246,24 +246,26 @@ def convert(self, value, param, ctx): return dtime self.fail( - 'invalid datetime format: {}. (choose from {})'.format( - value, ', '.join(self.formats))) + "invalid datetime format: {}. (choose from {})".format( + value, ", ".join(self.formats) + ) + ) def __repr__(self): - return 'DateTime' + return "DateTime" class IntParamType(ParamType): - name = 'integer' + name = "integer" def convert(self, value, param, ctx): try: return int(value) except (ValueError, UnicodeError): - self.fail('%s is not a valid integer' % value, param, ctx) + self.fail("%s is not a valid integer" % value, param, ctx) def __repr__(self): - return 'INT' + return "INT" class IntRange(IntParamType): @@ -274,7 +276,8 @@ class IntRange(IntParamType): See :ref:`ranges` for an example. """ - name = 'integer range' + + name = "integer range" def __init__(self, min=None, max=None, clamp=False): self.min = min @@ -288,35 +291,49 @@ def convert(self, value, param, ctx): return self.min if self.max is not None and rv > self.max: return self.max - if self.min is not None and rv < self.min or \ - self.max is not None and rv > self.max: + if ( + self.min is not None + and rv < self.min + or self.max is not None + and rv > self.max + ): if self.min is None: - self.fail('%s is bigger than the maximum valid value ' - '%s.' % (rv, self.max), param, ctx) + self.fail( + "%s is bigger than the maximum valid value %s." % (rv, self.max), + param, + ctx, + ) elif self.max is None: - self.fail('%s is smaller than the minimum valid value ' - '%s.' % (rv, self.min), param, ctx) + self.fail( + "%s is smaller than the minimum valid value " + "%s." % (rv, self.min), + param, + ctx, + ) else: - self.fail('%s is not in the valid range of %s to %s.' - % (rv, self.min, self.max), param, ctx) + self.fail( + "%s is not in the valid range of %s to %s." + % (rv, self.min, self.max), + param, + ctx, + ) return rv def __repr__(self): - return 'IntRange(%r, %r)' % (self.min, self.max) + return "IntRange(%r, %r)" % (self.min, self.max) class FloatParamType(ParamType): - name = 'float' + name = "float" def convert(self, value, param, ctx): try: return float(value) except (UnicodeError, ValueError): - self.fail('%s is not a valid floating point value' % - value, param, ctx) + self.fail("%s is not a valid floating point value" % value, param, ctx) def __repr__(self): - return 'FLOAT' + return "FLOAT" class FloatRange(FloatParamType): @@ -327,7 +344,8 @@ class FloatRange(FloatParamType): See :ref:`ranges` for an example. """ - name = 'float range' + + name = "float range" def __init__(self, min=None, max=None, clamp=False): self.min = min @@ -341,54 +359,70 @@ def convert(self, value, param, ctx): return self.min if self.max is not None and rv > self.max: return self.max - if self.min is not None and rv < self.min or \ - self.max is not None and rv > self.max: + if ( + self.min is not None + and rv < self.min + or self.max is not None + and rv > self.max + ): if self.min is None: - self.fail('%s is bigger than the maximum valid value ' - '%s.' % (rv, self.max), param, ctx) + self.fail( + "%s is bigger than the maximum valid value %s." % (rv, self.max), + param, + ctx, + ) elif self.max is None: - self.fail('%s is smaller than the minimum valid value ' - '%s.' % (rv, self.min), param, ctx) + self.fail( + "%s is smaller than the minimum valid value " + "%s." % (rv, self.min), + param, + ctx, + ) else: - self.fail('%s is not in the valid range of %s to %s.' - % (rv, self.min, self.max), param, ctx) + self.fail( + "%s is not in the valid range of %s to %s." + % (rv, self.min, self.max), + param, + ctx, + ) return rv def __repr__(self): - return 'FloatRange(%r, %r)' % (self.min, self.max) + return "FloatRange(%r, %r)" % (self.min, self.max) class BoolParamType(ParamType): - name = 'boolean' + name = "boolean" def convert(self, value, param, ctx): if isinstance(value, bool): return bool(value) value = value.lower() - if value in ('true', 't', '1', 'yes', 'y'): + if value in ("true", "t", "1", "yes", "y"): return True - elif value in ('false', 'f', '0', 'no', 'n'): + elif value in ("false", "f", "0", "no", "n"): return False - self.fail('%s is not a valid boolean' % value, param, ctx) + self.fail("%s is not a valid boolean" % value, param, ctx) def __repr__(self): - return 'BOOL' + return "BOOL" class UUIDParameterType(ParamType): - name = 'uuid' + name = "uuid" def convert(self, value, param, ctx): import uuid + try: if PY2 and isinstance(value, text_type): - value = value.encode('ascii') + value = value.encode("ascii") return uuid.UUID(value) except (UnicodeError, ValueError): - self.fail('%s is not a valid UUID value' % value, param, ctx) + self.fail("%s is not a valid UUID value" % value, param, ctx) def __repr__(self): - return 'UUID' + return "UUID" class File(ParamType): @@ -417,11 +451,13 @@ class File(ParamType): See :ref:`file-args` for more information. """ - name = 'filename' + + name = "filename" envvar_list_splitter = os.path.pathsep - def __init__(self, mode='r', encoding=None, errors='strict', lazy=None, - atomic=False): + def __init__( + self, mode="r", encoding=None, errors="strict", lazy=None, atomic=False + ): self.mode = mode self.encoding = encoding self.errors = errors @@ -431,29 +467,30 @@ def __init__(self, mode='r', encoding=None, errors='strict', lazy=None, def resolve_lazy_flag(self, value): if self.lazy is not None: return self.lazy - if value == '-': + if value == "-": return False - elif 'w' in self.mode: + elif "w" in self.mode: return True return False def convert(self, value, param, ctx): try: - if hasattr(value, 'read') or hasattr(value, 'write'): + if hasattr(value, "read") or hasattr(value, "write"): return value lazy = self.resolve_lazy_flag(value) if lazy: - f = LazyFile(value, self.mode, self.encoding, self.errors, - atomic=self.atomic) + f = LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) if ctx is not None: ctx.call_on_close(f.close_intelligently) return f - f, should_close = open_stream(value, self.mode, - self.encoding, self.errors, - atomic=self.atomic) + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) # If a context is provided, we automatically close the file # at the end of the context execution (or flush out). If a # context does not exist, it's the caller's responsibility to @@ -466,10 +503,12 @@ def convert(self, value, param, ctx): ctx.call_on_close(safecall(f.flush)) return f except (IOError, OSError) as e: - self.fail('Could not open file: %s: %s' % ( - filename_to_ui(value), - get_streerror(e), - ), param, ctx) + self.fail( + "Could not open file: %s: %s" + % (filename_to_ui(value), get_streerror(e),), + param, + ctx, + ) class Path(ParamType): @@ -502,11 +541,20 @@ class Path(ParamType): unicode depending on what makes most sense given the input data Click deals with. """ + envvar_list_splitter = os.path.pathsep - def __init__(self, exists=False, file_okay=True, dir_okay=True, - writable=False, readable=True, resolve_path=False, - allow_dash=False, path_type=None): + def __init__( + self, + exists=False, + file_okay=True, + dir_okay=True, + writable=False, + readable=True, + resolve_path=False, + allow_dash=False, + path_type=None, + ): self.exists = exists self.file_okay = file_okay self.dir_okay = dir_okay @@ -517,14 +565,14 @@ def __init__(self, exists=False, file_okay=True, dir_okay=True, self.type = path_type if self.file_okay and not self.dir_okay: - self.name = 'file' - self.path_type = 'File' + self.name = "file" + self.path_type = "File" elif self.dir_okay and not self.file_okay: - self.name = 'directory' - self.path_type = 'Directory' + self.name = "directory" + self.path_type = "Directory" else: - self.name = 'path' - self.path_type = 'Path' + self.name = "path" + self.path_type = "Path" def coerce_path_result(self, rv): if self.type is not None and not isinstance(rv, self.type): @@ -537,7 +585,7 @@ def coerce_path_result(self, rv): def convert(self, value, param, ctx): rv = value - is_dash = self.file_okay and self.allow_dash and rv in (b'-', '-') + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") if not is_dash: if self.resolve_path: @@ -548,31 +596,38 @@ def convert(self, value, param, ctx): except OSError: if not self.exists: return self.coerce_path_result(rv) - self.fail('%s "%s" does not exist.' % ( - self.path_type, - filename_to_ui(value) - ), param, ctx) + self.fail( + '%s "%s" does not exist.' % (self.path_type, filename_to_ui(value)), + param, + ctx, + ) if not self.file_okay and stat.S_ISREG(st.st_mode): - self.fail('%s "%s" is a file.' % ( - self.path_type, - filename_to_ui(value) - ), param, ctx) + self.fail( + '%s "%s" is a file.' % (self.path_type, filename_to_ui(value)), + param, + ctx, + ) if not self.dir_okay and stat.S_ISDIR(st.st_mode): - self.fail('%s "%s" is a directory.' % ( - self.path_type, - filename_to_ui(value) - ), param, ctx) + self.fail( + '%s "%s" is a directory.' % (self.path_type, filename_to_ui(value)), + param, + ctx, + ) if self.writable and not os.access(value, os.W_OK): - self.fail('%s "%s" is not writable.' % ( - self.path_type, - filename_to_ui(value) - ), param, ctx) + self.fail( + '%s "%s" is not writable.' + % (self.path_type, filename_to_ui(value)), + param, + ctx, + ) if self.readable and not os.access(value, os.R_OK): - self.fail('%s "%s" is not readable.' % ( - self.path_type, - filename_to_ui(value) - ), param, ctx) + self.fail( + '%s "%s" is not readable.' + % (self.path_type, filename_to_ui(value)), + param, + ctx, + ) return self.coerce_path_result(rv) @@ -604,8 +659,10 @@ def arity(self): def convert(self, value, param, ctx): if len(value) != len(self.types): - raise TypeError('It would appear that nargs is set to conflict ' - 'with the composite type arity.') + raise TypeError( + "It would appear that nargs is set to conflict " + "with the composite type arity." + ) return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) @@ -644,8 +701,9 @@ def convert_type(ty, default=None): if __debug__: try: if issubclass(ty, ParamType): - raise AssertionError('Attempted to use an uninstantiated ' - 'parameter type (%s).' % ty) + raise AssertionError( + "Attempted to use an uninstantiated parameter type (%s)." % ty + ) except TypeError: pass return FuncParamType(ty) diff --git a/src/click/utils.py b/src/click/utils.py index 3c436a4c2..210bbe4bf 100644 --- a/src/click/utils.py +++ b/src/click/utils.py @@ -30,16 +30,18 @@ def _posixify(name): - return '-'.join(name.split()).lower() + return "-".join(name.split()).lower() def safecall(func): """Wraps a function so that it swallows exceptions.""" + def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except Exception: pass + return wrapper @@ -49,7 +51,7 @@ def make_str(value): try: return value.decode(get_filesystem_encoding()) except UnicodeError: - return value.decode('utf-8', 'replace') + return value.decode("utf-8", "replace") return text_type(value) @@ -61,21 +63,21 @@ def make_default_short_help(help, max_length=45): done = False for word in words: - if word[-1:] == '.': + if word[-1:] == ".": done = True new_length = result and 1 + len(word) or len(word) if total_length + new_length > max_length: - result.append('...') + result.append("...") done = True else: if result: - result.append(' ') + result.append(" ") result.append(word) if done: break total_length += new_length - return ''.join(result) + return "".join(result) class LazyFile(object): @@ -85,19 +87,19 @@ class LazyFile(object): files for writing. """ - def __init__(self, filename, mode='r', encoding=None, errors='strict', - atomic=False): + def __init__( + self, filename, mode="r", encoding=None, errors="strict", atomic=False + ): self.name = filename self.mode = mode self.encoding = encoding self.errors = errors self.atomic = atomic - if filename == '-': - self._f, self.should_close = open_stream(filename, mode, - encoding, errors) + if filename == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) else: - if 'r' in mode: + if "r" in mode: # Open and close the file in case we're opening it for # reading so that we can catch at least some errors in # some cases early. @@ -111,7 +113,7 @@ def __getattr__(self, name): def __repr__(self): if self._f is not None: return repr(self._f) - return '' % (self.name, self.mode) + return "" % (self.name, self.mode) def open(self): """Opens the file if it's not yet open. This call might fail with @@ -121,12 +123,12 @@ def open(self): if self._f is not None: return self._f try: - rv, self.should_close = open_stream(self.name, self.mode, - self.encoding, - self.errors, - atomic=self.atomic) + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) except (IOError, OSError) as e: from .exceptions import FileError + raise FileError(self.name, hint=get_streerror(e)) self._f = rv return rv @@ -155,7 +157,6 @@ def __iter__(self): class KeepOpenFile(object): - def __init__(self, file): self._file = file @@ -233,11 +234,11 @@ def echo(message=None, file=None, nl=True, err=False, color=None): message = text_type(message) if nl: - message = message or u'' + message = message or u"" if isinstance(message, text_type): - message += u'\n' + message += u"\n" else: - message += b'\n' + message += b"\n" # If there is a message, and we're in Python 3, and the value looks # like bytes, we manually need to find the binary stream and write the @@ -284,11 +285,11 @@ def get_binary_stream(name): """ opener = binary_streams.get(name) if opener is None: - raise TypeError('Unknown standard stream %r' % name) + raise TypeError("Unknown standard stream %r" % name) return opener() -def get_text_stream(name, encoding=None, errors='strict'): +def get_text_stream(name, encoding=None, errors="strict"): """Returns a system stream for text processing. This usually returns a wrapped stream around a binary stream returned from :func:`get_binary_stream` but it also can take shortcuts on Python 3 @@ -301,12 +302,13 @@ def get_text_stream(name, encoding=None, errors='strict'): """ opener = text_streams.get(name) if opener is None: - raise TypeError('Unknown standard stream %r' % name) + raise TypeError("Unknown standard stream %r" % name) return opener(encoding, errors) -def open_file(filename, mode='r', encoding=None, errors='strict', - lazy=False, atomic=False): +def open_file( + filename, mode="r", encoding=None, errors="strict", lazy=False, atomic=False +): """This is similar to how the :class:`File` works but for manual usage. Files are opened non lazy by default. This can open regular files as well as stdin/stdout if ``'-'`` is passed. @@ -331,8 +333,7 @@ def open_file(filename, mode='r', encoding=None, errors='strict', """ if lazy: return LazyFile(filename, mode, encoding, errors, atomic=atomic) - f, should_close = open_stream(filename, mode, encoding, errors, - atomic=atomic) + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) if not should_close: f = KeepOpenFile(f) return f @@ -412,19 +413,21 @@ def get_app_dir(app_name, roaming=True, force_posix=False): application support folder. """ if WIN: - key = roaming and 'APPDATA' or 'LOCALAPPDATA' + key = roaming and "APPDATA" or "LOCALAPPDATA" folder = os.environ.get(key) if folder is None: - folder = os.path.expanduser('~') + folder = os.path.expanduser("~") return os.path.join(folder, app_name) if force_posix: - return os.path.join(os.path.expanduser('~/.' + _posixify(app_name))) - if sys.platform == 'darwin': - return os.path.join(os.path.expanduser( - '~/Library/Application Support'), app_name) + return os.path.join(os.path.expanduser("~/." + _posixify(app_name))) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) return os.path.join( - os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), - _posixify(app_name)) + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) class PacifyFlushWrapper(object): @@ -444,6 +447,7 @@ def flush(self): self.wrapped.flush() except IOError as e: import errno + if e.errno != errno.EPIPE: raise diff --git a/tests/conftest.py b/tests/conftest.py index 05b59c1fd..9440804fe 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,6 @@ from click.testing import CliRunner -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def runner(request): return CliRunner() diff --git a/tests/test_arguments.py b/tests/test_arguments.py index f92a5607e..7b214ad71 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -10,45 +10,47 @@ def test_nargs_star(runner): @click.command() - @click.argument('src', nargs=-1) - @click.argument('dst') + @click.argument("src", nargs=-1) + @click.argument("dst") def copy(src, dst): - click.echo('src=%s' % '|'.join(src)) - click.echo('dst=%s' % dst) + click.echo("src=%s" % "|".join(src)) + click.echo("dst=%s" % dst) - result = runner.invoke(copy, ['foo.txt', 'bar.txt', 'dir']) + result = runner.invoke(copy, ["foo.txt", "bar.txt", "dir"]) assert not result.exception assert result.output.splitlines() == [ - 'src=foo.txt|bar.txt', - 'dst=dir', + "src=foo.txt|bar.txt", + "dst=dir", ] def test_nargs_default(runner): try: + @click.command() - @click.argument('src', nargs=-1, default=42) + @click.argument("src", nargs=-1, default=42) def copy(src): pass + except TypeError as e: - assert 'nargs=-1' in str(e) + assert "nargs=-1" in str(e) else: assert False def test_nargs_tup(runner): @click.command() - @click.argument('name', nargs=1) - @click.argument('point', nargs=2, type=click.INT) + @click.argument("name", nargs=1) + @click.argument("point", nargs=2, type=click.INT) def copy(name, point): - click.echo('name=%s' % name) - click.echo('point=%d/%d' % point) + click.echo("name=%s" % name) + click.echo("point=%d/%d" % point) - result = runner.invoke(copy, ['peter', '1', '2']) + result = runner.invoke(copy, ["peter", "1", "2"]) assert not result.exception assert result.output.splitlines() == [ - 'name=peter', - 'point=1/2', + "name=peter", + "point=1/2", ] @@ -61,57 +63,63 @@ def test_nargs_tup_composite(runner): ] for opts in variations: + @click.command() - @click.argument('item', **opts) + @click.argument("item", **opts) def copy(item): - click.echo('name=%s id=%d' % item) + click.echo("name=%s id=%d" % item) - result = runner.invoke(copy, ['peter', '1']) + result = runner.invoke(copy, ["peter", "1"]) assert not result.exception assert result.output.splitlines() == [ - 'name=peter id=1', + "name=peter id=1", ] def test_nargs_err(runner): @click.command() - @click.argument('x') + @click.argument("x") def copy(x): click.echo(x) - result = runner.invoke(copy, ['foo']) + result = runner.invoke(copy, ["foo"]) assert not result.exception - assert result.output == 'foo\n' + assert result.output == "foo\n" - result = runner.invoke(copy, ['foo', 'bar']) + result = runner.invoke(copy, ["foo", "bar"]) assert result.exit_code == 2 - assert 'Got unexpected extra argument (bar)' in result.output + assert "Got unexpected extra argument (bar)" in result.output def test_bytes_args(runner, monkeypatch): @click.command() - @click.argument('arg') + @click.argument("arg") def from_bytes(arg): - assert isinstance(arg, text_type), "UTF-8 encoded argument should be implicitly converted to Unicode" + assert isinstance( + arg, text_type + ), "UTF-8 encoded argument should be implicitly converted to Unicode" # Simulate empty locale environment variables if PY2: - monkeypatch.setattr(sys.stdin, 'encoding', 'ANSI_X3.4-1968') - monkeypatch.setattr(sys, 'getfilesystemencoding', lambda: 'ANSI_X3.4-1968') - monkeypatch.setattr(sys, 'getdefaultencoding', lambda: 'ascii') + monkeypatch.setattr(sys.stdin, "encoding", "ANSI_X3.4-1968") + monkeypatch.setattr(sys, "getfilesystemencoding", lambda: "ANSI_X3.4-1968") + monkeypatch.setattr(sys, "getdefaultencoding", lambda: "ascii") else: - monkeypatch.setattr(sys.stdin, 'encoding', 'utf-8') - monkeypatch.setattr(sys, 'getfilesystemencoding', lambda: 'utf-8') - monkeypatch.setattr(sys, 'getdefaultencoding', lambda: 'utf-8') + monkeypatch.setattr(sys.stdin, "encoding", "utf-8") + monkeypatch.setattr(sys, "getfilesystemencoding", lambda: "utf-8") + monkeypatch.setattr(sys, "getdefaultencoding", lambda: "utf-8") - runner.invoke(from_bytes, [u'Something outside of ASCII range: 林'.encode('UTF-8')], - catch_exceptions=False) + runner.invoke( + from_bytes, + [u"Something outside of ASCII range: 林".encode("UTF-8")], + catch_exceptions=False, + ) def test_file_args(runner): @click.command() - @click.argument('input', type=click.File('rb')) - @click.argument('output', type=click.File('wb')) + @click.argument("input", type=click.File("rb")) + @click.argument("output", type=click.File("wb")) def inout(input, output): while True: chunk = input.read(1024) @@ -120,96 +128,96 @@ def inout(input, output): output.write(chunk) with runner.isolated_filesystem(): - result = runner.invoke(inout, ['-', 'hello.txt'], input='Hey!') - assert result.output == '' + result = runner.invoke(inout, ["-", "hello.txt"], input="Hey!") + assert result.output == "" assert result.exit_code == 0 - with open('hello.txt', 'rb') as f: - assert f.read() == b'Hey!' + with open("hello.txt", "rb") as f: + assert f.read() == b"Hey!" - result = runner.invoke(inout, ['hello.txt', '-']) - assert result.output == 'Hey!' + result = runner.invoke(inout, ["hello.txt", "-"]) + assert result.output == "Hey!" assert result.exit_code == 0 def test_path_args(runner): @click.command() - @click.argument('input', type=click.Path(dir_okay=False, allow_dash=True)) + @click.argument("input", type=click.Path(dir_okay=False, allow_dash=True)) def foo(input): click.echo(input) - result = runner.invoke(foo, ['-']) - assert result.output == '-\n' + result = runner.invoke(foo, ["-"]) + assert result.output == "-\n" assert result.exit_code == 0 def test_file_atomics(runner): @click.command() - @click.argument('output', type=click.File('wb', atomic=True)) + @click.argument("output", type=click.File("wb", atomic=True)) def inout(output): - output.write(b'Foo bar baz\n') + output.write(b"Foo bar baz\n") output.flush() - with open(output.name, 'rb') as f: + with open(output.name, "rb") as f: old_content = f.read() - assert old_content == b'OLD\n' + assert old_content == b"OLD\n" with runner.isolated_filesystem(): - with open('foo.txt', 'wb') as f: - f.write(b'OLD\n') - result = runner.invoke(inout, ['foo.txt'], input='Hey!', - catch_exceptions=False) - assert result.output == '' + with open("foo.txt", "wb") as f: + f.write(b"OLD\n") + result = runner.invoke(inout, ["foo.txt"], input="Hey!", catch_exceptions=False) + assert result.output == "" assert result.exit_code == 0 - with open('foo.txt', 'rb') as f: - assert f.read() == b'Foo bar baz\n' + with open("foo.txt", "rb") as f: + assert f.read() == b"Foo bar baz\n" def test_stdout_default(runner): @click.command() - @click.argument('output', type=click.File('w'), default='-') + @click.argument("output", type=click.File("w"), default="-") def inout(output): - output.write('Foo bar baz\n') + output.write("Foo bar baz\n") output.flush() result = runner.invoke(inout, []) assert not result.exception - assert result.output == 'Foo bar baz\n' + assert result.output == "Foo bar baz\n" def test_nargs_envvar(runner): @click.command() - @click.option('--arg', nargs=2) + @click.option("--arg", nargs=2) def cmd(arg): - click.echo('|'.join(arg)) + click.echo("|".join(arg)) - result = runner.invoke(cmd, [], auto_envvar_prefix='TEST', - env={'TEST_ARG': 'foo bar'}) + result = runner.invoke( + cmd, [], auto_envvar_prefix="TEST", env={"TEST_ARG": "foo bar"} + ) assert not result.exception - assert result.output == 'foo|bar\n' + assert result.output == "foo|bar\n" @click.command() - @click.option('--arg', envvar='X', nargs=2) + @click.option("--arg", envvar="X", nargs=2) def cmd(arg): - click.echo('|'.join(arg)) + click.echo("|".join(arg)) - result = runner.invoke(cmd, [], env={'X': 'foo bar'}) + result = runner.invoke(cmd, [], env={"X": "foo bar"}) assert not result.exception - assert result.output == 'foo|bar\n' + assert result.output == "foo|bar\n" def test_empty_nargs(runner): @click.command() - @click.argument('arg', nargs=-1) + @click.argument("arg", nargs=-1) def cmd(arg): - click.echo('arg:' + '|'.join(arg)) + click.echo("arg:" + "|".join(arg)) result = runner.invoke(cmd, []) assert result.exit_code == 0 - assert result.output == 'arg:\n' + assert result.output == "arg:\n" @click.command() - @click.argument('arg', nargs=-1, required=True) + @click.argument("arg", nargs=-1, required=True) def cmd2(arg): - click.echo('arg:' + '|'.join(arg)) + click.echo("arg:" + "|".join(arg)) result = runner.invoke(cmd2, []) assert result.exit_code == 2 @@ -218,9 +226,9 @@ def cmd2(arg): def test_missing_arg(runner): @click.command() - @click.argument('arg') + @click.argument("arg") def cmd(arg): - click.echo('arg:' + arg) + click.echo("arg:" + arg) result = runner.invoke(cmd, []) assert result.exit_code == 2 @@ -238,94 +246,95 @@ def test_missing_argument_string_cast(): def test_implicit_non_required(runner): @click.command() - @click.argument('f', default='test') + @click.argument("f", default="test") def cli(f): click.echo(f) result = runner.invoke(cli, []) assert result.exit_code == 0 - assert result.output == 'test\n' + assert result.output == "test\n" def test_eat_options(runner): @click.command() - @click.option('-f') - @click.argument('files', nargs=-1) + @click.option("-f") + @click.argument("files", nargs=-1) def cmd(f, files): for filename in files: click.echo(filename) click.echo(f) - result = runner.invoke(cmd, ['--', '-foo', 'bar']) + result = runner.invoke(cmd, ["--", "-foo", "bar"]) assert result.output.splitlines() == [ - '-foo', - 'bar', - '', + "-foo", + "bar", + "", ] - result = runner.invoke(cmd, ['-f', '-x', '--', '-foo', 'bar']) + result = runner.invoke(cmd, ["-f", "-x", "--", "-foo", "bar"]) assert result.output.splitlines() == [ - '-foo', - 'bar', - '-x', + "-foo", + "bar", + "-x", ] def test_nargs_star_ordering(runner): @click.command() - @click.argument('a', nargs=-1) - @click.argument('b') - @click.argument('c') + @click.argument("a", nargs=-1) + @click.argument("b") + @click.argument("c") def cmd(a, b, c): for arg in (a, b, c): click.echo(arg) - result = runner.invoke(cmd, ['a', 'b', 'c']) + result = runner.invoke(cmd, ["a", "b", "c"]) assert result.output.splitlines() == [ PY2 and "(u'a',)" or "('a',)", - 'b', - 'c', + "b", + "c", ] def test_nargs_specified_plus_star_ordering(runner): @click.command() - @click.argument('a', nargs=-1) - @click.argument('b') - @click.argument('c', nargs=2) + @click.argument("a", nargs=-1) + @click.argument("b") + @click.argument("c", nargs=2) def cmd(a, b, c): for arg in (a, b, c): click.echo(arg) - result = runner.invoke(cmd, ['a', 'b', 'c', 'd', 'e', 'f']) + result = runner.invoke(cmd, ["a", "b", "c", "d", "e", "f"]) assert result.output.splitlines() == [ PY2 and "(u'a', u'b', u'c')" or "('a', 'b', 'c')", - 'd', + "d", PY2 and "(u'e', u'f')" or "('e', 'f')", ] def test_defaults_for_nargs(runner): @click.command() - @click.argument('a', nargs=2, type=int, default=(1, 2)) + @click.argument("a", nargs=2, type=int, default=(1, 2)) def cmd(a): x, y = a click.echo(x + y) result = runner.invoke(cmd, []) - assert result.output.strip() == '3' + assert result.output.strip() == "3" - result = runner.invoke(cmd, ['3', '4']) - assert result.output.strip() == '7' + result = runner.invoke(cmd, ["3", "4"]) + assert result.output.strip() == "7" - result = runner.invoke(cmd, ['3']) + result = runner.invoke(cmd, ["3"]) assert result.exception is not None - assert 'argument a takes 2 values' in result.output + assert "argument a takes 2 values" in result.output def test_multiple_param_decls_not_allowed(runner): with pytest.raises(TypeError): + @click.command() - @click.argument('x', click.Choice(['a', 'b'])) + @click.argument("x", click.Choice(["a", "b"])) def copy(x): click.echo(x) diff --git a/tests/test_bashcomplete.py b/tests/test_bashcomplete.py index 05557a01b..df4ef287c 100644 --- a/tests/test_bashcomplete.py +++ b/tests/test_bashcomplete.py @@ -6,94 +6,95 @@ def choices_without_help(cli, args, incomplete): - completions = get_choices(cli, 'dummy', args, incomplete) + completions = get_choices(cli, "dummy", args, incomplete) return [c[0] for c in completions] def choices_with_help(cli, args, incomplete): - return list(get_choices(cli, 'dummy', args, incomplete)) + return list(get_choices(cli, "dummy", args, incomplete)) def test_single_command(): @click.command() - @click.option('--local-opt') + @click.option("--local-opt") def cli(local_opt): pass - assert choices_without_help(cli, [], '-') == ['--local-opt'] - assert choices_without_help(cli, [], '') == [] + assert choices_without_help(cli, [], "-") == ["--local-opt"] + assert choices_without_help(cli, [], "") == [] def test_boolean_flag(): @click.command() - @click.option('--shout/--no-shout', default=False) + @click.option("--shout/--no-shout", default=False) def cli(local_opt): pass - assert choices_without_help(cli, [], '-') == ['--shout', '--no-shout'] + assert choices_without_help(cli, [], "-") == ["--shout", "--no-shout"] def test_multi_value_option(): @click.group() - @click.option('--pos', nargs=2, type=float) + @click.option("--pos", nargs=2, type=float) def cli(local_opt): pass @cli.command() - @click.option('--local-opt') + @click.option("--local-opt") def sub(local_opt): pass - assert choices_without_help(cli, [], '-') == ['--pos'] - assert choices_without_help(cli, ['--pos'], '') == [] - assert choices_without_help(cli, ['--pos', '1.0'], '') == [] - assert choices_without_help(cli, ['--pos', '1.0', '1.0'], '') == ['sub'] + assert choices_without_help(cli, [], "-") == ["--pos"] + assert choices_without_help(cli, ["--pos"], "") == [] + assert choices_without_help(cli, ["--pos", "1.0"], "") == [] + assert choices_without_help(cli, ["--pos", "1.0", "1.0"], "") == ["sub"] def test_multi_option(): @click.command() - @click.option('--message', '-m', multiple=True) + @click.option("--message", "-m", multiple=True) def cli(local_opt): pass - assert choices_without_help(cli, [], '-') == ['--message', '-m'] - assert choices_without_help(cli, ['-m'], '') == [] + assert choices_without_help(cli, [], "-") == ["--message", "-m"] + assert choices_without_help(cli, ["-m"], "") == [] def test_small_chain(): @click.group() - @click.option('--global-opt') + @click.option("--global-opt") def cli(global_opt): pass @cli.command() - @click.option('--local-opt') + @click.option("--local-opt") def sub(local_opt): pass - assert choices_without_help(cli, [], '') == ['sub'] - assert choices_without_help(cli, [], '-') == ['--global-opt'] - assert choices_without_help(cli, ['sub'], '') == [] - assert choices_without_help(cli, ['sub'], '-') == ['--local-opt'] + assert choices_without_help(cli, [], "") == ["sub"] + assert choices_without_help(cli, [], "-") == ["--global-opt"] + assert choices_without_help(cli, ["sub"], "") == [] + assert choices_without_help(cli, ["sub"], "-") == ["--local-opt"] def test_long_chain(): - @click.group('cli') - @click.option('--cli-opt') + @click.group("cli") + @click.option("--cli-opt") def cli(cli_opt): pass - @cli.group('asub') - @click.option('--asub-opt') + @cli.group("asub") + @click.option("--asub-opt") def asub(asub_opt): pass - @asub.group('bsub') - @click.option('--bsub-opt') + @asub.group("bsub") + @click.option("--bsub-opt") def bsub(bsub_opt): pass - COLORS = ['red', 'green', 'blue'] + COLORS = ["red", "green", "blue"] + def get_colors(ctx, args, incomplete): for c in COLORS: if c.startswith(incomplete): @@ -104,197 +105,237 @@ def search_colors(ctx, args, incomplete): if incomplete in c: yield c - CSUB_OPT_CHOICES = ['foo', 'bar'] - CSUB_CHOICES = ['bar', 'baz'] - @bsub.command('csub') - @click.option('--csub-opt', type=click.Choice(CSUB_OPT_CHOICES)) - @click.option('--csub', type=click.Choice(CSUB_CHOICES)) - @click.option('--search-color', autocompletion=search_colors) - @click.argument('color', autocompletion=get_colors) + CSUB_OPT_CHOICES = ["foo", "bar"] + CSUB_CHOICES = ["bar", "baz"] + + @bsub.command("csub") + @click.option("--csub-opt", type=click.Choice(CSUB_OPT_CHOICES)) + @click.option("--csub", type=click.Choice(CSUB_CHOICES)) + @click.option("--search-color", autocompletion=search_colors) + @click.argument("color", autocompletion=get_colors) def csub(csub_opt, color): pass - assert choices_without_help(cli, [], '-') == ['--cli-opt'] - assert choices_without_help(cli, [], '') == ['asub'] - assert choices_without_help(cli, ['asub'], '-') == ['--asub-opt'] - assert choices_without_help(cli, ['asub'], '') == ['bsub'] - assert choices_without_help(cli, ['asub', 'bsub'], '-') == ['--bsub-opt'] - assert choices_without_help(cli, ['asub', 'bsub'], '') == ['csub'] - assert choices_without_help(cli, ['asub', 'bsub', 'csub'], '-') == ['--csub-opt', '--csub', '--search-color'] - assert choices_without_help(cli, ['asub', 'bsub', 'csub', '--csub-opt'], '') == CSUB_OPT_CHOICES - assert choices_without_help(cli, ['asub', 'bsub', 'csub'], '--csub') == ['--csub-opt', '--csub'] - assert choices_without_help(cli, ['asub', 'bsub', 'csub', '--csub'], '') == CSUB_CHOICES - assert choices_without_help(cli, ['asub', 'bsub', 'csub', '--csub-opt'], 'f') == ['foo'] - assert choices_without_help(cli, ['asub', 'bsub', 'csub'], '') == COLORS - assert choices_without_help(cli, ['asub', 'bsub', 'csub'], 'b') == ['blue'] - assert choices_without_help(cli, ['asub', 'bsub', 'csub', '--search-color'], 'een') == ['green'] + assert choices_without_help(cli, [], "-") == ["--cli-opt"] + assert choices_without_help(cli, [], "") == ["asub"] + assert choices_without_help(cli, ["asub"], "-") == ["--asub-opt"] + assert choices_without_help(cli, ["asub"], "") == ["bsub"] + assert choices_without_help(cli, ["asub", "bsub"], "-") == ["--bsub-opt"] + assert choices_without_help(cli, ["asub", "bsub"], "") == ["csub"] + assert choices_without_help(cli, ["asub", "bsub", "csub"], "-") == [ + "--csub-opt", + "--csub", + "--search-color", + ] + assert ( + choices_without_help(cli, ["asub", "bsub", "csub", "--csub-opt"], "") + == CSUB_OPT_CHOICES + ) + assert choices_without_help(cli, ["asub", "bsub", "csub"], "--csub") == [ + "--csub-opt", + "--csub", + ] + assert ( + choices_without_help(cli, ["asub", "bsub", "csub", "--csub"], "") + == CSUB_CHOICES + ) + assert choices_without_help(cli, ["asub", "bsub", "csub", "--csub-opt"], "f") == [ + "foo" + ] + assert choices_without_help(cli, ["asub", "bsub", "csub"], "") == COLORS + assert choices_without_help(cli, ["asub", "bsub", "csub"], "b") == ["blue"] + assert choices_without_help( + cli, ["asub", "bsub", "csub", "--search-color"], "een" + ) == ["green"] def test_chaining(): - @click.group('cli', chain=True) - @click.option('--cli-opt') - @click.argument('arg', type=click.Choice(['cliarg1', 'cliarg2'])) + @click.group("cli", chain=True) + @click.option("--cli-opt") + @click.argument("arg", type=click.Choice(["cliarg1", "cliarg2"])) def cli(cli_opt, arg): pass @cli.command() - @click.option('--asub-opt') + @click.option("--asub-opt") def asub(asub_opt): pass - @cli.command(help='bsub help') - @click.option('--bsub-opt') - @click.argument('arg', type=click.Choice(['arg1', 'arg2'])) + @cli.command(help="bsub help") + @click.option("--bsub-opt") + @click.argument("arg", type=click.Choice(["arg1", "arg2"])) def bsub(bsub_opt, arg): pass @cli.command() - @click.option('--csub-opt') - @click.argument('arg', type=click.Choice(['carg1', 'carg2']), default='carg1') + @click.option("--csub-opt") + @click.argument("arg", type=click.Choice(["carg1", "carg2"]), default="carg1") def csub(csub_opt, arg): pass - assert choices_without_help(cli, [], '-') == ['--cli-opt'] - assert choices_without_help(cli, [], '') == ['cliarg1', 'cliarg2'] - assert choices_without_help(cli, ['cliarg1', 'asub'], '-') == ['--asub-opt'] - assert choices_without_help(cli, ['cliarg1', 'asub'], '') == ['bsub', 'csub'] - assert choices_without_help(cli, ['cliarg1', 'bsub'], '') == ['arg1', 'arg2'] - assert choices_without_help(cli, ['cliarg1', 'asub', '--asub-opt'], '') == [] - assert choices_without_help(cli, ['cliarg1', 'asub', '--asub-opt', '5', 'bsub'], '-') == ['--bsub-opt'] - assert choices_without_help(cli, ['cliarg1', 'asub', 'bsub'], '-') == ['--bsub-opt'] - assert choices_without_help(cli, ['cliarg1', 'asub', 'csub'], '') == ['carg1', 'carg2'] - assert choices_without_help(cli, ['cliarg1', 'bsub', 'arg1', 'csub'], '') == ['carg1', 'carg2'] - assert choices_without_help(cli, ['cliarg1', 'asub', 'csub'], '-') == ['--csub-opt'] - assert choices_with_help(cli, ['cliarg1', 'asub'], 'b') == [('bsub', 'bsub help')] + assert choices_without_help(cli, [], "-") == ["--cli-opt"] + assert choices_without_help(cli, [], "") == ["cliarg1", "cliarg2"] + assert choices_without_help(cli, ["cliarg1", "asub"], "-") == ["--asub-opt"] + assert choices_without_help(cli, ["cliarg1", "asub"], "") == ["bsub", "csub"] + assert choices_without_help(cli, ["cliarg1", "bsub"], "") == ["arg1", "arg2"] + assert choices_without_help(cli, ["cliarg1", "asub", "--asub-opt"], "") == [] + assert choices_without_help( + cli, ["cliarg1", "asub", "--asub-opt", "5", "bsub"], "-" + ) == ["--bsub-opt"] + assert choices_without_help(cli, ["cliarg1", "asub", "bsub"], "-") == ["--bsub-opt"] + assert choices_without_help(cli, ["cliarg1", "asub", "csub"], "") == [ + "carg1", + "carg2", + ] + assert choices_without_help(cli, ["cliarg1", "bsub", "arg1", "csub"], "") == [ + "carg1", + "carg2", + ] + assert choices_without_help(cli, ["cliarg1", "asub", "csub"], "-") == ["--csub-opt"] + assert choices_with_help(cli, ["cliarg1", "asub"], "b") == [("bsub", "bsub help")] def test_argument_choice(): @click.command() - @click.argument('arg1', required=True, type=click.Choice(['arg11', 'arg12'])) - @click.argument('arg2', type=click.Choice(['arg21', 'arg22']), default='arg21') - @click.argument('arg3', type=click.Choice(['arg', 'argument']), default='arg') + @click.argument("arg1", required=True, type=click.Choice(["arg11", "arg12"])) + @click.argument("arg2", type=click.Choice(["arg21", "arg22"]), default="arg21") + @click.argument("arg3", type=click.Choice(["arg", "argument"]), default="arg") def cli(): pass - assert choices_without_help(cli, [], '') == ['arg11', 'arg12'] - assert choices_without_help(cli, [], 'arg') == ['arg11', 'arg12'] - assert choices_without_help(cli, ['arg11'], '') == ['arg21', 'arg22'] - assert choices_without_help(cli, ['arg12', 'arg21'], '') == ['arg', 'argument'] - assert choices_without_help(cli, ['arg12', 'arg21'], 'argu') == ['argument'] + assert choices_without_help(cli, [], "") == ["arg11", "arg12"] + assert choices_without_help(cli, [], "arg") == ["arg11", "arg12"] + assert choices_without_help(cli, ["arg11"], "") == ["arg21", "arg22"] + assert choices_without_help(cli, ["arg12", "arg21"], "") == ["arg", "argument"] + assert choices_without_help(cli, ["arg12", "arg21"], "argu") == ["argument"] def test_option_choice(): @click.command() - @click.option('--opt1', type=click.Choice(['opt11', 'opt12']), help='opt1 help') - @click.option('--opt2', type=click.Choice(['opt21', 'opt22']), default='opt21') - @click.option('--opt3', type=click.Choice(['opt', 'option'])) + @click.option("--opt1", type=click.Choice(["opt11", "opt12"]), help="opt1 help") + @click.option("--opt2", type=click.Choice(["opt21", "opt22"]), default="opt21") + @click.option("--opt3", type=click.Choice(["opt", "option"])) def cli(): pass - assert choices_with_help(cli, [], '-') == [('--opt1', 'opt1 help'), - ('--opt2', None), - ('--opt3', None)] - assert choices_without_help(cli, [], '--opt') == ['--opt1', '--opt2', '--opt3'] - assert choices_without_help(cli, [], '--opt1=') == ['opt11', 'opt12'] - assert choices_without_help(cli, [], '--opt2=') == ['opt21', 'opt22'] - assert choices_without_help(cli, ['--opt2'], '=') == ['opt21', 'opt22'] - assert choices_without_help(cli, ['--opt2', '='], 'opt') == ['opt21', 'opt22'] - assert choices_without_help(cli, ['--opt1'], '') == ['opt11', 'opt12'] - assert choices_without_help(cli, ['--opt2'], '') == ['opt21', 'opt22'] - assert choices_without_help(cli, ['--opt1', 'opt11', '--opt2'], '') == ['opt21', 'opt22'] - assert choices_without_help(cli, ['--opt2', 'opt21'], '-') == ['--opt1', '--opt3'] - assert choices_without_help(cli, ['--opt1', 'opt11'], '-') == ['--opt2', '--opt3'] - assert choices_without_help(cli, ['--opt1'], 'opt') == ['opt11', 'opt12'] - assert choices_without_help(cli, ['--opt3'], 'opti') == ['option'] - - assert choices_without_help(cli, ['--opt1', 'invalid_opt'], '-') == ['--opt2', '--opt3'] + assert choices_with_help(cli, [], "-") == [ + ("--opt1", "opt1 help"), + ("--opt2", None), + ("--opt3", None), + ] + assert choices_without_help(cli, [], "--opt") == ["--opt1", "--opt2", "--opt3"] + assert choices_without_help(cli, [], "--opt1=") == ["opt11", "opt12"] + assert choices_without_help(cli, [], "--opt2=") == ["opt21", "opt22"] + assert choices_without_help(cli, ["--opt2"], "=") == ["opt21", "opt22"] + assert choices_without_help(cli, ["--opt2", "="], "opt") == ["opt21", "opt22"] + assert choices_without_help(cli, ["--opt1"], "") == ["opt11", "opt12"] + assert choices_without_help(cli, ["--opt2"], "") == ["opt21", "opt22"] + assert choices_without_help(cli, ["--opt1", "opt11", "--opt2"], "") == [ + "opt21", + "opt22", + ] + assert choices_without_help(cli, ["--opt2", "opt21"], "-") == ["--opt1", "--opt3"] + assert choices_without_help(cli, ["--opt1", "opt11"], "-") == ["--opt2", "--opt3"] + assert choices_without_help(cli, ["--opt1"], "opt") == ["opt11", "opt12"] + assert choices_without_help(cli, ["--opt3"], "opti") == ["option"] + + assert choices_without_help(cli, ["--opt1", "invalid_opt"], "-") == [ + "--opt2", + "--opt3", + ] def test_option_and_arg_choice(): @click.command() - @click.option('--opt1', type=click.Choice(['opt11', 'opt12'])) - @click.argument('arg1', required=False, type=click.Choice(['arg11', 'arg12'])) - @click.option('--opt2', type=click.Choice(['opt21', 'opt22'])) + @click.option("--opt1", type=click.Choice(["opt11", "opt12"])) + @click.argument("arg1", required=False, type=click.Choice(["arg11", "arg12"])) + @click.option("--opt2", type=click.Choice(["opt21", "opt22"])) def cli(): pass - assert choices_without_help(cli, ['--opt1'], '') == ['opt11', 'opt12'] - assert choices_without_help(cli, [''], '--opt1=') == ['opt11', 'opt12'] - assert choices_without_help(cli, [], '') == ['arg11', 'arg12'] - assert choices_without_help(cli, ['--opt2'], '') == ['opt21', 'opt22'] - assert choices_without_help(cli, ['arg11'], '--opt') == ['--opt1', '--opt2'] - assert choices_without_help(cli, [], '--opt') == ['--opt1', '--opt2'] + assert choices_without_help(cli, ["--opt1"], "") == ["opt11", "opt12"] + assert choices_without_help(cli, [""], "--opt1=") == ["opt11", "opt12"] + assert choices_without_help(cli, [], "") == ["arg11", "arg12"] + assert choices_without_help(cli, ["--opt2"], "") == ["opt21", "opt22"] + assert choices_without_help(cli, ["arg11"], "--opt") == ["--opt1", "--opt2"] + assert choices_without_help(cli, [], "--opt") == ["--opt1", "--opt2"] def test_boolean_flag_choice(): @click.command() - @click.option('--shout/--no-shout', default=False) - @click.argument('arg', required=False, type=click.Choice(['arg1', 'arg2'])) + @click.option("--shout/--no-shout", default=False) + @click.argument("arg", required=False, type=click.Choice(["arg1", "arg2"])) def cli(local_opt): pass - assert choices_without_help(cli, [], '-') == ['--shout', '--no-shout'] - assert choices_without_help(cli, ['--shout'], '') == ['arg1', 'arg2'] + assert choices_without_help(cli, [], "-") == ["--shout", "--no-shout"] + assert choices_without_help(cli, ["--shout"], "") == ["arg1", "arg2"] def test_multi_value_option_choice(): @click.command() - @click.option('--pos', nargs=2, type=click.Choice(['pos1', 'pos2'])) - @click.argument('arg', required=False, type=click.Choice(['arg1', 'arg2'])) + @click.option("--pos", nargs=2, type=click.Choice(["pos1", "pos2"])) + @click.argument("arg", required=False, type=click.Choice(["arg1", "arg2"])) def cli(local_opt): pass - assert choices_without_help(cli, ['--pos'], '') == ['pos1', 'pos2'] - assert choices_without_help(cli, ['--pos', 'pos1'], '') == ['pos1', 'pos2'] - assert choices_without_help(cli, ['--pos', 'pos1', 'pos2'], '') == ['arg1', 'arg2'] - assert choices_without_help(cli, ['--pos', 'pos1', 'pos2', 'arg1'], '') == [] + assert choices_without_help(cli, ["--pos"], "") == ["pos1", "pos2"] + assert choices_without_help(cli, ["--pos", "pos1"], "") == ["pos1", "pos2"] + assert choices_without_help(cli, ["--pos", "pos1", "pos2"], "") == ["arg1", "arg2"] + assert choices_without_help(cli, ["--pos", "pos1", "pos2", "arg1"], "") == [] def test_multi_option_choice(): @click.command() - @click.option('--message', '-m', multiple=True, type=click.Choice(['m1', 'm2'])) - @click.argument('arg', required=False, type=click.Choice(['arg1', 'arg2'])) + @click.option("--message", "-m", multiple=True, type=click.Choice(["m1", "m2"])) + @click.argument("arg", required=False, type=click.Choice(["arg1", "arg2"])) def cli(local_opt): pass - assert choices_without_help(cli, ['-m'], '') == ['m1', 'm2'] - assert choices_without_help(cli, ['-m', 'm1', '-m'], '') == ['m1', 'm2'] - assert choices_without_help(cli, ['-m', 'm1'], '') == ['arg1', 'arg2'] + assert choices_without_help(cli, ["-m"], "") == ["m1", "m2"] + assert choices_without_help(cli, ["-m", "m1", "-m"], "") == ["m1", "m2"] + assert choices_without_help(cli, ["-m", "m1"], "") == ["arg1", "arg2"] def test_variadic_argument_choice(): @click.command() - @click.option('--opt', type=click.Choice(['opt1', 'opt2'])) - @click.argument('src', nargs=-1, type=click.Choice(['src1', 'src2'])) + @click.option("--opt", type=click.Choice(["opt1", "opt2"])) + @click.argument("src", nargs=-1, type=click.Choice(["src1", "src2"])) def cli(local_opt): pass - assert choices_without_help(cli, ['src1', 'src2'], '') == ['src1', 'src2'] - assert choices_without_help(cli, ['src1', 'src2'], '--o') == ['--opt'] - assert choices_without_help(cli, ['src1', 'src2', '--opt'], '') == ['opt1', 'opt2'] - assert choices_without_help(cli, ['src1', 'src2'], '') == ['src1', 'src2'] + assert choices_without_help(cli, ["src1", "src2"], "") == ["src1", "src2"] + assert choices_without_help(cli, ["src1", "src2"], "--o") == ["--opt"] + assert choices_without_help(cli, ["src1", "src2", "--opt"], "") == ["opt1", "opt2"] + assert choices_without_help(cli, ["src1", "src2"], "") == ["src1", "src2"] def test_variadic_argument_complete(): - def _complete(ctx, args, incomplete): - return ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu', 'vwx', 'yz'] + return ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz"] @click.group() def entrypoint(): pass @click.command() - @click.option('--opt', autocompletion=_complete) - @click.argument('arg', nargs=-1) + @click.option("--opt", autocompletion=_complete) + @click.argument("arg", nargs=-1) def subcommand(opt, arg): pass entrypoint.add_command(subcommand) - assert choices_without_help(entrypoint, ['subcommand', '--opt'], '') == _complete(0,0,0) - assert choices_without_help(entrypoint, ['subcommand', 'whatever', '--opt'], '') == _complete(0,0,0) - assert choices_without_help(entrypoint, ['subcommand', 'whatever', '--opt', 'abc'], '') == [] + assert choices_without_help(entrypoint, ["subcommand", "--opt"], "") == _complete( + 0, 0, 0 + ) + assert choices_without_help( + entrypoint, ["subcommand", "whatever", "--opt"], "" + ) == _complete(0, 0, 0) + assert ( + choices_without_help(entrypoint, ["subcommand", "whatever", "--opt", "abc"], "") + == [] + ) def test_long_chain_choice(): @@ -303,19 +344,25 @@ def cli(): pass @cli.group() - @click.option('--sub-opt', type=click.Choice(['subopt1', 'subopt2'])) - @click.argument('sub-arg', required=False, type=click.Choice(['subarg1', 'subarg2'])) + @click.option("--sub-opt", type=click.Choice(["subopt1", "subopt2"])) + @click.argument( + "sub-arg", required=False, type=click.Choice(["subarg1", "subarg2"]) + ) def sub(sub_opt, sub_arg): pass - @sub.command(short_help='bsub help') - @click.option('--bsub-opt', type=click.Choice(['bsubopt1', 'bsubopt2'])) - @click.argument('bsub-arg1', required=False, type=click.Choice(['bsubarg1', 'bsubarg2'])) - @click.argument('bbsub-arg2', required=False, type=click.Choice(['bbsubarg1', 'bbsubarg2'])) + @sub.command(short_help="bsub help") + @click.option("--bsub-opt", type=click.Choice(["bsubopt1", "bsubopt2"])) + @click.argument( + "bsub-arg1", required=False, type=click.Choice(["bsubarg1", "bsubarg2"]) + ) + @click.argument( + "bbsub-arg2", required=False, type=click.Choice(["bbsubarg1", "bbsubarg2"]) + ) def bsub(bsub_opt): pass - @sub.group('csub') + @sub.group("csub") def csub(): pass @@ -323,24 +370,42 @@ def csub(): def dsub(): pass - assert choices_with_help(cli, ['sub', 'subarg1'], '') == [('bsub', 'bsub help'), ('csub', '')] - assert choices_without_help(cli, ['sub'], '') == ['subarg1', 'subarg2'] - assert choices_without_help(cli, ['sub', '--sub-opt'], '') == ['subopt1', 'subopt2'] - assert choices_without_help(cli, ['sub', '--sub-opt', 'subopt1'], '') == \ - ['subarg1', 'subarg2'] - assert choices_without_help(cli, - ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub'], '-') == ['--bsub-opt'] - assert choices_without_help(cli, - ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub'], '') == ['bsubarg1', 'bsubarg2'] - assert choices_without_help(cli, - ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub', '--bsub-opt'], '') == \ - ['bsubopt1', 'bsubopt2'] - assert choices_without_help(cli, - ['sub', '--sub-opt', 'subopt1', 'subarg1', 'bsub', '--bsub-opt', 'bsubopt1', 'bsubarg1'], - '') == ['bbsubarg1', 'bbsubarg2'] - assert choices_without_help(cli, - ['sub', '--sub-opt', 'subopt1', 'subarg1', 'csub'], - '') == ['dsub'] + assert choices_with_help(cli, ["sub", "subarg1"], "") == [ + ("bsub", "bsub help"), + ("csub", ""), + ] + assert choices_without_help(cli, ["sub"], "") == ["subarg1", "subarg2"] + assert choices_without_help(cli, ["sub", "--sub-opt"], "") == ["subopt1", "subopt2"] + assert choices_without_help(cli, ["sub", "--sub-opt", "subopt1"], "") == [ + "subarg1", + "subarg2", + ] + assert choices_without_help( + cli, ["sub", "--sub-opt", "subopt1", "subarg1", "bsub"], "-" + ) == ["--bsub-opt"] + assert choices_without_help( + cli, ["sub", "--sub-opt", "subopt1", "subarg1", "bsub"], "" + ) == ["bsubarg1", "bsubarg2"] + assert choices_without_help( + cli, ["sub", "--sub-opt", "subopt1", "subarg1", "bsub", "--bsub-opt"], "" + ) == ["bsubopt1", "bsubopt2"] + assert choices_without_help( + cli, + [ + "sub", + "--sub-opt", + "subopt1", + "subarg1", + "bsub", + "--bsub-opt", + "bsubopt1", + "bsubarg1", + ], + "", + ) == ["bbsubarg1", "bbsubarg2"] + assert choices_without_help( + cli, ["sub", "--sub-opt", "subopt1", "subarg1", "csub"], "" + ) == ["dsub"] def test_chained_multi(): @@ -368,16 +433,16 @@ def dsub(): def esub(): pass - assert choices_without_help(cli, ['sub'], '') == ['bsub', 'csub'] - assert choices_without_help(cli, ['sub'], 'c') == ['csub'] - assert choices_without_help(cli, ['sub', 'csub'], '') == ['dsub', 'esub'] - assert choices_without_help(cli, ['sub', 'csub', 'dsub'], '') == ['esub'] + assert choices_without_help(cli, ["sub"], "") == ["bsub", "csub"] + assert choices_without_help(cli, ["sub"], "c") == ["csub"] + assert choices_without_help(cli, ["sub", "csub"], "") == ["dsub", "esub"] + assert choices_without_help(cli, ["sub", "csub", "dsub"], "") == ["esub"] def test_hidden(): @click.group() - @click.option('--name', hidden=True) - @click.option('--choices', type=click.Choice([1, 2]), hidden=True) + @click.option("--name", hidden=True) + @click.option("--choices", type=click.Choice([1, 2]), hidden=True) def cli(name): pass @@ -394,20 +459,20 @@ def asub(): pass @cli.command(hidden=True) - @click.option('--hname') + @click.option("--hname") def hsub(): pass - assert choices_without_help(cli, [], '--n') == [] - assert choices_without_help(cli, [], '--c') == [] + assert choices_without_help(cli, [], "--n") == [] + assert choices_without_help(cli, [], "--c") == [] # If the user exactly types out the hidden param, complete its options. - assert choices_without_help(cli, ['--choices'], '') == [1, 2] - assert choices_without_help(cli, [], '') == ['asub'] - assert choices_without_help(cli, [], '') == ['asub'] - assert choices_without_help(cli, [], 'h') == [] + assert choices_without_help(cli, ["--choices"], "") == [1, 2] + assert choices_without_help(cli, [], "") == ["asub"] + assert choices_without_help(cli, [], "") == ["asub"] + assert choices_without_help(cli, [], "h") == [] # If the user exactly types out the hidden command, complete its subcommands. - assert choices_without_help(cli, ['hgroup'], '') == ['hgroupsub'] - assert choices_without_help(cli, ['hsub'], '--h') == ['--hname'] + assert choices_without_help(cli, ["hgroup"], "") == ["hgroupsub"] + assert choices_without_help(cli, ["hsub"], "--h") == ["--hname"] @pytest.mark.parametrize( diff --git a/tests/test_basic.py b/tests/test_basic.py index 939c2054d..82c7e5a75 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -9,18 +9,18 @@ def test_basic_functionality(runner): @click.command() def cli(): """Hello World!""" - click.echo('I EXECUTED') + click.echo("I EXECUTED") - result = runner.invoke(cli, ['--help']) + result = runner.invoke(cli, ["--help"]) assert not result.exception - assert 'Hello World!' in result.output - assert 'Show this message and exit.' in result.output + assert "Hello World!" in result.output + assert "Show this message and exit." in result.output assert result.exit_code == 0 - assert 'I EXECUTED' not in result.output + assert "I EXECUTED" not in result.output result = runner.invoke(cli, []) assert not result.exception - assert 'I EXECUTED' in result.output + assert "I EXECUTED" in result.output assert result.exit_code == 0 @@ -37,9 +37,9 @@ def group(): def subcommand(): pass - assert repr(command) == '' - assert repr(group) == '' - assert repr(subcommand) == '' + assert repr(command) == "" + assert repr(group) == "" + assert repr(subcommand) == "" def test_return_values(): @@ -47,7 +47,7 @@ def test_return_values(): def cli(): return 42 - with cli.make_context('foo', []) as ctx: + with cli.make_context("foo", []) as ctx: rv = cli.invoke(ctx) assert rv is 42 @@ -56,408 +56,420 @@ def test_basic_group(runner): @click.group() def cli(): """This is the root.""" - click.echo('ROOT EXECUTED') + click.echo("ROOT EXECUTED") @cli.command() def subcommand(): """This is a subcommand.""" - click.echo('SUBCOMMAND EXECUTED') + click.echo("SUBCOMMAND EXECUTED") - result = runner.invoke(cli, ['--help']) + result = runner.invoke(cli, ["--help"]) assert not result.exception - assert 'COMMAND [ARGS]...' in result.output - assert 'This is the root' in result.output - assert 'This is a subcommand.' in result.output + assert "COMMAND [ARGS]..." in result.output + assert "This is the root" in result.output + assert "This is a subcommand." in result.output assert result.exit_code == 0 - assert 'ROOT EXECUTED' not in result.output + assert "ROOT EXECUTED" not in result.output - result = runner.invoke(cli, ['subcommand']) + result = runner.invoke(cli, ["subcommand"]) assert not result.exception assert result.exit_code == 0 - assert 'ROOT EXECUTED' in result.output - assert 'SUBCOMMAND EXECUTED' in result.output + assert "ROOT EXECUTED" in result.output + assert "SUBCOMMAND EXECUTED" in result.output def test_basic_option(runner): @click.command() - @click.option('--foo', default='no value') + @click.option("--foo", default="no value") def cli(foo): - click.echo('FOO:[%s]' % foo) + click.echo("FOO:[%s]" % foo) result = runner.invoke(cli, []) assert not result.exception - assert 'FOO:[no value]' in result.output + assert "FOO:[no value]" in result.output - result = runner.invoke(cli, ['--foo=42']) + result = runner.invoke(cli, ["--foo=42"]) assert not result.exception - assert 'FOO:[42]' in result.output + assert "FOO:[42]" in result.output - result = runner.invoke(cli, ['--foo']) + result = runner.invoke(cli, ["--foo"]) assert result.exception - assert '--foo option requires an argument' in result.output + assert "--foo option requires an argument" in result.output - result = runner.invoke(cli, ['--foo=']) + result = runner.invoke(cli, ["--foo="]) assert not result.exception - assert 'FOO:[]' in result.output + assert "FOO:[]" in result.output - result = runner.invoke(cli, [u'--foo=\N{SNOWMAN}']) + result = runner.invoke(cli, [u"--foo=\N{SNOWMAN}"]) assert not result.exception - assert u'FOO:[\N{SNOWMAN}]' in result.output + assert u"FOO:[\N{SNOWMAN}]" in result.output def test_int_option(runner): @click.command() - @click.option('--foo', default=42) + @click.option("--foo", default=42) def cli(foo): - click.echo('FOO:[%s]' % (foo * 2)) + click.echo("FOO:[%s]" % (foo * 2)) result = runner.invoke(cli, []) assert not result.exception - assert 'FOO:[84]' in result.output + assert "FOO:[84]" in result.output - result = runner.invoke(cli, ['--foo=23']) + result = runner.invoke(cli, ["--foo=23"]) assert not result.exception - assert 'FOO:[46]' in result.output + assert "FOO:[46]" in result.output - result = runner.invoke(cli, ['--foo=bar']) + result = runner.invoke(cli, ["--foo=bar"]) assert result.exception - assert 'Invalid value for "--foo": bar is not a valid integer' \ - in result.output + assert 'Invalid value for "--foo": bar is not a valid integer' in result.output def test_uuid_option(runner): @click.command() - @click.option('--u', default='ba122011-349f-423b-873b-9d6a79c688ab', - type=click.UUID) + @click.option( + "--u", default="ba122011-349f-423b-873b-9d6a79c688ab", type=click.UUID + ) def cli(u): assert type(u) is uuid.UUID - click.echo('U:[%s]' % u) + click.echo("U:[%s]" % u) result = runner.invoke(cli, []) assert not result.exception - assert 'U:[ba122011-349f-423b-873b-9d6a79c688ab]' in result.output + assert "U:[ba122011-349f-423b-873b-9d6a79c688ab]" in result.output - result = runner.invoke(cli, ['--u=821592c1-c50e-4971-9cd6-e89dc6832f86']) + result = runner.invoke(cli, ["--u=821592c1-c50e-4971-9cd6-e89dc6832f86"]) assert not result.exception - assert 'U:[821592c1-c50e-4971-9cd6-e89dc6832f86]' in result.output + assert "U:[821592c1-c50e-4971-9cd6-e89dc6832f86]" in result.output - result = runner.invoke(cli, ['--u=bar']) + result = runner.invoke(cli, ["--u=bar"]) assert result.exception - assert 'Invalid value for "--u": bar is not a valid UUID value' \ - in result.output + assert 'Invalid value for "--u": bar is not a valid UUID value' in result.output def test_float_option(runner): @click.command() - @click.option('--foo', default=42, type=click.FLOAT) + @click.option("--foo", default=42, type=click.FLOAT) def cli(foo): assert type(foo) is float - click.echo('FOO:[%s]' % foo) + click.echo("FOO:[%s]" % foo) result = runner.invoke(cli, []) assert not result.exception - assert 'FOO:[42.0]' in result.output + assert "FOO:[42.0]" in result.output - result = runner.invoke(cli, ['--foo=23.5']) + result = runner.invoke(cli, ["--foo=23.5"]) assert not result.exception - assert 'FOO:[23.5]' in result.output + assert "FOO:[23.5]" in result.output - result = runner.invoke(cli, ['--foo=bar']) + result = runner.invoke(cli, ["--foo=bar"]) assert result.exception - assert 'Invalid value for "--foo": bar is not a valid float' \ - in result.output + assert 'Invalid value for "--foo": bar is not a valid float' in result.output def test_boolean_option(runner): for default in True, False: + @click.command() - @click.option('--with-foo/--without-foo', default=default) + @click.option("--with-foo/--without-foo", default=default) def cli(with_foo): click.echo(with_foo) - result = runner.invoke(cli, ['--with-foo']) + result = runner.invoke(cli, ["--with-foo"]) assert not result.exception - assert result.output == 'True\n' - result = runner.invoke(cli, ['--without-foo']) + assert result.output == "True\n" + result = runner.invoke(cli, ["--without-foo"]) assert not result.exception - assert result.output == 'False\n' + assert result.output == "False\n" result = runner.invoke(cli, []) assert not result.exception - assert result.output == '%s\n' % default + assert result.output == "%s\n" % default for default in True, False: + @click.command() - @click.option('--flag', is_flag=True, default=default) + @click.option("--flag", is_flag=True, default=default) def cli(flag): click.echo(flag) - result = runner.invoke(cli, ['--flag']) + result = runner.invoke(cli, ["--flag"]) assert not result.exception - assert result.output == '%s\n' % (not default) + assert result.output == "%s\n" % (not default) result = runner.invoke(cli, []) assert not result.exception - assert result.output == '%s\n' % (default) + assert result.output == "%s\n" % (default) def test_boolean_conversion(runner): for default in True, False: + @click.command() - @click.option('--flag', default=default, type=bool) + @click.option("--flag", default=default, type=bool) def cli(flag): click.echo(flag) - for value in 'true', 't', '1', 'yes', 'y': - result = runner.invoke(cli, ['--flag', value]) + for value in "true", "t", "1", "yes", "y": + result = runner.invoke(cli, ["--flag", value]) assert not result.exception - assert result.output == 'True\n' + assert result.output == "True\n" - for value in 'false', 'f', '0', 'no', 'n': - result = runner.invoke(cli, ['--flag', value]) + for value in "false", "f", "0", "no", "n": + result = runner.invoke(cli, ["--flag", value]) assert not result.exception - assert result.output == 'False\n' + assert result.output == "False\n" result = runner.invoke(cli, []) assert not result.exception - assert result.output == '%s\n' % default + assert result.output == "%s\n" % default def test_file_option(runner): @click.command() - @click.option('--file', type=click.File('w')) + @click.option("--file", type=click.File("w")) def input(file): - file.write('Hello World!\n') + file.write("Hello World!\n") @click.command() - @click.option('--file', type=click.File('r')) + @click.option("--file", type=click.File("r")) def output(file): click.echo(file.read()) with runner.isolated_filesystem(): - result_in = runner.invoke(input, ['--file=example.txt']) - result_out = runner.invoke(output, ['--file=example.txt']) + result_in = runner.invoke(input, ["--file=example.txt"]) + result_out = runner.invoke(output, ["--file=example.txt"]) assert not result_in.exception - assert result_in.output == '' + assert result_in.output == "" assert not result_out.exception - assert result_out.output == 'Hello World!\n\n' + assert result_out.output == "Hello World!\n\n" def test_file_lazy_mode(runner): do_io = False @click.command() - @click.option('--file', type=click.File('w')) + @click.option("--file", type=click.File("w")) def input(file): if do_io: - file.write('Hello World!\n') + file.write("Hello World!\n") @click.command() - @click.option('--file', type=click.File('r')) + @click.option("--file", type=click.File("r")) def output(file): pass with runner.isolated_filesystem(): - os.mkdir('example.txt') + os.mkdir("example.txt") do_io = True - result_in = runner.invoke(input, ['--file=example.txt']) + result_in = runner.invoke(input, ["--file=example.txt"]) assert result_in.exit_code == 1 do_io = False - result_in = runner.invoke(input, ['--file=example.txt']) + result_in = runner.invoke(input, ["--file=example.txt"]) assert result_in.exit_code == 0 - result_out = runner.invoke(output, ['--file=example.txt']) + result_out = runner.invoke(output, ["--file=example.txt"]) assert result_out.exception @click.command() - @click.option('--file', type=click.File('w', lazy=False)) + @click.option("--file", type=click.File("w", lazy=False)) def input_non_lazy(file): - file.write('Hello World!\n') + file.write("Hello World!\n") with runner.isolated_filesystem(): - os.mkdir('example.txt') - result_in = runner.invoke(input_non_lazy, ['--file=example.txt']) + os.mkdir("example.txt") + result_in = runner.invoke(input_non_lazy, ["--file=example.txt"]) assert result_in.exit_code == 2 - assert 'Invalid value for "--file": Could not open file: example.txt' \ + assert ( + 'Invalid value for "--file": Could not open file: example.txt' in result_in.output + ) def test_path_option(runner): @click.command() - @click.option('-O', type=click.Path(file_okay=False, exists=True, - writable=True)) + @click.option("-O", type=click.Path(file_okay=False, exists=True, writable=True)) def write_to_dir(o): - with open(os.path.join(o, 'foo.txt'), 'wb') as f: - f.write(b'meh\n') + with open(os.path.join(o, "foo.txt"), "wb") as f: + f.write(b"meh\n") with runner.isolated_filesystem(): - os.mkdir('test') + os.mkdir("test") - result = runner.invoke(write_to_dir, ['-O', 'test']) + result = runner.invoke(write_to_dir, ["-O", "test"]) assert not result.exception - with open('test/foo.txt', 'rb') as f: - assert f.read() == b'meh\n' + with open("test/foo.txt", "rb") as f: + assert f.read() == b"meh\n" - result = runner.invoke(write_to_dir, ['-O', 'test/foo.txt']) - assert 'Invalid value for "-O": Directory "test/foo.txt" is a file.' \ + result = runner.invoke(write_to_dir, ["-O", "test/foo.txt"]) + assert ( + 'Invalid value for "-O": Directory "test/foo.txt" is a file.' in result.output + ) @click.command() - @click.option('-f', type=click.Path(exists=True)) + @click.option("-f", type=click.Path(exists=True)) def showtype(f): - click.echo('is_file=%s' % os.path.isfile(f)) - click.echo('is_dir=%s' % os.path.isdir(f)) + click.echo("is_file=%s" % os.path.isfile(f)) + click.echo("is_dir=%s" % os.path.isdir(f)) with runner.isolated_filesystem(): - result = runner.invoke(showtype, ['-f', 'xxx']) - assert 'Error: Invalid value for "-f": Path "xxx" does not exist' \ - in result.output + result = runner.invoke(showtype, ["-f", "xxx"]) + assert ( + 'Error: Invalid value for "-f": Path "xxx" does not exist' in result.output + ) - result = runner.invoke(showtype, ['-f', '.']) - assert 'is_file=False' in result.output - assert 'is_dir=True' in result.output + result = runner.invoke(showtype, ["-f", "."]) + assert "is_file=False" in result.output + assert "is_dir=True" in result.output @click.command() - @click.option('-f', type=click.Path()) + @click.option("-f", type=click.Path()) def exists(f): - click.echo('exists=%s' % os.path.exists(f)) + click.echo("exists=%s" % os.path.exists(f)) with runner.isolated_filesystem(): - result = runner.invoke(exists, ['-f', 'xxx']) - assert 'exists=False' in result.output + result = runner.invoke(exists, ["-f", "xxx"]) + assert "exists=False" in result.output - result = runner.invoke(exists, ['-f', '.']) - assert 'exists=True' in result.output + result = runner.invoke(exists, ["-f", "."]) + assert "exists=True" in result.output def test_choice_option(runner): @click.command() - @click.option('--method', type=click.Choice(['foo', 'bar', 'baz'])) + @click.option("--method", type=click.Choice(["foo", "bar", "baz"])) def cli(method): click.echo(method) - result = runner.invoke(cli, ['--method=foo']) + result = runner.invoke(cli, ["--method=foo"]) assert not result.exception - assert result.output == 'foo\n' + assert result.output == "foo\n" - result = runner.invoke(cli, ['--method=meh']) + result = runner.invoke(cli, ["--method=meh"]) assert result.exit_code == 2 - assert 'Invalid value for "--method": invalid choice: meh. ' \ - '(choose from foo, bar, baz)' in result.output + assert ( + 'Invalid value for "--method": invalid choice: meh. ' + "(choose from foo, bar, baz)" in result.output + ) - result = runner.invoke(cli, ['--help']) - assert '--method [foo|bar|baz]' in result.output + result = runner.invoke(cli, ["--help"]) + assert "--method [foo|bar|baz]" in result.output def test_datetime_option_default(runner): - @click.command() - @click.option('--start_date', type=click.DateTime()) + @click.option("--start_date", type=click.DateTime()) def cli(start_date): - click.echo(start_date.strftime('%Y-%m-%dT%H:%M:%S')) + click.echo(start_date.strftime("%Y-%m-%dT%H:%M:%S")) - result = runner.invoke(cli, ['--start_date=2015-09-29']) + result = runner.invoke(cli, ["--start_date=2015-09-29"]) assert not result.exception - assert result.output == '2015-09-29T00:00:00\n' + assert result.output == "2015-09-29T00:00:00\n" - result = runner.invoke(cli, ['--start_date=2015-09-29T09:11:22']) + result = runner.invoke(cli, ["--start_date=2015-09-29T09:11:22"]) assert not result.exception - assert result.output == '2015-09-29T09:11:22\n' + assert result.output == "2015-09-29T09:11:22\n" - result = runner.invoke(cli, ['--start_date=2015-09']) + result = runner.invoke(cli, ["--start_date=2015-09"]) assert result.exit_code == 2 - assert ('Invalid value for "--start_date": ' - 'invalid datetime format: 2015-09. ' - '(choose from %Y-%m-%d, %Y-%m-%dT%H:%M:%S, %Y-%m-%d %H:%M:%S)' - ) in result.output + assert ( + 'Invalid value for "--start_date": ' + "invalid datetime format: 2015-09. " + "(choose from %Y-%m-%d, %Y-%m-%dT%H:%M:%S, %Y-%m-%d %H:%M:%S)" + ) in result.output - result = runner.invoke(cli, ['--help']) - assert '--start_date [%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]' in result.output + result = runner.invoke(cli, ["--help"]) + assert ( + "--start_date [%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]" in result.output + ) def test_datetime_option_custom(runner): @click.command() - @click.option('--start_date', - type=click.DateTime(formats=['%A %B %d, %Y'])) + @click.option("--start_date", type=click.DateTime(formats=["%A %B %d, %Y"])) def cli(start_date): - click.echo(start_date.strftime('%Y-%m-%dT%H:%M:%S')) + click.echo(start_date.strftime("%Y-%m-%dT%H:%M:%S")) - result = runner.invoke(cli, ['--start_date=Wednesday June 05, 2010']) + result = runner.invoke(cli, ["--start_date=Wednesday June 05, 2010"]) assert not result.exception - assert result.output == '2010-06-05T00:00:00\n' + assert result.output == "2010-06-05T00:00:00\n" def test_int_range_option(runner): @click.command() - @click.option('--x', type=click.IntRange(0, 5)) + @click.option("--x", type=click.IntRange(0, 5)) def cli(x): click.echo(x) - result = runner.invoke(cli, ['--x=5']) + result = runner.invoke(cli, ["--x=5"]) assert not result.exception - assert result.output == '5\n' + assert result.output == "5\n" - result = runner.invoke(cli, ['--x=6']) + result = runner.invoke(cli, ["--x=6"]) assert result.exit_code == 2 - assert 'Invalid value for "--x": 6 is not in the valid range of 0 to 5.\n' \ + assert ( + 'Invalid value for "--x": 6 is not in the valid range of 0 to 5.\n' in result.output + ) @click.command() - @click.option('--x', type=click.IntRange(0, 5, clamp=True)) + @click.option("--x", type=click.IntRange(0, 5, clamp=True)) def clamp(x): click.echo(x) - result = runner.invoke(clamp, ['--x=5']) + result = runner.invoke(clamp, ["--x=5"]) assert not result.exception - assert result.output == '5\n' + assert result.output == "5\n" - result = runner.invoke(clamp, ['--x=6']) + result = runner.invoke(clamp, ["--x=6"]) assert not result.exception - assert result.output == '5\n' + assert result.output == "5\n" - result = runner.invoke(clamp, ['--x=-1']) + result = runner.invoke(clamp, ["--x=-1"]) assert not result.exception - assert result.output == '0\n' + assert result.output == "0\n" def test_float_range_option(runner): @click.command() - @click.option('--x', type=click.FloatRange(0, 5)) + @click.option("--x", type=click.FloatRange(0, 5)) def cli(x): click.echo(x) - result = runner.invoke(cli, ['--x=5.0']) + result = runner.invoke(cli, ["--x=5.0"]) assert not result.exception - assert result.output == '5.0\n' + assert result.output == "5.0\n" - result = runner.invoke(cli, ['--x=6.0']) + result = runner.invoke(cli, ["--x=6.0"]) assert result.exit_code == 2 - assert 'Invalid value for "--x": 6.0 is not in the valid range of 0 to 5.\n' \ + assert ( + 'Invalid value for "--x": 6.0 is not in the valid range of 0 to 5.\n' in result.output + ) @click.command() - @click.option('--x', type=click.FloatRange(0, 5, clamp=True)) + @click.option("--x", type=click.FloatRange(0, 5, clamp=True)) def clamp(x): click.echo(x) - result = runner.invoke(clamp, ['--x=5.0']) + result = runner.invoke(clamp, ["--x=5.0"]) assert not result.exception - assert result.output == '5.0\n' + assert result.output == "5.0\n" - result = runner.invoke(clamp, ['--x=6.0']) + result = runner.invoke(clamp, ["--x=6.0"]) assert not result.exception - assert result.output == '5\n' + assert result.output == "5\n" - result = runner.invoke(clamp, ['--x=-1.0']) + result = runner.invoke(clamp, ["--x=-1.0"]) assert not result.exception - assert result.output == '0\n' + assert result.output == "0\n" def test_required_option(runner): @click.command() - @click.option('--foo', required=True) + @click.option("--foo", required=True) def cli(foo): click.echo(foo) @@ -474,52 +486,50 @@ def memo(ctx, param, value): return value @click.command() - @click.option('--missing', default='missing', - is_eager=False, callback=memo) - @click.option('--eager-flag1', flag_value='eager1', - is_eager=True, callback=memo) - @click.option('--eager-flag2', flag_value='eager2', - is_eager=True, callback=memo) - @click.option('--eager-flag3', flag_value='eager3', - is_eager=True, callback=memo) - @click.option('--normal-flag1', flag_value='normal1', - is_eager=False, callback=memo) - @click.option('--normal-flag2', flag_value='normal2', - is_eager=False, callback=memo) - @click.option('--normal-flag3', flag_value='normal3', - is_eager=False, callback=memo) + @click.option("--missing", default="missing", is_eager=False, callback=memo) + @click.option("--eager-flag1", flag_value="eager1", is_eager=True, callback=memo) + @click.option("--eager-flag2", flag_value="eager2", is_eager=True, callback=memo) + @click.option("--eager-flag3", flag_value="eager3", is_eager=True, callback=memo) + @click.option("--normal-flag1", flag_value="normal1", is_eager=False, callback=memo) + @click.option("--normal-flag2", flag_value="normal2", is_eager=False, callback=memo) + @click.option("--normal-flag3", flag_value="normal3", is_eager=False, callback=memo) def cli(**x): pass - result = runner.invoke(cli, ['--eager-flag2', - '--eager-flag1', - '--normal-flag2', - '--eager-flag3', - '--normal-flag3', - '--normal-flag3', - '--normal-flag1', - '--normal-flag1']) + result = runner.invoke( + cli, + [ + "--eager-flag2", + "--eager-flag1", + "--normal-flag2", + "--eager-flag3", + "--normal-flag3", + "--normal-flag3", + "--normal-flag1", + "--normal-flag1", + ], + ) assert not result.exception assert called == [ - 'eager2', - 'eager1', - 'eager3', - 'normal2', - 'normal3', - 'normal1', - 'missing', + "eager2", + "eager1", + "eager3", + "normal2", + "normal3", + "normal1", + "missing", ] def test_hidden_option(runner): @click.command() - @click.option('--nope', hidden=True) + @click.option("--nope", hidden=True) def cli(nope): click.echo(nope) - result = runner.invoke(cli, ['--help']) + result = runner.invoke(cli, ["--help"]) assert result.exit_code == 0 - assert '--nope' not in result.output + assert "--nope" not in result.output def test_hidden_command(runner): @@ -531,9 +541,9 @@ def cli(): def nope(): pass - result = runner.invoke(cli, ['--help']) + result = runner.invoke(cli, ["--help"]) assert result.exit_code == 0 - assert 'nope' not in result.output + assert "nope" not in result.output def test_hidden_group(runner): @@ -549,7 +559,7 @@ def subgroup(): def nope(): pass - result = runner.invoke(cli, ['--help']) + result = runner.invoke(cli, ["--help"]) assert result.exit_code == 0 - assert 'subgroup' not in result.output - assert 'nope' not in result.output + assert "subgroup" not in result.output + assert "nope" not in result.output diff --git a/tests/test_chain.py b/tests/test_chain.py index 282920cd4..6aa452c4f 100644 --- a/tests/test_chain.py +++ b/tests/test_chain.py @@ -6,10 +6,10 @@ def debug(): - click.echo('%s=%s' % ( - sys._getframe(1).f_code.co_name, - '|'.join(click.get_current_context().args), - )) + click.echo( + "%s=%s" + % (sys._getframe(1).f_code.co_name, "|".join(click.get_current_context().args),) + ) def test_basic_chaining(runner): @@ -17,20 +17,20 @@ def test_basic_chaining(runner): def cli(): pass - @cli.command('sdist') + @cli.command("sdist") def sdist(): - click.echo('sdist called') + click.echo("sdist called") - @cli.command('bdist') + @cli.command("bdist") def bdist(): - click.echo('bdist called') + click.echo("bdist called") - result = runner.invoke(cli, ['bdist', 'sdist', 'bdist']) + result = runner.invoke(cli, ["bdist", "sdist", "bdist"]) assert not result.exception assert result.output.splitlines() == [ - 'bdist called', - 'sdist called', - 'bdist called', + "bdist called", + "sdist called", + "bdist called", ] @@ -40,32 +40,32 @@ def cli(): """ROOT HELP""" pass - @cli.command('sdist') + @cli.command("sdist") def sdist(): """SDIST HELP""" - click.echo('sdist called') + click.echo("sdist called") - @cli.command('bdist') + @cli.command("bdist") def bdist(): """BDIST HELP""" - click.echo('bdist called') + click.echo("bdist called") - result = runner.invoke(cli, ['--help']) + result = runner.invoke(cli, ["--help"]) assert not result.exception - assert 'COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...' in result.output - assert 'ROOT HELP' in result.output + assert "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." in result.output + assert "ROOT HELP" in result.output - result = runner.invoke(cli, ['sdist', '--help']) + result = runner.invoke(cli, ["sdist", "--help"]) assert not result.exception - assert 'SDIST HELP' in result.output + assert "SDIST HELP" in result.output - result = runner.invoke(cli, ['bdist', '--help']) + result = runner.invoke(cli, ["bdist", "--help"]) assert not result.exception - assert 'BDIST HELP' in result.output + assert "BDIST HELP" in result.output - result = runner.invoke(cli, ['bdist', 'sdist', '--help']) + result = runner.invoke(cli, ["bdist", "sdist", "--help"]) assert not result.exception - assert 'SDIST HELP' in result.output + assert "SDIST HELP" in result.output def test_chaining_with_options(runner): @@ -73,21 +73,21 @@ def test_chaining_with_options(runner): def cli(): pass - @cli.command('sdist') - @click.option('--format') + @cli.command("sdist") + @click.option("--format") def sdist(format): - click.echo('sdist called %s' % format) + click.echo("sdist called %s" % format) - @cli.command('bdist') - @click.option('--format') + @cli.command("bdist") + @click.option("--format") def bdist(format): - click.echo('bdist called %s' % format) + click.echo("bdist called %s" % format) - result = runner.invoke(cli, ['bdist', '--format=1', 'sdist', '--format=2']) + result = runner.invoke(cli, ["bdist", "--format=1", "sdist", "--format=2"]) assert not result.exception assert result.output.splitlines() == [ - 'bdist called 1', - 'sdist called 2', + "bdist called 1", + "sdist called 2", ] @@ -96,72 +96,73 @@ def test_chaining_with_arguments(runner): def cli(): pass - @cli.command('sdist') - @click.argument('format') + @cli.command("sdist") + @click.argument("format") def sdist(format): - click.echo('sdist called %s' % format) + click.echo("sdist called %s" % format) - @cli.command('bdist') - @click.argument('format') + @cli.command("bdist") + @click.argument("format") def bdist(format): - click.echo('bdist called %s' % format) + click.echo("bdist called %s" % format) - result = runner.invoke(cli, ['bdist', '1', 'sdist', '2']) + result = runner.invoke(cli, ["bdist", "1", "sdist", "2"]) assert not result.exception assert result.output.splitlines() == [ - 'bdist called 1', - 'sdist called 2', + "bdist called 1", + "sdist called 2", ] def test_pipeline(runner): @click.group(chain=True, invoke_without_command=True) - @click.option('-i', '--input', type=click.File('r')) + @click.option("-i", "--input", type=click.File("r")) def cli(input): pass @cli.resultcallback() def process_pipeline(processors, input): - iterator = (x.rstrip('\r\n') for x in input) + iterator = (x.rstrip("\r\n") for x in input) for processor in processors: iterator = processor(iterator) for item in iterator: click.echo(item) - @cli.command('uppercase') + @cli.command("uppercase") def make_uppercase(): def processor(iterator): for line in iterator: yield line.upper() + return processor - @cli.command('strip') + @cli.command("strip") def make_strip(): def processor(iterator): for line in iterator: yield line.strip() + return processor - result = runner.invoke(cli, ['-i', '-'], input='foo\nbar') + result = runner.invoke(cli, ["-i", "-"], input="foo\nbar") assert not result.exception assert result.output.splitlines() == [ - 'foo', - 'bar', + "foo", + "bar", ] - result = runner.invoke(cli, ['-i', '-', 'strip'], input='foo \n bar') + result = runner.invoke(cli, ["-i", "-", "strip"], input="foo \n bar") assert not result.exception assert result.output.splitlines() == [ - 'foo', - 'bar', + "foo", + "bar", ] - result = runner.invoke(cli, ['-i', '-', 'strip', 'uppercase'], - input='foo \n bar') + result = runner.invoke(cli, ["-i", "-", "strip", "uppercase"], input="foo \n bar") assert not result.exception assert result.output.splitlines() == [ - 'FOO', - 'BAR', + "FOO", + "BAR", ] @@ -182,43 +183,45 @@ def b(): def c(): debug() - result = runner.invoke(cli, ['a', 'b', 'c']) + result = runner.invoke(cli, ["a", "b", "c"]) assert not result.exception assert result.output.splitlines() == [ - 'cli=', - 'a=', - 'b=', - 'c=', + "cli=", + "a=", + "b=", + "c=", ] def test_multicommand_arg_behavior(runner): with pytest.raises(RuntimeError): + @click.group(chain=True) - @click.argument('forbidden', required=False) + @click.argument("forbidden", required=False) def bad_cli(): pass with pytest.raises(RuntimeError): + @click.group(chain=True) - @click.argument('forbidden', nargs=-1) + @click.argument("forbidden", nargs=-1) def bad_cli2(): pass @click.group(chain=True) - @click.argument('arg') + @click.argument("arg") def cli(arg): - click.echo('cli:%s' % arg) + click.echo("cli:%s" % arg) @cli.command() def a(): - click.echo('a') + click.echo("a") - result = runner.invoke(cli, ['foo', 'a']) + result = runner.invoke(cli, ["foo", "a"]) assert not result.exception assert result.output.splitlines() == [ - 'cli:foo', - 'a', + "cli:foo", + "a", ] @@ -244,11 +247,11 @@ def l2b(): def l1b(): debug() - result = runner.invoke(cli, ['l1a', 'l2a', 'l1b']) + result = runner.invoke(cli, ["l1a", "l2a", "l1b"]) assert not result.exception assert result.output.splitlines() == [ - 'cli=', - 'l1a=', - 'l2a=', - 'l1b=', + "cli=", + "l1a=", + "l2a=", + "l1b=", ] diff --git a/tests/test_commands.py b/tests/test_commands.py index aa96e71e2..d53b724ab 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -11,33 +11,33 @@ def cli(ctx): return ctx.invoke(other_cmd, arg=42) @click.command() - @click.argument('arg', type=click.INT) + @click.argument("arg", type=click.INT) def other_cmd(arg): click.echo(arg) result = runner.invoke(cli, []) assert not result.exception - assert result.output == '42\n' + assert result.output == "42\n" def test_other_command_forward(runner): cli = click.Group() @cli.command() - @click.option('--count', default=1) + @click.option("--count", default=1) def test(count): - click.echo('Count: %d' % count) + click.echo("Count: %d" % count) @cli.command() - @click.option('--count', default=1) + @click.option("--count", default=1) @click.pass_context def dist(ctx, count): ctx.forward(test) ctx.invoke(test, count=42) - result = runner.invoke(cli, ['dist']) + result = runner.invoke(cli, ["dist"]) assert not result.exception - assert result.output == 'Count: 1\nCount: 42\n' + assert result.output == "Count: 1\nCount: 42\n" def test_auto_shorthelp(runner): @@ -58,13 +58,17 @@ def long(): """This is a long text that is too long to show as short help and will be truncated instead.""" - result = runner.invoke(cli, ['--help']) - assert re.search( - r'Commands:\n\s+' - r'long\s+This is a long text that is too long to show as short help\.\.\.\n\s+' - r'short\s+This is a short text\.\n\s+' - r'special-chars\s+Login and store the token in ~/.netrc\.\s*', - result.output) is not None + result = runner.invoke(cli, ["--help"]) + assert ( + re.search( + r"Commands:\n\s+" + r"long\s+This is a long text that is too long to show as short help\.\.\.\n\s+" + r"short\s+This is a short text\.\n\s+" + r"special-chars\s+Login and store the token in ~/.netrc\.\s*", + result.output, + ) + is not None + ) def test_no_args_is_help(runner): @@ -74,7 +78,7 @@ def cli(): result = runner.invoke(cli, []) assert result.exit_code == 0 - assert 'Show this message and exit.' in result.output + assert "Show this message and exit." in result.output def test_default_maps(runner): @@ -83,43 +87,41 @@ def cli(): pass @cli.command() - @click.option('--name', default='normal') + @click.option("--name", default="normal") def foo(name): click.echo(name) - result = runner.invoke(cli, ['foo'], default_map={ - 'foo': {'name': 'changed'} - }) + result = runner.invoke(cli, ["foo"], default_map={"foo": {"name": "changed"}}) assert not result.exception - assert result.output == 'changed\n' + assert result.output == "changed\n" def test_group_with_args(runner): @click.group() - @click.argument('obj') + @click.argument("obj") def cli(obj): - click.echo('obj=%s' % obj) + click.echo("obj=%s" % obj) @cli.command() def move(): - click.echo('move') + click.echo("move") result = runner.invoke(cli, []) assert result.exit_code == 0 - assert 'Show this message and exit.' in result.output + assert "Show this message and exit." in result.output - result = runner.invoke(cli, ['obj1']) + result = runner.invoke(cli, ["obj1"]) assert result.exit_code == 2 - assert 'Error: Missing command.' in result.output + assert "Error: Missing command." in result.output - result = runner.invoke(cli, ['obj1', '--help']) + result = runner.invoke(cli, ["obj1", "--help"]) assert result.exit_code == 0 - assert 'Show this message and exit.' in result.output + assert "Show this message and exit." in result.output - result = runner.invoke(cli, ['obj1', 'move']) + result = runner.invoke(cli, ["obj1", "move"]) assert result.exit_code == 0 - assert result.output == 'obj=obj1\nmove\n' + assert result.output == "obj=obj1\nmove\n" def test_base_command(runner): @@ -130,7 +132,6 @@ def cli(): pass class OptParseCommand(click.BaseCommand): - def __init__(self, name, parser, callback): click.BaseCommand.__init__(self, name) self.parser = parser @@ -153,58 +154,67 @@ def get_help(self, ctx): def invoke(self, ctx): ctx.invoke(self.callback, ctx.args, **ctx.params) - parser = optparse.OptionParser(usage='Usage: foo test [OPTIONS]') - parser.add_option("-f", "--file", dest="filename", - help="write report to FILE", metavar="FILE") - parser.add_option("-q", "--quiet", - action="store_false", dest="verbose", default=True, - help="don't print status messages to stdout") + parser = optparse.OptionParser(usage="Usage: foo test [OPTIONS]") + parser.add_option( + "-f", "--file", dest="filename", help="write report to FILE", metavar="FILE" + ) + parser.add_option( + "-q", + "--quiet", + action="store_false", + dest="verbose", + default=True, + help="don't print status messages to stdout", + ) def test_callback(args, filename, verbose): - click.echo(' '.join(args)) + click.echo(" ".join(args)) click.echo(filename) click.echo(verbose) - cli.add_command(OptParseCommand('test', parser, test_callback)) - result = runner.invoke(cli, ['test', '-f', 'test.txt', '-q', - 'whatever.txt', 'whateverelse.txt']) + cli.add_command(OptParseCommand("test", parser, test_callback)) + + result = runner.invoke( + cli, ["test", "-f", "test.txt", "-q", "whatever.txt", "whateverelse.txt"] + ) assert not result.exception assert result.output.splitlines() == [ - 'whatever.txt whateverelse.txt', - 'test.txt', - 'False', + "whatever.txt whateverelse.txt", + "test.txt", + "False", ] - result = runner.invoke(cli, ['test', '--help']) + result = runner.invoke(cli, ["test", "--help"]) assert not result.exception assert result.output.splitlines() == [ - 'Usage: foo test [OPTIONS]', - '', - 'Options:', - ' -h, --help show this help message and exit', - ' -f FILE, --file=FILE write report to FILE', - ' -q, --quiet don\'t print status messages to stdout', + "Usage: foo test [OPTIONS]", + "", + "Options:", + " -h, --help show this help message and exit", + " -f FILE, --file=FILE write report to FILE", + " -q, --quiet don't print status messages to stdout", ] def test_object_propagation(runner): for chain in False, True: + @click.group(chain=chain) - @click.option('--debug/--no-debug', default=False) + @click.option("--debug/--no-debug", default=False) @click.pass_context def cli(ctx, debug): if ctx.obj is None: ctx.obj = {} - ctx.obj['DEBUG'] = debug + ctx.obj["DEBUG"] = debug @cli.command() @click.pass_context def sync(ctx): - click.echo('Debug is %s' % (ctx.obj['DEBUG'] and 'on' or 'off')) + click.echo("Debug is %s" % (ctx.obj["DEBUG"] and "on" or "off")) - result = runner.invoke(cli, ['sync']) + result = runner.invoke(cli, ["sync"]) assert result.exception is None - assert result.output == 'Debug is off\n' + assert result.output == "Debug is off\n" def test_other_command_invoke_with_defaults(runner): @@ -214,15 +224,15 @@ def cli(ctx): return ctx.invoke(other_cmd) @click.command() - @click.option('--foo', type=click.INT, default=42) + @click.option("--foo", type=click.INT, default=42) @click.pass_context def other_cmd(ctx, foo): - assert ctx.info_name == 'other-cmd' + assert ctx.info_name == "other-cmd" click.echo(foo) result = runner.invoke(cli, []) assert not result.exception - assert result.output == '42\n' + assert result.output == "42\n" def test_invoked_subcommand(runner): @@ -230,39 +240,37 @@ def test_invoked_subcommand(runner): @click.pass_context def cli(ctx): if ctx.invoked_subcommand is None: - click.echo('no subcommand, use default') + click.echo("no subcommand, use default") ctx.invoke(sync) else: - click.echo('invoke subcommand') + click.echo("invoke subcommand") @cli.command() def sync(): - click.echo('in subcommand') + click.echo("in subcommand") - result = runner.invoke(cli, ['sync']) + result = runner.invoke(cli, ["sync"]) assert not result.exception - assert result.output == 'invoke subcommand\nin subcommand\n' + assert result.output == "invoke subcommand\nin subcommand\n" result = runner.invoke(cli) assert not result.exception - assert result.output == 'no subcommand, use default\nin subcommand\n' + assert result.output == "no subcommand, use default\nin subcommand\n" def test_unprocessed_options(runner): - @click.command(context_settings=dict( - ignore_unknown_options=True - )) - @click.argument('args', nargs=-1, type=click.UNPROCESSED) - @click.option('--verbose', '-v', count=True) + @click.command(context_settings=dict(ignore_unknown_options=True)) + @click.argument("args", nargs=-1, type=click.UNPROCESSED) + @click.option("--verbose", "-v", count=True) def cli(verbose, args): - click.echo('Verbosity: %s' % verbose) - click.echo('Args: %s' % '|'.join(args)) + click.echo("Verbosity: %s" % verbose) + click.echo("Args: %s" % "|".join(args)) - result = runner.invoke(cli, ['-foo', '-vvvvx', '--muhaha', 'x', 'y', '-x']) + result = runner.invoke(cli, ["-foo", "-vvvvx", "--muhaha", "x", "y", "-x"]) assert not result.exception assert result.output.splitlines() == [ - 'Verbosity: 4', - 'Args: -foo|-x|--muhaha|x|y|-x', + "Verbosity: 4", + "Args: -foo|-x|--muhaha|x|y|-x", ] @@ -272,15 +280,15 @@ def cmd_with_help(): """CLI HELP""" pass - result = runner.invoke(cmd_with_help, ['--help']) - assert '(DEPRECATED)' in result.output + result = runner.invoke(cmd_with_help, ["--help"]) + assert "(DEPRECATED)" in result.output @click.command(deprecated=True) def cmd_without_help(): pass - result = runner.invoke(cmd_without_help, ['--help']) - assert '(DEPRECATED)' in result.output + result = runner.invoke(cmd_without_help, ["--help"]) + assert "(DEPRECATED)" in result.output def test_deprecated_in_invocation(runner): @@ -289,4 +297,4 @@ def deprecated_cmd(): debug() result = runner.invoke(deprecated_cmd) - assert 'DeprecationWarning:' in result.output + assert "DeprecationWarning:" in result.output diff --git a/tests/test_compat.py b/tests/test_compat.py index 152301d12..c26834a9a 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -10,29 +10,31 @@ def legacy_callback(ctx, value): return value.upper() @click.command() - @click.option('--foo', callback=legacy_callback) + @click.option("--foo", callback=legacy_callback) def cli(foo): click.echo(foo) - with pytest.warns(Warning, match='Invoked legacy parameter callback'): - result = runner.invoke(cli, ['--foo', 'wat']) + with pytest.warns(Warning, match="Invoked legacy parameter callback"): + result = runner.invoke(cli, ["--foo", "wat"]) assert result.exit_code == 0 - assert 'WAT' in result.output + assert "WAT" in result.output def test_bash_func_name(): from click._bashcomplete import get_completion_script - script = get_completion_script('foo-bar baz_blah', '_COMPLETE_VAR', 'bash').strip() - assert script.startswith('_foo_barbaz_blah_completion()') - assert '_COMPLETE_VAR=complete $1' in script + + script = get_completion_script("foo-bar baz_blah", "_COMPLETE_VAR", "bash").strip() + assert script.startswith("_foo_barbaz_blah_completion()") + assert "_COMPLETE_VAR=complete $1" in script def test_zsh_func_name(): from click._bashcomplete import get_completion_script - script = get_completion_script('foo-bar', '_COMPLETE_VAR', 'zsh').strip() - assert script.startswith('#compdef foo-bar') - assert 'compdef _foo_bar_completion foo-bar;' in script - assert '(( ! $+commands[foo-bar] )) && return 1' in script + + script = get_completion_script("foo-bar", "_COMPLETE_VAR", "zsh").strip() + assert script.startswith("#compdef foo-bar") + assert "compdef _foo_bar_completion foo-bar;" in script + assert "(( ! $+commands[foo-bar] )) && return 1" in script @pytest.mark.xfail(WIN, reason="Jupyter not tested/supported on Windows") diff --git a/tests/test_context.py b/tests/test_context.py index 35933beba..4d131e74f 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -5,7 +5,7 @@ def test_ensure_context_objects(runner): class Foo(object): def __init__(self): - self.title = 'default' + self.title = "default" pass_foo = click.make_pass_decorator(Foo, ensure=True) @@ -19,15 +19,15 @@ def cli(foo): def test(foo): click.echo(foo.title) - result = runner.invoke(cli, ['test']) + result = runner.invoke(cli, ["test"]) assert not result.exception - assert result.output == 'default\n' + assert result.output == "default\n" def test_get_context_objects(runner): class Foo(object): def __init__(self): - self.title = 'default' + self.title = "default" pass_foo = click.make_pass_decorator(Foo, ensure=True) @@ -35,22 +35,22 @@ def __init__(self): @click.pass_context def cli(ctx): ctx.obj = Foo() - ctx.obj.title = 'test' + ctx.obj.title = "test" @cli.command() @pass_foo def test(foo): click.echo(foo.title) - result = runner.invoke(cli, ['test']) + result = runner.invoke(cli, ["test"]) assert not result.exception - assert result.output == 'test\n' + assert result.output == "test\n" def test_get_context_objects_no_ensuring(runner): class Foo(object): def __init__(self): - self.title = 'default' + self.title = "default" pass_foo = click.make_pass_decorator(Foo) @@ -58,16 +58,16 @@ def __init__(self): @click.pass_context def cli(ctx): ctx.obj = Foo() - ctx.obj.title = 'test' + ctx.obj.title = "test" @cli.command() @pass_foo def test(foo): click.echo(foo.title) - result = runner.invoke(cli, ['test']) + result = runner.invoke(cli, ["test"]) assert not result.exception - assert result.output == 'test\n' + assert result.output == "test\n" def test_get_context_objects_missing(runner): @@ -86,11 +86,13 @@ def cli(ctx): def test(foo): click.echo(foo.title) - result = runner.invoke(cli, ['test']) + result = runner.invoke(cli, ["test"]) assert result.exception is not None assert isinstance(result.exception, RuntimeError) - assert "Managed to invoke callback without a context object " \ + assert ( + "Managed to invoke callback without a context object " "of type 'Foo' existing" in str(result.exception) + ) def test_multi_enter(runner): @@ -101,6 +103,7 @@ def test_multi_enter(runner): def cli(ctx): def callback(): called.append(True) + ctx.call_on_close(callback) with ctx: @@ -117,8 +120,8 @@ def test_global_context_object(runner): @click.pass_context def cli(ctx): assert click.get_current_context() is ctx - ctx.obj = 'FOOBAR' - assert click.get_current_context().obj == 'FOOBAR' + ctx.obj = "FOOBAR" + assert click.get_current_context().obj == "FOOBAR" assert click.get_current_context(silent=True) is None runner.invoke(cli, [], catch_exceptions=False) @@ -126,20 +129,20 @@ def cli(ctx): def test_context_meta(runner): - LANG_KEY = __name__ + '.lang' + LANG_KEY = __name__ + ".lang" def set_language(value): click.get_current_context().meta[LANG_KEY] = value def get_language(): - return click.get_current_context().meta.get(LANG_KEY, 'en_US') + return click.get_current_context().meta.get(LANG_KEY, "en_US") @click.command() @click.pass_context def cli(ctx): - assert get_language() == 'en_US' - set_language('de_DE') - assert get_language() == 'de_DE' + assert get_language() == "en_US" + set_language("de_DE") + assert get_language() == "de_DE" runner.invoke(cli, [], catch_exceptions=False) @@ -174,16 +177,16 @@ def test_pass_obj(runner): @click.group() @click.pass_context def cli(ctx): - ctx.obj = 'test' + ctx.obj = "test" @cli.command() @click.pass_obj def test(obj): click.echo(obj) - result = runner.invoke(cli, ['test']) + result = runner.invoke(cli, ["test"]) assert not result.exception - assert result.output == 'test\n' + assert result.output == "test\n" def test_close_before_pop(runner): @@ -192,17 +195,18 @@ def test_close_before_pop(runner): @click.command() @click.pass_context def cli(ctx): - ctx.obj = 'test' + ctx.obj = "test" @ctx.call_on_close def foo(): - assert click.get_current_context().obj == 'test' + assert click.get_current_context().obj == "test" called.append(True) - click.echo('aha!') + + click.echo("aha!") result = runner.invoke(cli, []) assert not result.exception - assert result.output == 'aha!\n' + assert result.output == "aha!\n" assert called == [True] @@ -211,8 +215,9 @@ def test_make_pass_decorator_args(runner): Test to check that make_pass_decorator doesn't consume arguments based on invocation order. """ + class Foo(object): - title = 'foocmd' + title = "foocmd" pass_foo = click.make_pass_decorator(Foo) @@ -233,13 +238,13 @@ def test1(foo, ctx): def test2(ctx, foo): click.echo(foo.title) - result = runner.invoke(cli, ['test1']) + result = runner.invoke(cli, ["test1"]) assert not result.exception - assert result.output == 'foocmd\n' + assert result.output == "foocmd\n" - result = runner.invoke(cli, ['test2']) + result = runner.invoke(cli, ["test2"]) assert not result.exception - assert result.output == 'foocmd\n' + assert result.output == "foocmd\n" def test_exit_not_standalone(): @@ -248,11 +253,11 @@ def test_exit_not_standalone(): def cli(ctx): ctx.exit(1) - assert cli.main([], 'test_exit_not_standalone', standalone_mode=False) == 1 + assert cli.main([], "test_exit_not_standalone", standalone_mode=False) == 1 @click.command() @click.pass_context def cli(ctx): ctx.exit(0) - assert cli.main([], 'test_exit_not_standalone', standalone_mode=False) == 0 + assert cli.main([], "test_exit_not_standalone", standalone_mode=False) == 0 diff --git a/tests/test_defaults.py b/tests/test_defaults.py index dba5a9c85..7b1d7e563 100644 --- a/tests/test_defaults.py +++ b/tests/test_defaults.py @@ -3,20 +3,19 @@ def test_basic_defaults(runner): @click.command() - @click.option('--foo', default=42, type=click.FLOAT) + @click.option("--foo", default=42, type=click.FLOAT) def cli(foo): assert type(foo) is float - click.echo('FOO:[%s]' % foo) + click.echo("FOO:[%s]" % foo) result = runner.invoke(cli, []) assert not result.exception - assert 'FOO:[42.0]' in result.output + assert "FOO:[42.0]" in result.output def test_multiple_defaults(runner): @click.command() - @click.option('--foo', default=[23, 42], type=click.FLOAT, - multiple=True) + @click.option("--foo", default=[23, 42], type=click.FLOAT, multiple=True) def cli(foo): for item in foo: assert type(item) is float @@ -25,22 +24,23 @@ def cli(foo): result = runner.invoke(cli, []) assert not result.exception assert result.output.splitlines() == [ - '23.0', - '42.0', + "23.0", + "42.0", ] def test_nargs_plus_multiple(runner): @click.command() - @click.option('--arg', default=((1, 2), (3, 4)), - nargs=2, multiple=True, type=click.INT) + @click.option( + "--arg", default=((1, 2), (3, 4)), nargs=2, multiple=True, type=click.INT + ) def cli(arg): for item in arg: - click.echo('<%d|%d>' % item) + click.echo("<%d|%d>" % item) result = runner.invoke(cli, []) assert not result.exception assert result.output.splitlines() == [ - '<1|2>', - '<3|4>', + "<1|2>", + "<3|4>", ] diff --git a/tests/test_formatting.py b/tests/test_formatting.py index e4d25717a..30ab288e6 100644 --- a/tests/test_formatting.py +++ b/tests/test_formatting.py @@ -25,28 +25,28 @@ def cli(): that will be rewrapped again. """ - result = runner.invoke(cli, ['--help'], terminal_width=60) + result = runner.invoke(cli, ["--help"], terminal_width=60) assert not result.exception assert result.output.splitlines() == [ - 'Usage: cli [OPTIONS]', - '', - ' First paragraph.', - '', - ' This is a very long second paragraph and not correctly', - ' wrapped but it will be rewrapped.', - '', - ' This is', - ' a paragraph', - ' without rewrapping.', - '', - ' 1', - ' 2', - ' 3', - '', - ' And this is a paragraph that will be rewrapped again.', - '', - 'Options:', - ' --help Show this message and exit.', + "Usage: cli [OPTIONS]", + "", + " First paragraph.", + "", + " This is a very long second paragraph and not correctly", + " wrapped but it will be rewrapped.", + "", + " This is", + " a paragraph", + " without rewrapping.", + "", + " 1", + " 2", + " 3", + "", + " And this is a paragraph that will be rewrapped again.", + "", + "Options:", + " --help Show this message and exit.", ] @@ -62,30 +62,29 @@ def a_very_long(): """ @a_very_long.command() - @click.argument('first') - @click.argument('second') - @click.argument('third') - @click.argument('fourth') - @click.argument('fifth') - @click.argument('sixth') + @click.argument("first") + @click.argument("second") + @click.argument("third") + @click.argument("fourth") + @click.argument("fifth") + @click.argument("sixth") def command(): """A command. """ # 54 is chosen as a length where the second line is one character # longer than the maximum length. - result = runner.invoke(cli, ['a-very-long', 'command', '--help'], - terminal_width=54) + result = runner.invoke(cli, ["a-very-long", "command", "--help"], terminal_width=54) assert not result.exception assert result.output.splitlines() == [ - 'Usage: cli a-very-long command [OPTIONS] FIRST SECOND', - ' THIRD FOURTH FIFTH', - ' SIXTH', - '', - ' A command.', - '', - 'Options:', - ' --help Show this message and exit.', + "Usage: cli a-very-long command [OPTIONS] FIRST SECOND", + " THIRD FOURTH FIFTH", + " SIXTH", + "", + " A command.", + "", + "Options:", + " --help Show this message and exit.", ] @@ -101,28 +100,29 @@ def a_very_very_very_long(): """ @a_very_very_very_long.command() - @click.argument('first') - @click.argument('second') - @click.argument('third') - @click.argument('fourth') - @click.argument('fifth') - @click.argument('sixth') + @click.argument("first") + @click.argument("second") + @click.argument("third") + @click.argument("fourth") + @click.argument("fifth") + @click.argument("sixth") def command(): """A command. """ - result = runner.invoke(cli, ['a-very-very-very-long', 'command', '--help'], - terminal_width=54) + result = runner.invoke( + cli, ["a-very-very-very-long", "command", "--help"], terminal_width=54 + ) assert not result.exception assert result.output.splitlines() == [ - 'Usage: cli a-very-very-very-long command ', - ' [OPTIONS] FIRST SECOND THIRD FOURTH FIFTH', - ' SIXTH', - '', - ' A command.', - '', - 'Options:', - ' --help Show this message and exit.', + "Usage: cli a-very-very-very-long command ", + " [OPTIONS] FIRST SECOND THIRD FOURTH FIFTH", + " SIXTH", + "", + " A command.", + "", + "Options:", + " --help Show this message and exit.", ] @@ -133,33 +133,33 @@ def cli(): """ - result = runner.invoke(cli, ['--help']) + result = runner.invoke(cli, ["--help"]) assert not result.exception assert result.output.splitlines() == [ - 'Usage: cli [OPTIONS]', - '', - ' Top level command', - '', - '', - '', - 'Options:', - ' --help Show this message and exit.', + "Usage: cli [OPTIONS]", + "", + " Top level command", + "", + "", + "", + "Options:", + " --help Show this message and exit.", ] def test_formatting_usage_error(runner): @click.command() - @click.argument('arg') + @click.argument("arg") def cmd(arg): - click.echo('arg:' + arg) + click.echo("arg:" + arg) result = runner.invoke(cmd, []) assert result.exit_code == 2 assert result.output.splitlines() == [ - 'Usage: cmd [OPTIONS] ARG', + "Usage: cmd [OPTIONS] ARG", 'Try "cmd --help" for help.', - '', - 'Error: Missing argument "ARG".' + "", + 'Error: Missing argument "ARG".', ] @@ -168,34 +168,35 @@ def test_formatting_usage_error_metavar_missing_arg(runner): :author: @r-m-n Including attribution to #612 """ + @click.command() - @click.argument('arg', metavar='metavar') + @click.argument("arg", metavar="metavar") def cmd(arg): pass result = runner.invoke(cmd, []) assert result.exit_code == 2 assert result.output.splitlines() == [ - 'Usage: cmd [OPTIONS] metavar', + "Usage: cmd [OPTIONS] metavar", 'Try "cmd --help" for help.', - '', - 'Error: Missing argument "metavar".' + "", + 'Error: Missing argument "metavar".', ] def test_formatting_usage_error_metavar_bad_arg(runner): @click.command() - @click.argument('arg', type=click.INT, metavar='metavar') + @click.argument("arg", type=click.INT, metavar="metavar") def cmd(arg): pass - result = runner.invoke(cmd, ['3.14']) + result = runner.invoke(cmd, ["3.14"]) assert result.exit_code == 2 assert result.output.splitlines() == [ - 'Usage: cmd [OPTIONS] metavar', + "Usage: cmd [OPTIONS] metavar", 'Try "cmd --help" for help.', - '', - 'Error: Invalid value for "metavar": 3.14 is not a valid integer' + "", + 'Error: Invalid value for "metavar": 3.14 is not a valid integer', ] @@ -205,50 +206,51 @@ def cmd(): pass @cmd.command() - @click.argument('bar') + @click.argument("bar") def foo(bar): - click.echo('foo:' + bar) + click.echo("foo:" + bar) - result = runner.invoke(cmd, ['foo']) + result = runner.invoke(cmd, ["foo"]) assert result.exit_code == 2 assert result.output.splitlines() == [ - 'Usage: cmd foo [OPTIONS] BAR', + "Usage: cmd foo [OPTIONS] BAR", 'Try "cmd foo --help" for help.', - '', - 'Error: Missing argument "BAR".' + "", + 'Error: Missing argument "BAR".', ] def test_formatting_usage_error_no_help(runner): @click.command(add_help_option=False) - @click.argument('arg') + @click.argument("arg") def cmd(arg): - click.echo('arg:' + arg) + click.echo("arg:" + arg) result = runner.invoke(cmd, []) assert result.exit_code == 2 assert result.output.splitlines() == [ - 'Usage: cmd [OPTIONS] ARG', - '', - 'Error: Missing argument "ARG".' + "Usage: cmd [OPTIONS] ARG", + "", + 'Error: Missing argument "ARG".', ] def test_formatting_usage_custom_help(runner): - @click.command(context_settings=dict(help_option_names=['--man'])) - @click.argument('arg') + @click.command(context_settings=dict(help_option_names=["--man"])) + @click.argument("arg") def cmd(arg): - click.echo('arg:' + arg) + click.echo("arg:" + arg) result = runner.invoke(cmd, []) assert result.exit_code == 2 assert result.output.splitlines() == [ - 'Usage: cmd [OPTIONS] ARG', + "Usage: cmd [OPTIONS] ARG", 'Try "cmd --man" for help.', - '', - 'Error: Missing argument "ARG".' + "", + 'Error: Missing argument "ARG".', ] + def test_formatting_custom_type_metavar(runner): class MyType(click.ParamType): def get_metavar(self, param): @@ -260,13 +262,13 @@ def get_metavar(self, param): def cmd(param): pass - result = runner.invoke(cmd, '--help') + result = runner.invoke(cmd, "--help") assert not result.exception assert result.output.splitlines() == [ - 'Usage: foo [OPTIONS] MY_TYPE', - '', - 'Options:', - ' --help Show this message and exit.' + "Usage: foo [OPTIONS] MY_TYPE", + "", + "Options:", + " --help Show this message and exit.", ] @@ -284,18 +286,18 @@ def cli(ctx): :param click.core.Context ctx: Click context. """ - result = runner.invoke(cli, ['--help'], terminal_width=60) + result = runner.invoke(cli, ["--help"], terminal_width=60) assert not result.exception assert result.output.splitlines() == [ - 'Usage: cli [OPTIONS]', - '', - ' First paragraph.', - '', - ' This is a very long second paragraph and not correctly', - ' wrapped but it will be rewrapped.', - '', - 'Options:', - ' --help Show this message and exit.', + "Usage: cli [OPTIONS]", + "", + " First paragraph.", + "", + " This is a very long second paragraph and not correctly", + " wrapped but it will be rewrapped.", + "", + "Options:", + " --help Show this message and exit.", ] @@ -305,47 +307,47 @@ def test_global_show_default(runner): def cli(): pass - result = runner.invoke(cli, ['--help'],) + result = runner.invoke(cli, ["--help"],) assert result.output.splitlines() == [ - 'Usage: cli [OPTIONS]', - '', - 'Options:', - ' -f TEXT Output file name [default: out.txt]', - ' --help Show this message and exit. [default: False]' + "Usage: cli [OPTIONS]", + "", + "Options:", + " -f TEXT Output file name [default: out.txt]", + " --help Show this message and exit. [default: False]", ] def test_formatting_usage_multiline_option_padding(runner): - @click.command('foo') - @click.option('--bar', help='This help message will be padded if it wraps.') + @click.command("foo") + @click.option("--bar", help="This help message will be padded if it wraps.") def cli(): pass - result = runner.invoke(cli, '--help', terminal_width=45) + result = runner.invoke(cli, "--help", terminal_width=45) assert not result.exception assert result.output.splitlines() == [ - 'Usage: foo [OPTIONS]', - '', - 'Options:', - ' --bar TEXT This help message will be', - ' padded if it wraps.', - '', - ' --help Show this message and exit.' + "Usage: foo [OPTIONS]", + "", + "Options:", + " --bar TEXT This help message will be", + " padded if it wraps.", + "", + " --help Show this message and exit.", ] def test_formatting_usage_no_option_padding(runner): - @click.command('foo') - @click.option('--bar', help='This help message will be padded if it wraps.') + @click.command("foo") + @click.option("--bar", help="This help message will be padded if it wraps.") def cli(): pass - result = runner.invoke(cli, '--help', terminal_width=80) + result = runner.invoke(cli, "--help", terminal_width=80) assert not result.exception assert result.output.splitlines() == [ - 'Usage: foo [OPTIONS]', - '', - 'Options:', - ' --bar TEXT This help message will be padded if it wraps.', - ' --help Show this message and exit.' + "Usage: foo [OPTIONS]", + "", + "Options:", + " --bar TEXT This help message will be padded if it wraps.", + " --help Show this message and exit.", ] diff --git a/tests/test_imports.py b/tests/test_imports.py index 00b604b43..9d5281182 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -5,7 +5,7 @@ from click._compat import WIN -IMPORT_TEST = b'''\ +IMPORT_TEST = b"""\ try: import __builtin__ as builtins except ImportError: @@ -27,29 +27,48 @@ def tracking_import(module, locals=None, globals=None, fromlist=None, rv = list(found_imports) import json click.echo(json.dumps(rv)) -''' +""" -ALLOWED_IMPORTS = set([ - 'weakref', 'os', 'struct', 'collections', 'sys', 'contextlib', - 'functools', 'stat', 're', 'codecs', 'inspect', 'itertools', 'io', - 'threading', 'colorama', 'errno', 'fcntl', 'datetime', 'pipes', 'shlex' -]) +ALLOWED_IMPORTS = set( + [ + "weakref", + "os", + "struct", + "collections", + "sys", + "contextlib", + "functools", + "stat", + "re", + "codecs", + "inspect", + "itertools", + "io", + "threading", + "colorama", + "errno", + "fcntl", + "datetime", + "pipes", + "shlex", + ] +) if WIN: - ALLOWED_IMPORTS.update(['ctypes', 'ctypes.wintypes', 'msvcrt', 'time', - 'zlib']) + ALLOWED_IMPORTS.update(["ctypes", "ctypes.wintypes", "msvcrt", "time", "zlib"]) def test_light_imports(): - c = subprocess.Popen([sys.executable, '-'], stdin=subprocess.PIPE, - stdout=subprocess.PIPE) + c = subprocess.Popen( + [sys.executable, "-"], stdin=subprocess.PIPE, stdout=subprocess.PIPE + ) rv = c.communicate(IMPORT_TEST)[0] if sys.version_info[0] != 2: - rv = rv.decode('utf-8') + rv = rv.decode("utf-8") imported = json.loads(rv) for module in imported: - if module == 'click' or module.startswith('click.'): + if module == "click" or module.startswith("click."): continue assert module in ALLOWED_IMPORTS diff --git a/tests/test_normalization.py b/tests/test_normalization.py index 4790d32bb..32df098e9 100644 --- a/tests/test_normalization.py +++ b/tests/test_normalization.py @@ -6,24 +6,24 @@ def test_option_normalization(runner): @click.command(context_settings=CONTEXT_SETTINGS) - @click.option('--foo') - @click.option('-x') + @click.option("--foo") + @click.option("-x") def cli(foo, x): click.echo(foo) click.echo(x) - result = runner.invoke(cli, ['--FOO', '42', '-X', 23]) - assert result.output == '42\n23\n' + result = runner.invoke(cli, ["--FOO", "42", "-X", 23]) + assert result.output == "42\n23\n" def test_choice_normalization(runner): @click.command(context_settings=CONTEXT_SETTINGS) - @click.option('--choice', type=click.Choice(['Foo', 'Bar'])) + @click.option("--choice", type=click.Choice(["Foo", "Bar"])) def cli(choice): click.echo(choice) - result = runner.invoke(cli, ['--CHOICE', 'FOO']) - assert result.output == 'Foo\n' + result = runner.invoke(cli, ["--CHOICE", "FOO"]) + assert result.output == "Foo\n" def test_command_normalization(runner): @@ -33,7 +33,7 @@ def cli(): @cli.command() def foo(): - click.echo('here!') + click.echo("here!") - result = runner.invoke(cli, ['FOO']) - assert result.output == 'here!\n' + result = runner.invoke(cli, ["FOO"]) + assert result.output == "here!\n" diff --git a/tests/test_options.py b/tests/test_options.py index f3156b009..bdf1b956e 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -10,85 +10,89 @@ def test_prefixes(runner): @click.command() - @click.option('++foo', is_flag=True, help='das foo') - @click.option('--bar', is_flag=True, help='das bar') + @click.option("++foo", is_flag=True, help="das foo") + @click.option("--bar", is_flag=True, help="das bar") def cli(foo, bar): - click.echo('foo=%s bar=%s' % (foo, bar)) + click.echo("foo=%s bar=%s" % (foo, bar)) - result = runner.invoke(cli, ['++foo', '--bar']) + result = runner.invoke(cli, ["++foo", "--bar"]) assert not result.exception - assert result.output == 'foo=True bar=True\n' + assert result.output == "foo=True bar=True\n" - result = runner.invoke(cli, ['--help']) - assert re.search(r'\+\+foo\s+das foo', result.output) is not None - assert re.search(r'--bar\s+das bar', result.output) is not None + result = runner.invoke(cli, ["--help"]) + assert re.search(r"\+\+foo\s+das foo", result.output) is not None + assert re.search(r"--bar\s+das bar", result.output) is not None def test_invalid_option(runner): try: + @click.command() - @click.option('foo') + @click.option("foo") def cli(foo): pass + except TypeError as e: - assert 'No options defined but a name was passed (foo).' \ - in str(e) + assert "No options defined but a name was passed (foo)." in str(e) else: - assert False, 'Expected a type error because of an invalid option.' + assert False, "Expected a type error because of an invalid option." def test_invalid_nargs(runner): try: + @click.command() - @click.option('--foo', nargs=-1) + @click.option("--foo", nargs=-1) def cli(foo): pass + except TypeError as e: - assert 'Options cannot have nargs < 0' in str(e) + assert "Options cannot have nargs < 0" in str(e) else: - assert False, 'Expected a type error because of an invalid option.' + assert False, "Expected a type error because of an invalid option." def test_nargs_tup_composite_mult(runner): @click.command() - @click.option('--item', type=(str, int), multiple=True) + @click.option("--item", type=(str, int), multiple=True) def copy(item): for item in item: - click.echo('name=%s id=%d' % item) + click.echo("name=%s id=%d" % item) - result = runner.invoke(copy, ['--item', 'peter', '1', '--item', 'max', '2']) + result = runner.invoke(copy, ["--item", "peter", "1", "--item", "max", "2"]) assert not result.exception assert result.output.splitlines() == [ - 'name=peter id=1', - 'name=max id=2', + "name=peter id=1", + "name=max id=2", ] def test_counting(runner): @click.command() - @click.option('-v', count=True, help='Verbosity', - type=click.IntRange(0, 3)) + @click.option("-v", count=True, help="Verbosity", type=click.IntRange(0, 3)) def cli(v): - click.echo('verbosity=%d' % v) + click.echo("verbosity=%d" % v) - result = runner.invoke(cli, ['-vvv']) + result = runner.invoke(cli, ["-vvv"]) assert not result.exception - assert result.output == 'verbosity=3\n' + assert result.output == "verbosity=3\n" - result = runner.invoke(cli, ['-vvvv']) + result = runner.invoke(cli, ["-vvvv"]) assert result.exception - assert 'Invalid value for "-v": 4 is not in the valid range of 0 to 3.' \ + assert ( + 'Invalid value for "-v": 4 is not in the valid range of 0 to 3.' in result.output + ) result = runner.invoke(cli, []) assert not result.exception - assert result.output == 'verbosity=0\n' + assert result.output == "verbosity=0\n" - result = runner.invoke(cli, ['--help']) - assert re.search(r'-v\s+Verbosity', result.output) is not None + result = runner.invoke(cli, ["--help"]) + assert re.search(r"-v\s+Verbosity", result.output) is not None -@pytest.mark.parametrize('unknown_flag', ['--foo', '-f']) +@pytest.mark.parametrize("unknown_flag", ["--foo", "-f"]) def test_unknown_options(runner, unknown_flag): @click.command() def cli(): @@ -96,18 +100,18 @@ def cli(): result = runner.invoke(cli, [unknown_flag]) assert result.exception - assert 'no such option: {0}'.format(unknown_flag) in result.output + assert "no such option: {0}".format(unknown_flag) in result.output def test_multiple_required(runner): @click.command() - @click.option('-m', '--message', multiple=True, required=True) + @click.option("-m", "--message", multiple=True, required=True) def cli(message): - click.echo('\n'.join(message)) + click.echo("\n".join(message)) - result = runner.invoke(cli, ['-m', 'foo', '-mbar']) + result = runner.invoke(cli, ["-m", "foo", "-mbar"]) assert not result.exception - assert result.output == 'foo\nbar\n' + assert result.output == "foo\nbar\n" result = runner.invoke(cli, []) assert result.exception @@ -116,65 +120,68 @@ def cli(message): def test_empty_envvar(runner): @click.command() - @click.option('--mypath', type=click.Path(exists=True), envvar='MYPATH') + @click.option("--mypath", type=click.Path(exists=True), envvar="MYPATH") def cli(mypath): - click.echo('mypath: %s' % mypath) + click.echo("mypath: %s" % mypath) - result = runner.invoke(cli, [], env={'MYPATH': ''}) + result = runner.invoke(cli, [], env={"MYPATH": ""}) assert result.exit_code == 0 - assert result.output == 'mypath: None\n' + assert result.output == "mypath: None\n" def test_multiple_envvar(runner): @click.command() - @click.option('--arg', multiple=True) + @click.option("--arg", multiple=True) def cmd(arg): - click.echo('|'.join(arg)) + click.echo("|".join(arg)) - result = runner.invoke(cmd, [], auto_envvar_prefix='TEST', - env={'TEST_ARG': 'foo bar baz'}) + result = runner.invoke( + cmd, [], auto_envvar_prefix="TEST", env={"TEST_ARG": "foo bar baz"} + ) assert not result.exception - assert result.output == 'foo|bar|baz\n' + assert result.output == "foo|bar|baz\n" @click.command() - @click.option('--arg', multiple=True, envvar='X') + @click.option("--arg", multiple=True, envvar="X") def cmd(arg): - click.echo('|'.join(arg)) + click.echo("|".join(arg)) - result = runner.invoke(cmd, [], env={'X': 'foo bar baz'}) + result = runner.invoke(cmd, [], env={"X": "foo bar baz"}) assert not result.exception - assert result.output == 'foo|bar|baz\n' + assert result.output == "foo|bar|baz\n" @click.command() - @click.option('--arg', multiple=True, type=click.Path()) + @click.option("--arg", multiple=True, type=click.Path()) def cmd(arg): - click.echo('|'.join(arg)) + click.echo("|".join(arg)) - result = runner.invoke(cmd, [], auto_envvar_prefix='TEST', - env={'TEST_ARG': 'foo%sbar' % os.path.pathsep}) + result = runner.invoke( + cmd, + [], + auto_envvar_prefix="TEST", + env={"TEST_ARG": "foo%sbar" % os.path.pathsep}, + ) assert not result.exception - assert result.output == 'foo|bar\n' + assert result.output == "foo|bar\n" def test_multiple_default_help(runner): @click.command() - @click.option('--arg1', multiple=True, default=('foo', 'bar'), - show_default=True) - @click.option('--arg2', multiple=True, default=(1, 2), type=int, - show_default=True) + @click.option("--arg1", multiple=True, default=("foo", "bar"), show_default=True) + @click.option("--arg2", multiple=True, default=(1, 2), type=int, show_default=True) def cmd(arg, arg2): pass - result = runner.invoke(cmd, ['--help']) + result = runner.invoke(cmd, ["--help"]) assert not result.exception - assert 'foo, bar' in result.output - assert '1, 2' in result.output + assert "foo, bar" in result.output + assert "1, 2" in result.output def test_multiple_default_type(runner): @click.command() - @click.option('--arg1', multiple=True, default=('foo', 'bar')) - @click.option('--arg2', multiple=True, default=(1, 'a')) + @click.option("--arg1", multiple=True, default=("foo", "bar")) + @click.option("--arg2", multiple=True, default=(1, "a")) def cmd(arg1, arg2): assert all(isinstance(e[0], text_type) for e in arg1) assert all(isinstance(e[1], text_type) for e in arg1) @@ -182,96 +189,104 @@ def cmd(arg1, arg2): assert all(isinstance(e[0], int) for e in arg2) assert all(isinstance(e[1], text_type) for e in arg2) - result = runner.invoke(cmd, '--arg1 a b --arg1 test 1 --arg2 2 ' - 'two --arg2 4 four'.split()) + result = runner.invoke( + cmd, "--arg1 a b --arg1 test 1 --arg2 2 two --arg2 4 four".split() + ) assert not result.exception + def test_dynamic_default_help_unset(runner): @click.command() - @click.option('--username', prompt=True, - default=lambda: os.environ.get('USER', ''), - show_default=True) + @click.option( + "--username", + prompt=True, + default=lambda: os.environ.get("USER", ""), + show_default=True, + ) def cmd(username): print("Hello,", username) - result = runner.invoke(cmd, ['--help']) + result = runner.invoke(cmd, ["--help"]) assert result.exit_code == 0 - assert '--username' in result.output - assert 'lambda' not in result.output - assert '(dynamic)' in result.output + assert "--username" in result.output + assert "lambda" not in result.output + assert "(dynamic)" in result.output + def test_dynamic_default_help_text(runner): @click.command() - @click.option('--username', prompt=True, - default=lambda: os.environ.get('USER', ''), - show_default='current user') + @click.option( + "--username", + prompt=True, + default=lambda: os.environ.get("USER", ""), + show_default="current user", + ) def cmd(username): print("Hello,", username) - result = runner.invoke(cmd, ['--help']) + result = runner.invoke(cmd, ["--help"]) assert result.exit_code == 0 - assert '--username' in result.output - assert 'lambda' not in result.output - assert '(current user)' in result.output + assert "--username" in result.output + assert "lambda" not in result.output + assert "(current user)" in result.output def test_toupper_envvar_prefix(runner): @click.command() - @click.option('--arg') + @click.option("--arg") def cmd(arg): click.echo(arg) - result = runner.invoke(cmd, [], auto_envvar_prefix='test', - env={'TEST_ARG': 'foo'}) + result = runner.invoke(cmd, [], auto_envvar_prefix="test", env={"TEST_ARG": "foo"}) assert not result.exception - assert result.output == 'foo\n' + assert result.output == "foo\n" def test_nargs_envvar(runner): @click.command() - @click.option('--arg', nargs=2) + @click.option("--arg", nargs=2) def cmd(arg): - click.echo('|'.join(arg)) + click.echo("|".join(arg)) - result = runner.invoke(cmd, [], auto_envvar_prefix='TEST', - env={'TEST_ARG': 'foo bar'}) + result = runner.invoke( + cmd, [], auto_envvar_prefix="TEST", env={"TEST_ARG": "foo bar"} + ) assert not result.exception - assert result.output == 'foo|bar\n' + assert result.output == "foo|bar\n" @click.command() - @click.option('--arg', nargs=2, multiple=True) + @click.option("--arg", nargs=2, multiple=True) def cmd(arg): for item in arg: - click.echo('|'.join(item)) + click.echo("|".join(item)) - result = runner.invoke(cmd, [], auto_envvar_prefix='TEST', - env={'TEST_ARG': 'x 1 y 2'}) + result = runner.invoke( + cmd, [], auto_envvar_prefix="TEST", env={"TEST_ARG": "x 1 y 2"} + ) assert not result.exception - assert result.output == 'x|1\ny|2\n' + assert result.output == "x|1\ny|2\n" def test_show_envvar(runner): @click.command() - @click.option('--arg1', envvar='ARG1', - show_envvar=True) + @click.option("--arg1", envvar="ARG1", show_envvar=True) def cmd(arg): pass - result = runner.invoke(cmd, ['--help']) + result = runner.invoke(cmd, ["--help"]) assert not result.exception - assert 'ARG1' in result.output + assert "ARG1" in result.output def test_show_envvar_auto_prefix(runner): @click.command() - @click.option('--arg1', show_envvar=True) + @click.option("--arg1", show_envvar=True) def cmd(arg): pass - result = runner.invoke(cmd, ['--help'], - auto_envvar_prefix='TEST') + result = runner.invoke(cmd, ["--help"], auto_envvar_prefix="TEST") assert not result.exception - assert 'TEST_ARG1' in result.output + assert "TEST_ARG1" in result.output def test_show_envvar_auto_prefix_dash_in_command(runner): @@ -280,133 +295,128 @@ def cli(): pass @cli.command() - @click.option('--baz', show_envvar=True) + @click.option("--baz", show_envvar=True) def foo_bar(baz): pass - result = runner.invoke(cli, ['foo-bar', '--help'], - auto_envvar_prefix='TEST') + result = runner.invoke(cli, ["foo-bar", "--help"], auto_envvar_prefix="TEST") assert not result.exception - assert 'TEST_FOO_BAR_BAZ' in result.output + assert "TEST_FOO_BAR_BAZ" in result.output def test_custom_validation(runner): def validate_pos_int(ctx, param, value): if value < 0: - raise click.BadParameter('Value needs to be positive') + raise click.BadParameter("Value needs to be positive") return value @click.command() - @click.option('--foo', callback=validate_pos_int, default=1) + @click.option("--foo", callback=validate_pos_int, default=1) def cmd(foo): click.echo(foo) - result = runner.invoke(cmd, ['--foo', '-1']) - assert 'Invalid value for "--foo": Value needs to be positive' \ - in result.output + result = runner.invoke(cmd, ["--foo", "-1"]) + assert 'Invalid value for "--foo": Value needs to be positive' in result.output - result = runner.invoke(cmd, ['--foo', '42']) - assert result.output == '42\n' + result = runner.invoke(cmd, ["--foo", "42"]) + assert result.output == "42\n" def test_winstyle_options(runner): @click.command() - @click.option('/debug;/no-debug', help='Enables or disables debug mode.') + @click.option("/debug;/no-debug", help="Enables or disables debug mode.") def cmd(debug): click.echo(debug) - result = runner.invoke(cmd, ['/debug'], help_option_names=['/?']) - assert result.output == 'True\n' - result = runner.invoke(cmd, ['/no-debug'], help_option_names=['/?']) - assert result.output == 'False\n' - result = runner.invoke(cmd, [], help_option_names=['/?']) - assert result.output == 'False\n' - result = runner.invoke(cmd, ['/?'], help_option_names=['/?']) - assert '/debug; /no-debug Enables or disables debug mode.' in result.output - assert '/? Show this message and exit.' in result.output + result = runner.invoke(cmd, ["/debug"], help_option_names=["/?"]) + assert result.output == "True\n" + result = runner.invoke(cmd, ["/no-debug"], help_option_names=["/?"]) + assert result.output == "False\n" + result = runner.invoke(cmd, [], help_option_names=["/?"]) + assert result.output == "False\n" + result = runner.invoke(cmd, ["/?"], help_option_names=["/?"]) + assert "/debug; /no-debug Enables or disables debug mode." in result.output + assert "/? Show this message and exit." in result.output def test_legacy_options(runner): @click.command() - @click.option('-whatever') + @click.option("-whatever") def cmd(whatever): click.echo(whatever) - result = runner.invoke(cmd, ['-whatever', '42']) - assert result.output == '42\n' - result = runner.invoke(cmd, ['-whatever=23']) - assert result.output == '23\n' + result = runner.invoke(cmd, ["-whatever", "42"]) + assert result.output == "42\n" + result = runner.invoke(cmd, ["-whatever=23"]) + assert result.output == "23\n" def test_missing_option_string_cast(): ctx = click.Context(click.Command("")) with pytest.raises(click.MissingParameter) as excinfo: - click.Option(['-a'], required=True).full_process_value(ctx, None) + click.Option(["-a"], required=True).full_process_value(ctx, None) assert str(excinfo.value) == "missing parameter: a" def test_missing_choice(runner): @click.command() - @click.option('--foo', type=click.Choice(['foo', 'bar']), - required=True) + @click.option("--foo", type=click.Choice(["foo", "bar"]), required=True) def cmd(foo): click.echo(foo) result = runner.invoke(cmd) assert result.exit_code == 2 - error, separator, choices = result.output.partition('Choose from') + error, separator, choices = result.output.partition("Choose from") assert 'Error: Missing option "--foo". ' in error - assert 'Choose from' in separator - assert 'foo' in choices - assert 'bar' in choices + assert "Choose from" in separator + assert "foo" in choices + assert "bar" in choices def test_case_insensitive_choice(runner): @click.command() - @click.option('--foo', type=click.Choice( - ['Orange', 'Apple'], case_sensitive=False)) + @click.option("--foo", type=click.Choice(["Orange", "Apple"], case_sensitive=False)) def cmd(foo): click.echo(foo) - result = runner.invoke(cmd, ['--foo', 'apple']) + result = runner.invoke(cmd, ["--foo", "apple"]) assert result.exit_code == 0 - assert result.output == 'Apple\n' + assert result.output == "Apple\n" - result = runner.invoke(cmd, ['--foo', 'oRANGe']) + result = runner.invoke(cmd, ["--foo", "oRANGe"]) assert result.exit_code == 0 - assert result.output == 'Orange\n' + assert result.output == "Orange\n" - result = runner.invoke(cmd, ['--foo', 'Apple']) + result = runner.invoke(cmd, ["--foo", "Apple"]) assert result.exit_code == 0 - assert result.output == 'Apple\n' + assert result.output == "Apple\n" @click.command() - @click.option('--foo', type=click.Choice(['Orange', 'Apple'])) + @click.option("--foo", type=click.Choice(["Orange", "Apple"])) def cmd2(foo): click.echo(foo) - result = runner.invoke(cmd2, ['--foo', 'apple']) + result = runner.invoke(cmd2, ["--foo", "apple"]) assert result.exit_code == 2 - result = runner.invoke(cmd2, ['--foo', 'oRANGe']) + result = runner.invoke(cmd2, ["--foo", "oRANGe"]) assert result.exit_code == 2 - result = runner.invoke(cmd2, ['--foo', 'Apple']) + result = runner.invoke(cmd2, ["--foo", "Apple"]) assert result.exit_code == 0 def test_case_insensitive_choice_returned_exactly(runner): @click.command() - @click.option('--foo', type=click.Choice( - ['Orange', 'Apple'], case_sensitive=False)) + @click.option("--foo", type=click.Choice(["Orange", "Apple"], case_sensitive=False)) def cmd(foo): click.echo(foo) - result = runner.invoke(cmd, ['--foo', 'apple']) + result = runner.invoke(cmd, ["--foo", "apple"]) assert result.exit_code == 0 - assert result.output == 'Apple\n' + assert result.output == "Apple\n" def test_option_help_preserve_paragraphs(runner): @@ -424,7 +434,7 @@ def test_option_help_preserve_paragraphs(runner): def cmd(config): pass - result = runner.invoke(cmd, ['--help'], ) + result = runner.invoke(cmd, ["--help"],) assert result.exit_code == 0 assert ( " -C, --config PATH Configuration file to use.\n" @@ -434,36 +444,37 @@ def cmd(config): "{i}configuration file is loaded.".format(i=" " * 21) ) in result.output + def test_argument_custom_class(runner): class CustomArgument(click.Argument): def get_default(self, ctx): - '''a dumb override of a default value for testing''' - return 'I am a default' + """a dumb override of a default value for testing""" + return "I am a default" @click.command() - @click.argument('testarg', cls=CustomArgument, default='you wont see me') + @click.argument("testarg", cls=CustomArgument, default="you wont see me") def cmd(testarg): click.echo(testarg) result = runner.invoke(cmd) - assert 'I am a default' in result.output - assert 'you wont see me' not in result.output + assert "I am a default" in result.output + assert "you wont see me" not in result.output def test_option_custom_class(runner): class CustomOption(click.Option): def get_help_record(self, ctx): - '''a dumb override of a help text for testing''' - return ('--help', 'I am a help text') + """a dumb override of a help text for testing""" + return ("--help", "I am a help text") @click.command() - @click.option('--testoption', cls=CustomOption, help='you wont see me') + @click.option("--testoption", cls=CustomOption, help="you wont see me") def cmd(testoption): click.echo(testoption) - result = runner.invoke(cmd, ['--help']) - assert 'I am a help text' in result.output - assert 'you wont see me' not in result.output + result = runner.invoke(cmd, ["--help"]) + assert "I am a help text" in result.output + assert "you wont see me" not in result.output def test_option_custom_class_reusable(runner): @@ -471,11 +482,11 @@ def test_option_custom_class_reusable(runner): class CustomOption(click.Option): def get_help_record(self, ctx): - '''a dumb override of a help text for testing''' - return ('--help', 'I am a help text') + """a dumb override of a help text for testing""" + return ("--help", "I am a help text") # Assign to a variable to re-use the decorator. - testoption = click.option('--testoption', cls=CustomOption, help='you wont see me') + testoption = click.option("--testoption", cls=CustomOption, help="you wont see me") @click.command() @testoption @@ -490,14 +501,14 @@ def cmd2(testoption): # Both of the commands should have the --help option now. for cmd in (cmd1, cmd2): - result = runner.invoke(cmd, ['--help']) - assert 'I am a help text' in result.output - assert 'you wont see me' not in result.output + result = runner.invoke(cmd, ["--help"]) + assert "I am a help text" in result.output + assert "you wont see me" not in result.output def test_bool_flag_with_type(runner): @click.command() - @click.option('--shout/--no-shout', default=False, type=bool) + @click.option("--shout/--no-shout", default=False, type=bool) def cmd(shout): pass @@ -507,43 +518,46 @@ def cmd(shout): def test_aliases_for_flags(runner): @click.command() - @click.option('--warnings/--no-warnings', ' /-W', default=True) + @click.option("--warnings/--no-warnings", " /-W", default=True) def cli(warnings): click.echo(warnings) - result = runner.invoke(cli, ['--warnings']) - assert result.output == 'True\n' - result = runner.invoke(cli, ['--no-warnings']) - assert result.output == 'False\n' - result = runner.invoke(cli, ['-W']) - assert result.output == 'False\n' + result = runner.invoke(cli, ["--warnings"]) + assert result.output == "True\n" + result = runner.invoke(cli, ["--no-warnings"]) + assert result.output == "False\n" + result = runner.invoke(cli, ["-W"]) + assert result.output == "False\n" @click.command() - @click.option('--warnings/--no-warnings', '-w', default=True) + @click.option("--warnings/--no-warnings", "-w", default=True) def cli_alt(warnings): click.echo(warnings) - result = runner.invoke(cli_alt, ['--warnings']) - assert result.output == 'True\n' - result = runner.invoke(cli_alt, ['--no-warnings']) - assert result.output == 'False\n' - result = runner.invoke(cli_alt, ['-w']) - assert result.output == 'True\n' - -@pytest.mark.parametrize('option_args,expected', [ - (['--aggressive', '--all', '-a'], 'aggressive'), - (['--first', '--second', '--third', '-a', '-b', '-c'], 'first'), - (['--apple', '--banana', '--cantaloupe', '-a', '-b', '-c'], 'apple'), - (['--cantaloupe', '--banana', '--apple', '-c', '-b', '-a'], 'cantaloupe'), - (['-a', '-b', '-c'], 'a'), - (['-c', '-b', '-a'], 'c'), - (['-a', '--apple', '-b', '--banana', '-c', '--cantaloupe'], 'apple'), - (['-c', '-a', '--cantaloupe', '-b', '--banana', '--apple'], 'cantaloupe'), - (['--from', '-f', '_from'], '_from'), - (['--return', '-r', '_ret'], '_ret'), -]) + result = runner.invoke(cli_alt, ["--warnings"]) + assert result.output == "True\n" + result = runner.invoke(cli_alt, ["--no-warnings"]) + assert result.output == "False\n" + result = runner.invoke(cli_alt, ["-w"]) + assert result.output == "True\n" + + +@pytest.mark.parametrize( + "option_args,expected", + [ + (["--aggressive", "--all", "-a"], "aggressive"), + (["--first", "--second", "--third", "-a", "-b", "-c"], "first"), + (["--apple", "--banana", "--cantaloupe", "-a", "-b", "-c"], "apple"), + (["--cantaloupe", "--banana", "--apple", "-c", "-b", "-a"], "cantaloupe"), + (["-a", "-b", "-c"], "a"), + (["-c", "-b", "-a"], "c"), + (["-a", "--apple", "-b", "--banana", "-c", "--cantaloupe"], "apple"), + (["-c", "-a", "--cantaloupe", "-b", "--banana", "--apple"], "cantaloupe"), + (["--from", "-f", "_from"], "_from"), + (["--return", "-r", "_ret"], "_ret"), + ], +) def test_option_names(runner, option_args, expected): - @click.command() @click.option(*option_args, is_flag=True) def cmd(**kwargs): @@ -552,6 +566,6 @@ def cmd(**kwargs): assert cmd.params[0].name == expected for form in option_args: - if form.startswith('-'): + if form.startswith("-"): result = runner.invoke(cmd, [form]) - assert result.output == 'True\n' + assert result.output == "True\n" diff --git a/tests/test_termui.py b/tests/test_termui.py index 098e335cb..d6451cac6 100644 --- a/tests/test_termui.py +++ b/tests/test_termui.py @@ -28,7 +28,7 @@ def _create_progress(length=10, length_known=True, **kwargs): def test_progressbar_strip_regression(runner, monkeypatch): fake_clock = FakeClock() - label = ' padded line' + label = " padded line" @click.command() def cli(): @@ -36,8 +36,8 @@ def cli(): for thing in progress: fake_clock.advance_time() - monkeypatch.setattr(time, 'time', fake_clock.time) - monkeypatch.setattr(click._termui_impl, 'isatty', lambda _: True) + monkeypatch.setattr(time, "time", fake_clock.time) + monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True) assert label in runner.invoke(cli, []).output @@ -64,19 +64,19 @@ def __next__(self): @click.command() def cli(): - with click.progressbar(Hinted(10), label='test') as progress: + with click.progressbar(Hinted(10), label="test") as progress: for thing in progress: fake_clock.advance_time() - monkeypatch.setattr(time, 'time', fake_clock.time) - monkeypatch.setattr(click._termui_impl, 'isatty', lambda _: True) + monkeypatch.setattr(time, "time", fake_clock.time) + monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True) result = runner.invoke(cli, []) assert result.exception is None def test_progressbar_hidden(runner, monkeypatch): fake_clock = FakeClock() - label = 'whatever' + label = "whatever" @click.command() def cli(): @@ -84,81 +84,98 @@ def cli(): for thing in progress: fake_clock.advance_time() - monkeypatch.setattr(time, 'time', fake_clock.time) - monkeypatch.setattr(click._termui_impl, 'isatty', lambda _: False) - assert runner.invoke(cli, []).output == '' + monkeypatch.setattr(time, "time", fake_clock.time) + monkeypatch.setattr(click._termui_impl, "isatty", lambda _: False) + assert runner.invoke(cli, []).output == "" -@pytest.mark.parametrize('avg, expected', [([], 0.0), ([1, 4], 2.5)]) +@pytest.mark.parametrize("avg, expected", [([], 0.0), ([1, 4], 2.5)]) def test_progressbar_time_per_iteration(runner, avg, expected): with _create_progress(2, avg=avg) as progress: assert progress.time_per_iteration == expected -@pytest.mark.parametrize('finished, expected', [(False, 5), (True, 0)]) +@pytest.mark.parametrize("finished, expected", [(False, 5), (True, 0)]) def test_progressbar_eta(runner, finished, expected): with _create_progress(2, finished=finished, avg=[1, 4]) as progress: assert progress.eta == expected -@pytest.mark.parametrize('eta, expected', - [(0, '00:00:00'), (30, '00:00:30'), (90, '00:01:30'), (900, '00:15:00'), - (9000, '02:30:00'), (99999999999, '1157407d 09:46:39'), (None, '')]) +@pytest.mark.parametrize( + "eta, expected", + [ + (0, "00:00:00"), + (30, "00:00:30"), + (90, "00:01:30"), + (900, "00:15:00"), + (9000, "02:30:00"), + (99999999999, "1157407d 09:46:39"), + (None, ""), + ], +) def test_progressbar_format_eta(runner, eta, expected): with _create_progress(1, eta_known=eta is not None, avg=[eta]) as progress: assert progress.format_eta() == expected -@pytest.mark.parametrize('pos, length', [(0, 5), (-1, 1), (5, 5), (6, 5), (4, 0)]) +@pytest.mark.parametrize("pos, length", [(0, 5), (-1, 1), (5, 5), (6, 5), (4, 0)]) def test_progressbar_format_pos(runner, pos, length): with _create_progress(length, length_known=length != 0, pos=pos) as progress: result = progress.format_pos() if progress.length_known: - assert result == '%s/%s' % (pos, length) + assert result == "%s/%s" % (pos, length) else: assert result == str(pos) -@pytest.mark.parametrize('length, finished, pos, avg, expected', - [(8, False, 7, 0, '#######-'), - (0, True, 8, 0, '########'), - (0, False, 8, 0, '--------'), - (0, False, 5, 3, '#-------') - ]) +@pytest.mark.parametrize( + "length, finished, pos, avg, expected", + [ + (8, False, 7, 0, "#######-"), + (0, True, 8, 0, "########"), + (0, False, 8, 0, "--------"), + (0, False, 5, 3, "#-------"), + ], +) def test_progressbar_format_bar(runner, length, finished, pos, avg, expected): - with _create_progress(length, - length_known=length != 0, - width=8, - pos=pos, - finished=finished, - avg=[avg]) as progress: + with _create_progress( + length, length_known=length != 0, width=8, pos=pos, finished=finished, avg=[avg] + ) as progress: assert progress.format_bar() == expected -@pytest.mark.parametrize('length, length_known, show_percent, show_pos, pos, expected', - [(0, True, True, True, 0, ' [--------] 0/0 0%'), - (0, True, False, True, 0, ' [--------] 0/0'), - (0, True, False, False, 0, ' [--------]'), - (0, False, False, False, 0, ' [--------]'), - (8, True, True, True, 8, ' [########] 8/8 100%') - ]) -def test_progressbar_format_progress_line(runner, length, length_known, show_percent, show_pos, pos, expected): - with _create_progress(length, - length_known, - width=8, - show_percent=show_percent, - pos=pos, - show_pos=show_pos) as progress: +@pytest.mark.parametrize( + "length, length_known, show_percent, show_pos, pos, expected", + [ + (0, True, True, True, 0, " [--------] 0/0 0%"), + (0, True, False, True, 0, " [--------] 0/0"), + (0, True, False, False, 0, " [--------]"), + (0, False, False, False, 0, " [--------]"), + (8, True, True, True, 8, " [########] 8/8 100%"), + ], +) +def test_progressbar_format_progress_line( + runner, length, length_known, show_percent, show_pos, pos, expected +): + with _create_progress( + length, + length_known, + width=8, + show_percent=show_percent, + pos=pos, + show_pos=show_pos, + ) as progress: assert progress.format_progress_line() == expected -@pytest.mark.parametrize('test_item', ['test', None]) +@pytest.mark.parametrize("test_item", ["test", None]) def test_progressbar_format_progress_line_with_show_func(runner, test_item): - def item_show_func(item): return item - with _create_progress(item_show_func=item_show_func, current_item=test_item) as progress: + with _create_progress( + item_show_func=item_show_func, current_item=test_item + ) as progress: if test_item: assert progress.format_progress_line().endswith(test_item) else: @@ -169,9 +186,9 @@ def test_progressbar_init_exceptions(runner): try: click.progressbar() except TypeError as e: - assert str(e) == 'iterable or length is required' + assert str(e) == "iterable or length is required" else: - assert False, 'Expected an exception because unspecified arguments' + assert False, "Expected an exception because unspecified arguments" def test_progressbar_iter_outside_with_exceptions(runner): @@ -179,9 +196,9 @@ def test_progressbar_iter_outside_with_exceptions(runner): progress = click.progressbar(length=2) iter(progress) except RuntimeError as e: - assert str(e) == 'You need to use progress bars in a with block.' + assert str(e) == "You need to use progress bars in a with block." else: - assert False, 'Expected an exception because of abort-related inputs.' + assert False, "Expected an exception because of abort-related inputs." def test_progressbar_is_iterator(runner, monkeypatch): @@ -189,7 +206,7 @@ def test_progressbar_is_iterator(runner, monkeypatch): @click.command() def cli(): - with click.progressbar(range(10), label='test') as progress: + with click.progressbar(range(10), label="test") as progress: while True: try: next(progress) @@ -197,8 +214,8 @@ def cli(): except StopIteration: break - monkeypatch.setattr(time, 'time', fake_clock.time) - monkeypatch.setattr(click._termui_impl, 'isatty', lambda _: True) + monkeypatch.setattr(time, "time", fake_clock.time) + monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True) result = runner.invoke(cli, []) assert result.exception is None @@ -206,31 +223,31 @@ def cli(): def test_choices_list_in_prompt(runner, monkeypatch): @click.command() - @click.option('-g', type=click.Choice(['none', 'day', 'week', 'month']), - prompt=True) + @click.option( + "-g", type=click.Choice(["none", "day", "week", "month"]), prompt=True + ) def cli_with_choices(g): pass @click.command() - @click.option('-g', type=click.Choice(['none', 'day', 'week', 'month']), - prompt=True, show_choices=False) + @click.option( + "-g", + type=click.Choice(["none", "day", "week", "month"]), + prompt=True, + show_choices=False, + ) def cli_without_choices(g): pass - result = runner.invoke(cli_with_choices, [], input='none') - assert '(none, day, week, month)' in result.output + result = runner.invoke(cli_with_choices, [], input="none") + assert "(none, day, week, month)" in result.output - result = runner.invoke(cli_without_choices, [], input='none') - assert '(none, day, week, month)' not in result.output + result = runner.invoke(cli_without_choices, [], input="none") + assert "(none, day, week, month)" not in result.output @pytest.mark.parametrize( - "file_kwargs", - [ - {"mode": "rt"}, - {"mode": "rb"}, - {"lazy": True}, - ] + "file_kwargs", [{"mode": "rt"}, {"mode": "rb"}, {"lazy": True},] ) def test_file_prompt_default_format(runner, file_kwargs): @click.command() @@ -246,7 +263,7 @@ def test_secho(runner): with runner.isolation() as outstreams: click.secho(None, nl=False) bytes = outstreams[0].getvalue() - assert bytes == b'' + assert bytes == b"" def test_progressbar_yields_all_items(runner): @@ -264,49 +281,53 @@ def cli(): fake_clock.advance_time() print("") - monkeypatch.setattr(time, 'time', fake_clock.time) - monkeypatch.setattr(click._termui_impl, 'isatty', lambda _: True) + monkeypatch.setattr(time, "time", fake_clock.time) + monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True) output = runner.invoke(cli, []).output - lines = [line for line in output.split('\n') if '[' in line] + lines = [line for line in output.split("\n") if "[" in line] - assert ' 25% 00:00:03' in lines[0] - assert ' 50% 00:00:02' in lines[1] - assert ' 75% 00:00:01' in lines[2] - assert '100% ' in lines[3] + assert " 25% 00:00:03" in lines[0] + assert " 50% 00:00:02" in lines[1] + assert " 75% 00:00:01" in lines[2] + assert "100% " in lines[3] -@pytest.mark.parametrize( - 'key_char', (u'h', u'H', u'é', u'À', u' ', u'字', u'àH', u'àR') -) -@pytest.mark.parametrize('echo', [True, False]) -@pytest.mark.skipif(not WIN, reason='Tests user-input using the msvcrt module.') +@pytest.mark.parametrize("key_char", (u"h", u"H", u"é", u"À", u" ", u"字", u"àH", u"àR")) +@pytest.mark.parametrize("echo", [True, False]) +@pytest.mark.skipif(not WIN, reason="Tests user-input using the msvcrt module.") def test_getchar_windows(runner, monkeypatch, key_char, echo): - monkeypatch.setattr(click._termui_impl.msvcrt, 'getwche', lambda: key_char) - monkeypatch.setattr(click._termui_impl.msvcrt, 'getwch', lambda: key_char) - monkeypatch.setattr(click.termui, '_getchar', None) + monkeypatch.setattr(click._termui_impl.msvcrt, "getwche", lambda: key_char) + monkeypatch.setattr(click._termui_impl.msvcrt, "getwch", lambda: key_char) + monkeypatch.setattr(click.termui, "_getchar", None) assert click.getchar(echo) == key_char -@pytest.mark.parametrize('special_key_char, key_char', [(u'\x00', 'a'), (u'\x00', 'b'), (u'\xe0', 'c')]) -@pytest.mark.skipif(not WIN, reason='Tests special character inputs using the msvcrt module.') +@pytest.mark.parametrize( + "special_key_char, key_char", [(u"\x00", "a"), (u"\x00", "b"), (u"\xe0", "c")] +) +@pytest.mark.skipif( + not WIN, reason="Tests special character inputs using the msvcrt module." +) def test_getchar_special_key_windows(runner, monkeypatch, special_key_char, key_char): ordered_inputs = [key_char, special_key_char] - monkeypatch.setattr(click._termui_impl.msvcrt, 'getwch', lambda: ordered_inputs.pop()) - monkeypatch.setattr(click.termui, '_getchar', None) + monkeypatch.setattr( + click._termui_impl.msvcrt, "getwch", lambda: ordered_inputs.pop() + ) + monkeypatch.setattr(click.termui, "_getchar", None) assert click.getchar() == special_key_char + key_char -@pytest.mark.parametrize('key_char', [u'\x03', u'\x1a']) -@pytest.mark.skipif(not WIN, reason='Tests user-input using the msvcrt module.') +@pytest.mark.parametrize("key_char", [u"\x03", u"\x1a"]) +@pytest.mark.skipif(not WIN, reason="Tests user-input using the msvcrt module.") def test_getchar_windows_exceptions(runner, monkeypatch, key_char): - monkeypatch.setattr(click._termui_impl.msvcrt, 'getwch', lambda: key_char) - monkeypatch.setattr(click.termui, '_getchar', None) + monkeypatch.setattr(click._termui_impl.msvcrt, "getwch", lambda: key_char) + monkeypatch.setattr(click.termui, "_getchar", None) try: click.getchar() except KeyboardInterrupt: - assert key_char == u'\x03' + assert key_char == u"\x03" except EOFError: - assert key_char == u'\x1a' + assert key_char == u"\x1a" else: - assert False, 'Expected an exception because of abort-specific inputs.' + assert False, "Expected an exception because of abort-specific inputs." diff --git a/tests/test_testing.py b/tests/test_testing.py index 7802bbf82..f2d2d9a21 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -18,8 +18,8 @@ def test_runner(): @click.command() def test(): - i = click.get_binary_stream('stdin') - o = click.get_binary_stream('stdout') + i = click.get_binary_stream("stdin") + o = click.get_binary_stream("stdout") while 1: chunk = i.read(4096) if not chunk: @@ -28,21 +28,21 @@ def test(): o.flush() runner = CliRunner() - result = runner.invoke(test, input='Hello World!\n') + result = runner.invoke(test, input="Hello World!\n") assert not result.exception - assert result.output == 'Hello World!\n' + assert result.output == "Hello World!\n" runner = CliRunner(echo_stdin=True) - result = runner.invoke(test, input='Hello World!\n') + result = runner.invoke(test, input="Hello World!\n") assert not result.exception - assert result.output == 'Hello World!\nHello World!\n' + assert result.output == "Hello World!\nHello World!\n" def test_runner_with_stream(): @click.command() def test(): - i = click.get_binary_stream('stdin') - o = click.get_binary_stream('stdout') + i = click.get_binary_stream("stdin") + o = click.get_binary_stream("stdout") while 1: chunk = i.read(4096) if not chunk: @@ -51,36 +51,36 @@ def test(): o.flush() runner = CliRunner() - result = runner.invoke(test, input=ReasonableBytesIO(b'Hello World!\n')) + result = runner.invoke(test, input=ReasonableBytesIO(b"Hello World!\n")) assert not result.exception - assert result.output == 'Hello World!\n' + assert result.output == "Hello World!\n" runner = CliRunner(echo_stdin=True) - result = runner.invoke(test, input=ReasonableBytesIO(b'Hello World!\n')) + result = runner.invoke(test, input=ReasonableBytesIO(b"Hello World!\n")) assert not result.exception - assert result.output == 'Hello World!\nHello World!\n' + assert result.output == "Hello World!\nHello World!\n" def test_prompts(): @click.command() - @click.option('--foo', prompt=True) + @click.option("--foo", prompt=True) def test(foo): - click.echo('foo=%s' % foo) + click.echo("foo=%s" % foo) runner = CliRunner() - result = runner.invoke(test, input='wau wau\n') + result = runner.invoke(test, input="wau wau\n") assert not result.exception - assert result.output == 'Foo: wau wau\nfoo=wau wau\n' + assert result.output == "Foo: wau wau\nfoo=wau wau\n" @click.command() - @click.option('--foo', prompt=True, hide_input=True) + @click.option("--foo", prompt=True, hide_input=True) def test(foo): - click.echo('foo=%s' % foo) + click.echo("foo=%s" % foo) runner = CliRunner() - result = runner.invoke(test, input='wau wau\n') + result = runner.invoke(test, input="wau wau\n") assert not result.exception - assert result.output == 'Foo: \nfoo=wau wau\n' + assert result.output == "Foo: \nfoo=wau wau\n" def test_getchar(): @@ -89,9 +89,9 @@ def continue_it(): click.echo(click.getchar()) runner = CliRunner() - result = runner.invoke(continue_it, input='y') + result = runner.invoke(continue_it, input="y") assert not result.exception - assert result.output == 'y\n' + assert result.output == "y\n" def test_catch_exceptions(): @@ -118,20 +118,20 @@ def cli(): assert result.exit_code == 1 -@pytest.mark.skipif(WIN, reason='Test does not make sense on Windows.') +@pytest.mark.skipif(WIN, reason="Test does not make sense on Windows.") def test_with_color(): @click.command() def cli(): - click.secho('hello world', fg='blue') + click.secho("hello world", fg="blue") runner = CliRunner() result = runner.invoke(cli) - assert result.output == 'hello world\n' + assert result.output == "hello world\n" assert not result.exception result = runner.invoke(cli, color=True) - assert result.output == click.style('hello world', fg='blue') + '\n' + assert result.output == click.style("hello world", fg="blue") + "\n" assert not result.exception @@ -143,93 +143,93 @@ def cli(): runner = CliRunner() result = runner.invoke(cli, color=True) assert not result.exception - assert result.output == '' + assert result.output == "" def test_exit_code_and_output_from_sys_exit(): # See issue #362 @click.command() def cli_string(): - click.echo('hello world') - sys.exit('error') + click.echo("hello world") + sys.exit("error") @click.command() @click.pass_context def cli_string_ctx_exit(ctx): - click.echo('hello world') - ctx.exit('error') + click.echo("hello world") + ctx.exit("error") @click.command() def cli_int(): - click.echo('hello world') + click.echo("hello world") sys.exit(1) @click.command() @click.pass_context def cli_int_ctx_exit(ctx): - click.echo('hello world') + click.echo("hello world") ctx.exit(1) @click.command() def cli_float(): - click.echo('hello world') + click.echo("hello world") sys.exit(1.0) @click.command() @click.pass_context def cli_float_ctx_exit(ctx): - click.echo('hello world') + click.echo("hello world") ctx.exit(1.0) @click.command() def cli_no_error(): - click.echo('hello world') + click.echo("hello world") runner = CliRunner() result = runner.invoke(cli_string) assert result.exit_code == 1 - assert result.output == 'hello world\nerror\n' + assert result.output == "hello world\nerror\n" result = runner.invoke(cli_string_ctx_exit) assert result.exit_code == 1 - assert result.output == 'hello world\nerror\n' + assert result.output == "hello world\nerror\n" result = runner.invoke(cli_int) assert result.exit_code == 1 - assert result.output == 'hello world\n' + assert result.output == "hello world\n" result = runner.invoke(cli_int_ctx_exit) assert result.exit_code == 1 - assert result.output == 'hello world\n' + assert result.output == "hello world\n" result = runner.invoke(cli_float) assert result.exit_code == 1 - assert result.output == 'hello world\n1.0\n' + assert result.output == "hello world\n1.0\n" result = runner.invoke(cli_float_ctx_exit) assert result.exit_code == 1 - assert result.output == 'hello world\n1.0\n' + assert result.output == "hello world\n1.0\n" result = runner.invoke(cli_no_error) assert result.exit_code == 0 - assert result.output == 'hello world\n' + assert result.output == "hello world\n" def test_env(): @click.command() def cli_env(): - click.echo('ENV=%s' % os.environ['TEST_CLICK_ENV']) + click.echo("ENV=%s" % os.environ["TEST_CLICK_ENV"]) runner = CliRunner() env_orig = dict(os.environ) env = dict(env_orig) - assert 'TEST_CLICK_ENV' not in env - env['TEST_CLICK_ENV'] = 'some_value' + assert "TEST_CLICK_ENV" not in env + env["TEST_CLICK_ENV"] = "some_value" result = runner.invoke(cli_env, env=env) assert result.exit_code == 0 - assert result.output == 'ENV=some_value\n' + assert result.output == "ENV=some_value\n" assert os.environ == env_orig @@ -244,15 +244,15 @@ def cli_stderr(): result = runner.invoke(cli_stderr) - assert result.output == 'stdout\n' - assert result.stdout == 'stdout\n' - assert result.stderr == 'stderr\n' + assert result.output == "stdout\n" + assert result.stdout == "stdout\n" + assert result.stderr == "stderr\n" runner_mix = CliRunner(mix_stderr=True) result_mix = runner_mix.invoke(cli_stderr) - assert result_mix.output == 'stdout\nstderr\n' - assert result_mix.stdout == 'stdout\nstderr\n' + assert result_mix.output == "stdout\nstderr\n" + assert result_mix.stdout == "stdout\nstderr\n" with pytest.raises(ValueError): result_mix.stderr @@ -265,22 +265,24 @@ def cli_empty_stderr(): result = runner.invoke(cli_empty_stderr) - assert result.output == 'stdout\n' - assert result.stdout == 'stdout\n' - assert result.stderr == '' - - -@pytest.mark.parametrize('args, expected_output', [ - (None, 'bar\n'), - ([], 'bar\n'), - ('', 'bar\n'), - (['--foo', 'one two'], 'one two\n'), - ('--foo "one two"', 'one two\n'), -]) + assert result.output == "stdout\n" + assert result.stdout == "stdout\n" + assert result.stderr == "" + + +@pytest.mark.parametrize( + "args, expected_output", + [ + (None, "bar\n"), + ([], "bar\n"), + ("", "bar\n"), + (["--foo", "one two"], "one two\n"), + ('--foo "one two"', "one two\n"), + ], +) def test_args(args, expected_output): - @click.command() - @click.option('--foo', default='bar') + @click.option("--foo", default="bar") def cli_args(foo): click.echo(foo) @@ -298,4 +300,4 @@ def cli(): runner = CliRunner() result = runner.invoke(cli, prog_name="foobar") assert not result.exception - assert result.output == 'ok\n' + assert result.output == "ok\n" diff --git a/tests/test_utils.py b/tests/test_utils.py index 6ea9f9452..832571a29 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -12,43 +12,46 @@ def test_echo(runner): with runner.isolation() as outstreams: - click.echo(u'\N{SNOWMAN}') - click.echo(b'\x44\x44') + click.echo(u"\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' + 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 we are in Python 2, we expect that writing bytes into a string io # does not do anything crazy. In Python 3 if sys.version_info[0] == 2: import StringIO + sys.stdout = x = StringIO.StringIO() try: - click.echo('\xf6') + click.echo("\xf6") finally: sys.stdout = sys.__stdout__ - assert x.getvalue() == '\xf6\n' + assert x.getvalue() == "\xf6\n" # And in any case, if wrapped, we expect bytes to survive. @click.command() def cli(): - click.echo(b'\xf6') + click.echo(b"\xf6") + result = runner.invoke(cli, []) - assert result.stdout_bytes == b'\xf6\n' + 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' + 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(u'hello', file=f) - assert f.getvalue() == u'hello\n' + click.echo(u"hello", file=f) + assert f.getvalue() == u"hello\n" @pytest.mark.parametrize( @@ -81,107 +84,105 @@ def test_styling(styles, ref): assert click.unstyle(ref) == "x y" -@pytest.mark.parametrize( - ("text", "expect"), - [ - ("\x1b[?25lx y\x1b[?25h", "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(u'/x/foo.txt') == '/x/foo.txt' - assert click.format_filename(u'/x/foo.txt', shorten=True) == 'foo.txt' + assert click.format_filename(b"foo.txt") == "foo.txt" + assert click.format_filename(b"/x/foo.txt") == "/x/foo.txt" + assert click.format_filename(u"/x/foo.txt") == "/x/foo.txt" + assert click.format_filename(u"/x/foo.txt", shorten=True) == "foo.txt" # filesystem encoding on windows permits this. if not WIN: - assert click.format_filename(b'/x/foo\xff.txt', shorten=True) \ - == u'foo\ufffd.txt' + assert ( + click.format_filename(b"/x/foo\xff.txt", shorten=True) == u"foo\ufffd.txt" + ) def test_prompts(runner): @click.command() def test(): - if click.confirm('Foo'): - click.echo('yes!') + if click.confirm("Foo"): + click.echo("yes!") else: - click.echo('no :(') + click.echo("no :(") - result = runner.invoke(test, input='y\n') + result = runner.invoke(test, input="y\n") assert not result.exception - assert result.output == 'Foo [y/N]: y\nyes!\n' + assert result.output == "Foo [y/N]: y\nyes!\n" - result = runner.invoke(test, input='\n') + result = runner.invoke(test, input="\n") assert not result.exception - assert result.output == 'Foo [y/N]: \nno :(\n' + assert result.output == "Foo [y/N]: \nno :(\n" - result = runner.invoke(test, input='n\n') + result = runner.invoke(test, input="n\n") assert not result.exception - assert result.output == 'Foo [y/N]: n\nno :(\n' + assert result.output == "Foo [y/N]: n\nno :(\n" @click.command() def test_no(): - if click.confirm('Foo', default=True): - click.echo('yes!') + if click.confirm("Foo", default=True): + click.echo("yes!") else: - click.echo('no :(') + click.echo("no :(") - result = runner.invoke(test_no, input='y\n') + result = runner.invoke(test_no, input="y\n") assert not result.exception - assert result.output == 'Foo [Y/n]: y\nyes!\n' + assert result.output == "Foo [Y/n]: y\nyes!\n" - result = runner.invoke(test_no, input='\n') + result = runner.invoke(test_no, input="\n") assert not result.exception - assert result.output == 'Foo [Y/n]: \nyes!\n' + assert result.output == "Foo [Y/n]: \nyes!\n" - result = runner.invoke(test_no, input='n\n') + result = runner.invoke(test_no, input="n\n") assert not result.exception - assert result.output == 'Foo [Y/n]: n\nno :(\n' + assert result.output == "Foo [Y/n]: n\nno :(\n" -@pytest.mark.skipif(WIN, reason='Different behavior on windows.') +@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) + monkeypatch.setattr("click.termui.hidden_prompt_func", f) try: - click.prompt('Password', hide_input=True) + click.prompt("Password", hide_input=True) except click.Abort: - click.echo('Screw you.') + click.echo("Screw you.") out, err = capsys.readouterr() - assert out == 'Password: \nScrew you.\n' + assert out == "Password: \nScrew you.\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))), -]) + 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) + monkeypatch.setitem(os.environ, "PAGER", cat) + monkeypatch.setattr(click._termui_impl, "isatty", lambda x: True) expected_output = test[0] test_input = test[1]() @@ -192,35 +193,35 @@ def test_echo_via_pager(monkeypatch, capfd, cat, test): assert out == expected_output -@pytest.mark.skipif(WIN, reason='Test does not make sense on Windows.') +@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) + monkeypatch.setattr(click._compat, "isatty", lambda x: isatty) - text = 'foo' - styled_text = click.style(text, fg='red') - assert styled_text == '\x1b[31mfoo\x1b[0m' + 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 == text + '\n' + assert out == text + "\n" click.echo(styled_text, color=True) out, err = capfd.readouterr() - assert out == styled_text + '\n' + assert out == styled_text + "\n" isatty = True click.echo(styled_text) out, err = capfd.readouterr() - assert out == styled_text + '\n' + assert out == styled_text + "\n" isatty = False click.echo(styled_text) out, err = capfd.readouterr() - assert out == text + '\n' + assert out == text + "\n" -@pytest.mark.skipif(WIN, reason='Test too complex to make work windows.') +@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.""" @@ -228,76 +229,76 @@ def emulate_input(text): from StringIO import StringIO else: from io import StringIO - monkeypatch.setattr(sys, 'stdin', StringIO(text)) + monkeypatch.setattr(sys, "stdin", StringIO(text)) - click.echo('Echo to standard output') + click.echo("Echo to standard output") out, err = capfd.readouterr() - assert out == 'Echo to standard output\n' - assert err == '' + assert out == "Echo to standard output\n" + assert err == "" - click.echo('Echo to standard error', err=True) + click.echo("Echo to standard error", err=True) out, err = capfd.readouterr() - assert out == '' - assert err == 'Echo to standard error\n' + assert out == "" + assert err == "Echo to standard error\n" - emulate_input('asdlkj\n') - click.prompt('Prompt to stdin') + emulate_input("asdlkj\n") + click.prompt("Prompt to stdin") out, err = capfd.readouterr() - assert out == 'Prompt to stdin: ' - assert err == '' + assert out == "Prompt to stdin: " + assert err == "" - emulate_input('asdlkj\n') - click.prompt('Prompt to stderr', err=True) + emulate_input("asdlkj\n") + click.prompt("Prompt to stderr", err=True) out, err = capfd.readouterr() - assert out == '' - assert err == 'Prompt to stderr: ' + assert out == "" + assert err == "Prompt to stderr: " - emulate_input('y\n') - click.confirm('Prompt to stdin') + emulate_input("y\n") + click.confirm("Prompt to stdin") out, err = capfd.readouterr() - assert out == 'Prompt to stdin [y/N]: ' - assert err == '' + assert out == "Prompt to stdin [y/N]: " + assert err == "" - emulate_input('y\n') - click.confirm('Prompt to stderr', err=True) + emulate_input("y\n") + click.confirm("Prompt to stderr", err=True) out, err = capfd.readouterr() - assert out == '' - assert err == 'Prompt to stderr [y/N]: ' + assert out == "" + assert err == "Prompt to stderr [y/N]: " - monkeypatch.setattr(click.termui, 'isatty', lambda x: True) - monkeypatch.setattr(click.termui, 'getchar', lambda: ' ') + monkeypatch.setattr(click.termui, "isatty", lambda x: True) + monkeypatch.setattr(click.termui, "getchar", lambda: " ") - click.pause('Pause to stdout') + click.pause("Pause to stdout") out, err = capfd.readouterr() - assert out == 'Pause to stdout\n' - assert err == '' + assert out == "Pause to stdout\n" + assert err == "" - click.pause('Pause to stderr', err=True) + click.pause("Pause to stderr", err=True) out, err = capfd.readouterr() - assert out == '' - assert err == 'Pause to stderr\n' + assert out == "" + assert err == "Pause to stderr\n" def test_open_file(runner): @click.command() - @click.argument('filename') + @click.argument("filename") def cli(filename): with click.open_file(filename) as f: click.echo(f.read()) - click.echo('meep') + click.echo("meep") with runner.isolated_filesystem(): - with open('hello.txt', 'w') as f: - f.write('Cool stuff') + with open("hello.txt", "w") as f: + f.write("Cool stuff") - result = runner.invoke(cli, ['hello.txt']) + result = runner.invoke(cli, ["hello.txt"]) assert result.exception is None - assert result.output == 'Cool stuff\nmeep\n' + assert result.output == "Cool stuff\nmeep\n" - result = runner.invoke(cli, ['-'], input='foobar') + result = runner.invoke(cli, ["-"], input="foobar") assert result.exception is None - assert result.output == 'foobar\nmeep\n' + assert result.output == "foobar\nmeep\n" def test_open_file_ignore_errors_stdin(runner): @@ -338,51 +339,47 @@ def test_open_file_ignore_no_encoding(runner): f.read() -@pytest.mark.skipif(WIN, reason='os.chmod() is not fully supported on Windows.') -@pytest.mark.parametrize('permissions', [ - 0o400, - 0o444, - 0o600, - 0o644, - ]) +@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) + with open("existing.txt", "w") as f: + f.write("content") + os.chmod("existing.txt", permissions) @click.command() - @click.argument('filename') + @click.argument("filename") def cli(filename): - click.open_file(filename, 'w', atomic=True).close() + click.open_file(filename, "w", atomic=True).close() - result = runner.invoke(cli, ['existing.txt']) + result = runner.invoke(cli, ["existing.txt"]) assert result.exception is None - assert stat.S_IMODE(os.stat('existing.txt').st_mode) == permissions + assert stat.S_IMODE(os.stat("existing.txt").st_mode) == permissions -@pytest.mark.skipif(WIN, reason='os.stat() is not fully supported on Windows.') +@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') + @click.argument("filename") def cli(filename): - click.open_file(filename, 'w', atomic=True).close() + 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'): + with open("test.txt", "w"): pass - permissions = stat.S_IMODE(os.stat('test.txt').st_mode) + permissions = stat.S_IMODE(os.stat("test.txt").st_mode) - result = runner.invoke(cli, ['new.txt']) + result = runner.invoke(cli, ["new.txt"]) assert result.exception is None - assert stat.S_IMODE(os.stat('new.txt').st_mode) == permissions + 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 = 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)): @@ -391,7 +388,7 @@ def test_iter_keepopenfile(tmpdir): def test_iter_lazyfile(tmpdir): expected = list(map(str, range(10))) - p = tmpdir.mkdir('testdir').join('testfile') + p = tmpdir.mkdir("testdir").join("testfile") p.write("\n".join(expected)) with p.open() as f: with click.utils.LazyFile(f.name) as lf: From 5f75f9ec38ccf2ddac4e6b92cc07fec6d9b1b7e0 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 6 Mar 2020 14:38:03 -0800 Subject: [PATCH 05/10] apply flake8 --- examples/aliases/setup.py | 2 +- examples/bashcompletion/bashcompletion.py | 3 +- examples/bashcompletion/setup.py | 2 +- examples/complex/setup.py | 2 +- examples/imagepipe/imagepipe.py | 2 +- examples/imagepipe/setup.py | 2 +- examples/inout/setup.py | 2 +- examples/naval/setup.py | 2 +- examples/repo/setup.py | 2 +- examples/validation/setup.py | 2 +- setup.cfg | 5 +-- src/click/_bashcomplete.py | 42 ++++++++++++++--------- src/click/_compat.py | 1 + src/click/core.py | 18 +++++----- src/click/decorators.py | 12 +++---- src/click/exceptions.py | 2 ++ src/click/globals.py | 2 +- src/click/termui.py | 2 +- src/click/testing.py | 2 +- src/click/types.py | 8 ++--- src/click/utils.py | 2 +- tests/test_arguments.py | 7 +--- tests/test_basic.py | 2 +- tests/test_commands.py | 5 +-- tests/test_options.py | 16 ++------- tests/test_termui.py | 39 +++++++++------------ tests/test_utils.py | 5 ++- 27 files changed, 91 insertions(+), 100 deletions(-) diff --git a/examples/aliases/setup.py b/examples/aliases/setup.py index 2ea791ecb..a3d04e700 100644 --- a/examples/aliases/setup.py +++ b/examples/aliases/setup.py @@ -5,7 +5,7 @@ version="1.0", py_modules=["aliases"], include_package_data=True, - install_requires=["click",], + install_requires=["click"], entry_points=""" [console_scripts] aliases=aliases:cli diff --git a/examples/bashcompletion/bashcompletion.py b/examples/bashcompletion/bashcompletion.py index c43706998..636cf643f 100644 --- a/examples/bashcompletion/bashcompletion.py +++ b/examples/bashcompletion/bashcompletion.py @@ -31,7 +31,8 @@ def list_users(ctx, args, incomplete): # You can generate completions with descriptions by returning # tuples in the form (completion, description). users = [("bob", "butcher"), ("alice", "baker"), ("jerry", "candlestick maker")] - # Ths will allow completion matches based on matches within the description string too! + # Ths will allow completion matches based on matches within the + # description string too! return [user for user in users if incomplete in user[0] or incomplete in user[1]] diff --git a/examples/bashcompletion/setup.py b/examples/bashcompletion/setup.py index 31e3cfa3c..f9a2c2934 100644 --- a/examples/bashcompletion/setup.py +++ b/examples/bashcompletion/setup.py @@ -5,7 +5,7 @@ version="1.0", py_modules=["bashcompletion"], include_package_data=True, - install_requires=["click",], + install_requires=["click"], entry_points=""" [console_scripts] bashcompletion=bashcompletion:cli diff --git a/examples/complex/setup.py b/examples/complex/setup.py index f776e5e82..afe97289f 100644 --- a/examples/complex/setup.py +++ b/examples/complex/setup.py @@ -5,7 +5,7 @@ version="1.0", packages=["complex", "complex.commands"], include_package_data=True, - install_requires=["click",], + install_requires=["click"], entry_points=""" [console_scripts] complex=complex.cli:cli diff --git a/examples/imagepipe/imagepipe.py b/examples/imagepipe/imagepipe.py index 924911654..2cde2f67e 100644 --- a/examples/imagepipe/imagepipe.py +++ b/examples/imagepipe/imagepipe.py @@ -235,7 +235,7 @@ def smoothen_cmd(images, iterations): 'Smoothening "%s" %d time%s' % (image.filename, iterations, iterations != 1 and "s" or "",) ) - for x in xrange(iterations): + for _ in range(iterations): image = copy_filename(image.filter(ImageFilter.BLUR), image) yield image diff --git a/examples/imagepipe/setup.py b/examples/imagepipe/setup.py index 80d5c8549..c42b5ff60 100644 --- a/examples/imagepipe/setup.py +++ b/examples/imagepipe/setup.py @@ -5,7 +5,7 @@ version="1.0", py_modules=["imagepipe"], include_package_data=True, - install_requires=["click", "pillow",], + install_requires=["click", "pillow"], entry_points=""" [console_scripts] imagepipe=imagepipe:cli diff --git a/examples/inout/setup.py b/examples/inout/setup.py index 2f4e5a3b5..ff673e3b9 100644 --- a/examples/inout/setup.py +++ b/examples/inout/setup.py @@ -5,7 +5,7 @@ version="0.1", py_modules=["inout"], include_package_data=True, - install_requires=["click",], + install_requires=["click"], entry_points=""" [console_scripts] inout=inout:cli diff --git a/examples/naval/setup.py b/examples/naval/setup.py index a3d93bb2e..37b39f5d2 100644 --- a/examples/naval/setup.py +++ b/examples/naval/setup.py @@ -5,7 +5,7 @@ version="2.0", py_modules=["naval"], include_package_data=True, - install_requires=["click",], + install_requires=["click"], entry_points=""" [console_scripts] naval=naval:cli diff --git a/examples/repo/setup.py b/examples/repo/setup.py index e0aa5b06e..3028020d8 100644 --- a/examples/repo/setup.py +++ b/examples/repo/setup.py @@ -5,7 +5,7 @@ version="0.1", py_modules=["repo"], include_package_data=True, - install_requires=["click",], + install_requires=["click"], entry_points=""" [console_scripts] repo=repo:cli diff --git a/examples/validation/setup.py b/examples/validation/setup.py index 80f2bf02b..b7698f61b 100644 --- a/examples/validation/setup.py +++ b/examples/validation/setup.py @@ -5,7 +5,7 @@ version="1.0", py_modules=["validation"], include_package_data=True, - install_requires=["click",], + install_requires=["click"], entry_points=""" [console_scripts] validation=validation:cli diff --git a/setup.cfg b/setup.cfg index 4335cbf77..c2d41cc84 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,8 +25,9 @@ source = # E = pycodestyle errors # F = flake8 pyflakes # W = pycodestyle warnings -# B9 = bugbear opinions -select = B, E, F, W, B9 +# B9 = bugbear opinions, +# ISC = implicit str concat +select = B, E, F, W, B9, ISC ignore = # slice notation whitespace, invalid E203 diff --git a/src/click/_bashcomplete.py b/src/click/_bashcomplete.py index bbd45a06c..815eb029f 100644 --- a/src/click/_bashcomplete.py +++ b/src/click/_bashcomplete.py @@ -30,7 +30,8 @@ local COMPLETION_OPTIONS="" local BASH_VERSION_ARR=(${BASH_VERSION//./ }) # Only BASH version 4.4 and later have the nosort option. - if [ ${BASH_VERSION_ARR[0]} -gt 4 ] || ([ ${BASH_VERSION_ARR[0]} -eq 4 ] && [ ${BASH_VERSION_ARR[1]} -ge 4 ]); then + if [ ${BASH_VERSION_ARR[0]} -gt 4 ] || ([ ${BASH_VERSION_ARR[0]} -eq 4 ] \ +&& [ ${BASH_VERSION_ARR[1]} -ge 4 ]); then COMPLETION_OPTIONS="-o nosort" fi @@ -75,9 +76,12 @@ compdef %(complete_func)s %(script_names)s """ -COMPLETION_SCRIPT_FISH = """ -complete --no-files --command %(script_names)s --arguments "(env %(autocomplete_var)s=complete_fish COMP_WORDS=(commandline -cp) COMP_CWORD=(commandline -t) %(script_names)s)" -""" +COMPLETION_SCRIPT_FISH = ( + "complete --no-files --command %(script_names)s --arguments" + ' "(env %(autocomplete_var)s=complete_fish' + " COMP_WORDS=(commandline -cp) COMP_CWORD=(commandline -t)" + ' %(script_names)s)"' +) _completion_scripts = { "bash": COMPLETION_SCRIPT_BASH, @@ -102,8 +106,9 @@ def get_completion_script(prog_name, complete_var, shell): def resolve_ctx(cli, prog_name, args): - """ - Parse into a hierarchy of contexts. Contexts are connected through the parent variable. + """Parse into a hierarchy of contexts. Contexts are connected + through the parent variable. + :param cli: command definition :param prog_name: the program that is running :param args: full list of args @@ -146,7 +151,8 @@ def resolve_ctx(cli, prog_name, args): def start_of_option(param_str): """ :param param_str: param_str to check - :return: whether or not this is the start of an option declaration (i.e. starts "-" or "--") + :return: whether or not this is the start of an option declaration + (i.e. starts "-" or "--") """ return param_str and param_str[:1] == "-" @@ -155,9 +161,10 @@ def is_incomplete_option(all_args, cmd_param): """ :param all_args: the full original list of args supplied :param cmd_param: the current command paramter - :return: whether or not the last option declaration (i.e. starts "-" or "--") is incomplete and - corresponds to this cmd_param. In other words whether this cmd_param option can still accept - values + :return: whether or not the last option declaration (i.e. starts + "-" or "--") is incomplete and corresponds to this cmd_param. In + other words whether this cmd_param option can still accept + values """ if not isinstance(cmd_param, Option): return False @@ -177,10 +184,12 @@ def is_incomplete_option(all_args, cmd_param): def is_incomplete_argument(current_params, cmd_param): """ - :param current_params: the current params and values for this argument as already entered + :param current_params: the current params and values for this + argument as already entered :param cmd_param: the current command parameter - :return: whether or not the last argument is incomplete and corresponds to this cmd_param. In - other words whether or not the this cmd_param argument can still accept values + :return: whether or not the last argument is incomplete and + corresponds to this cmd_param. In other words whether or not the + this cmd_param argument can still accept values """ if not isinstance(cmd_param, Argument): return False @@ -245,7 +254,8 @@ def add_subcommand_completions(ctx, incomplete, completions_out): ] ) - # Walk up the context list and add any other completion possibilities from chained commands + # Walk up the context list and add any other completion + # possibilities from chained commands while ctx.parent is not None: ctx = ctx.parent if isinstance(ctx.command, MultiCommand) and ctx.command.chain: @@ -275,8 +285,8 @@ def get_choices(cli, prog_name, args, incomplete): has_double_dash = "--" in all_args - # In newer versions of bash long opts with '='s are partitioned, but it's easier to parse - # without the '=' + # In newer versions of bash long opts with '='s are partitioned, but + # it's easier to parse without the '=' if start_of_option(incomplete) and WORDBREAK in incomplete: partition_incomplete = incomplete.partition(WORDBREAK) all_args.append(partition_incomplete[0]) diff --git a/src/click/_compat.py b/src/click/_compat.py index 6431fc2fd..5caa5932b 100644 --- a/src/click/_compat.py +++ b/src/click/_compat.py @@ -1,3 +1,4 @@ +# flake8: noqa import codecs import io import os diff --git a/src/click/core.py b/src/click/core.py index ea46e8687..937d747d3 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -42,9 +42,7 @@ SUBCOMMANDS_METAVAR = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." DEPRECATED_HELP_NOTICE = " (DEPRECATED)" -DEPRECATED_INVOKE_NOTICE = ( - "DeprecationWarning: " + "The command %(name)s is deprecated." -) +DEPRECATED_INVOKE_NOTICE = "DeprecationWarning: The command %(name)s is deprecated." def _maybe_show_deprecated_notice(cmd): @@ -578,7 +576,7 @@ def get_help(self): """ return self.command.get_help(self) - def invoke(*args, **kwargs): + def invoke(*args, **kwargs): # noqa: B902 """Invokes a command callback in exactly the way it expects. There are two ways to invoke this method: @@ -618,7 +616,7 @@ def invoke(*args, **kwargs): with ctx: return callback(*args, **kwargs) - def forward(*args, **kwargs): + def forward(*args, **kwargs): # noqa: B902 """Similar to :meth:`invoke` but fills in default keyword arguments from the current context if the other command expects it. This cannot invoke callbacks directly, only other commands. @@ -985,7 +983,9 @@ def get_help(self, ctx): return formatter.getvalue().rstrip("\n") def get_short_help_str(self, limit=45): - """Gets short help for the command or makes it by shortening the long help string.""" + """Gets short help for the command or makes it by shortening the + long help string. + """ return ( self.short_help or self.help @@ -1362,6 +1362,7 @@ def command(self, *args, **kwargs): immediately registers the created command with this instance by calling into :meth:`add_command`. """ + from .decorators import command def decorator(f): cmd = command(*args, **kwargs)(f) @@ -1376,6 +1377,7 @@ def group(self, *args, **kwargs): immediately registers the created command with this instance by calling into :meth:`add_command`. """ + from .decorators import group def decorator(f): cmd = group(*args, **kwargs)(f) @@ -2037,7 +2039,3 @@ def get_error_hint(self, ctx): def add_to_parser(self, parser, ctx): parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) - - -# Circular dependency between decorators and core -from .decorators import command, group diff --git a/src/click/decorators.py b/src/click/decorators.py index 5f556e2c9..7e10adb60 100644 --- a/src/click/decorators.py +++ b/src/click/decorators.py @@ -4,6 +4,10 @@ from ._compat import iteritems from ._unicodefun import _check_for_unicode_literals +from .core import Argument +from .core import Command +from .core import Group +from .core import Option from .globals import get_current_context from .utils import echo @@ -283,13 +287,13 @@ def callback(ctx, param, value): else: for dist in pkg_resources.working_set: scripts = dist.get_entry_map().get("console_scripts") or {} - for script_name, entry_point in iteritems(scripts): + for _, entry_point in iteritems(scripts): if entry_point.module_name == module: ver = dist.version break if ver is None: raise RuntimeError("Could not determine version") - echo(message % {"prog": prog, "version": ver,}, color=ctx.color) + echo(message % {"prog": prog, "version": ver}, color=ctx.color) ctx.exit() attrs.setdefault("is_flag", True) @@ -327,7 +331,3 @@ def callback(ctx, param, value): return option(*(param_decls or ("--help",)), **attrs)(f) return decorator - - -# Circular dependencies between core and decorators -from .core import Command, Group, Argument, Option diff --git a/src/click/exceptions.py b/src/click/exceptions.py index a06400b13..2e2199b28 100644 --- a/src/click/exceptions.py +++ b/src/click/exceptions.py @@ -248,5 +248,7 @@ class Exit(RuntimeError): :param code: the status code to exit with. """ + __slots__ = ("exit_code",) + def __init__(self, code=0): self.exit_code = code diff --git a/src/click/globals.py b/src/click/globals.py index a0157fce5..1649f9a0b 100644 --- a/src/click/globals.py +++ b/src/click/globals.py @@ -19,7 +19,7 @@ def get_current_context(silent=False): :exc:`RuntimeError`. """ try: - return getattr(_local, "stack")[-1] + return _local.stack[-1] except (AttributeError, IndexError): if not silent: raise RuntimeError("There is no active click context.") diff --git a/src/click/termui.py b/src/click/termui.py index eb1baa819..d5239e778 100644 --- a/src/click/termui.py +++ b/src/click/termui.py @@ -156,7 +156,7 @@ def prompt_func(text): try: result = value_proc(value) except UsageError as e: - echo("Error: %s" % e.message, err=err) + echo("Error: %s" % e.message, err=err) # noqa: B306 continue if not confirmation_prompt: return result diff --git a/src/click/testing.py b/src/click/testing.py index c50418fac..6ad06fb1d 100644 --- a/src/click/testing.py +++ b/src/click/testing.py @@ -382,5 +382,5 @@ def isolated_filesystem(self): os.chdir(cwd) try: shutil.rmtree(t) - except (OSError, IOError): + except (OSError, IOError): # noqa: B014 pass diff --git a/src/click/types.py b/src/click/types.py index 1d3d5b319..098719031 100644 --- a/src/click/types.py +++ b/src/click/types.py @@ -261,7 +261,7 @@ class IntParamType(ParamType): def convert(self, value, param, ctx): try: return int(value) - except (ValueError, UnicodeError): + except ValueError: self.fail("%s is not a valid integer" % value, param, ctx) def __repr__(self): @@ -329,7 +329,7 @@ class FloatParamType(ParamType): def convert(self, value, param, ctx): try: return float(value) - except (UnicodeError, ValueError): + except ValueError: self.fail("%s is not a valid floating point value" % value, param, ctx) def __repr__(self): @@ -418,7 +418,7 @@ def convert(self, value, param, ctx): if PY2 and isinstance(value, text_type): value = value.encode("ascii") return uuid.UUID(value) - except (UnicodeError, ValueError): + except ValueError: self.fail("%s is not a valid UUID value" % value, param, ctx) def __repr__(self): @@ -502,7 +502,7 @@ def convert(self, value, param, ctx): else: ctx.call_on_close(safecall(f.flush)) return f - except (IOError, OSError) as e: + except (IOError, OSError) as e: # noqa: B014 self.fail( "Could not open file: %s: %s" % (filename_to_ui(value), get_streerror(e),), diff --git a/src/click/utils.py b/src/click/utils.py index 210bbe4bf..4e97d8487 100644 --- a/src/click/utils.py +++ b/src/click/utils.py @@ -126,7 +126,7 @@ def open(self): rv, self.should_close = open_stream( self.name, self.mode, self.encoding, self.errors, atomic=self.atomic ) - except (IOError, OSError) as e: + except (IOError, OSError) as e: # noqa: E402 from .exceptions import FileError raise FileError(self.name, hint=get_streerror(e)) diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 7b214ad71..85ba21246 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -25,18 +25,13 @@ def copy(src, dst): def test_nargs_default(runner): - try: + with pytest.raises(TypeError, match="nargs=-1"): @click.command() @click.argument("src", nargs=-1, default=42) def copy(src): pass - except TypeError as e: - assert "nargs=-1" in str(e) - else: - assert False - def test_nargs_tup(runner): @click.command() diff --git a/tests/test_basic.py b/tests/test_basic.py index 82c7e5a75..59566cbea 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -49,7 +49,7 @@ def cli(): with cli.make_context("foo", []) as ctx: rv = cli.invoke(ctx) - assert rv is 42 + assert rv == 42 def test_basic_group(runner): diff --git a/tests/test_commands.py b/tests/test_commands.py index d53b724ab..bcb30bd14 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -62,7 +62,8 @@ def long(): assert ( re.search( r"Commands:\n\s+" - r"long\s+This is a long text that is too long to show as short help\.\.\.\n\s+" + r"long\s+This is a long text that is too long to show as short help" + r"\.\.\.\n\s+" r"short\s+This is a short text\.\n\s+" r"special-chars\s+Login and store the token in ~/.netrc\.\s*", result.output, @@ -294,7 +295,7 @@ def cmd_without_help(): def test_deprecated_in_invocation(runner): @click.command(deprecated=True) def deprecated_cmd(): - debug() + pass result = runner.invoke(deprecated_cmd) assert "DeprecationWarning:" in result.output diff --git a/tests/test_options.py b/tests/test_options.py index bdf1b956e..0cdb064ff 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -25,32 +25,22 @@ def cli(foo, bar): def test_invalid_option(runner): - try: + with pytest.raises(TypeError, match="name was passed"): @click.command() @click.option("foo") def cli(foo): pass - except TypeError as e: - assert "No options defined but a name was passed (foo)." in str(e) - else: - assert False, "Expected a type error because of an invalid option." - def test_invalid_nargs(runner): - try: + with pytest.raises(TypeError, match="nargs < 0"): @click.command() @click.option("--foo", nargs=-1) def cli(foo): pass - except TypeError as e: - assert "Options cannot have nargs < 0" in str(e) - else: - assert False, "Expected a type error because of an invalid option." - def test_nargs_tup_composite_mult(runner): @click.command() @@ -426,7 +416,7 @@ def test_option_help_preserve_paragraphs(runner): "--config", type=click.Path(), help="""Configuration file to use. - + If not given, the environment variable CONFIG_FILE is consulted and used if set. If neither are given, a default configuration file is loaded.""", diff --git a/tests/test_termui.py b/tests/test_termui.py index d6451cac6..c1cbd2877 100644 --- a/tests/test_termui.py +++ b/tests/test_termui.py @@ -33,7 +33,7 @@ def test_progressbar_strip_regression(runner, monkeypatch): @click.command() def cli(): with _create_progress(label=label) as progress: - for thing in progress: + for _ in progress: fake_clock.advance_time() monkeypatch.setattr(time, "time", fake_clock.time) @@ -65,7 +65,7 @@ def __next__(self): @click.command() def cli(): with click.progressbar(Hinted(10), label="test") as progress: - for thing in progress: + for _ in progress: fake_clock.advance_time() monkeypatch.setattr(time, "time", fake_clock.time) @@ -81,7 +81,7 @@ def test_progressbar_hidden(runner, monkeypatch): @click.command() def cli(): with _create_progress(label=label) as progress: - for thing in progress: + for _ in progress: fake_clock.advance_time() monkeypatch.setattr(time, "time", fake_clock.time) @@ -183,22 +183,14 @@ def item_show_func(item): def test_progressbar_init_exceptions(runner): - try: + with pytest.raises(TypeError, match="iterable or length is required"): click.progressbar() - except TypeError as e: - assert str(e) == "iterable or length is required" - else: - assert False, "Expected an exception because unspecified arguments" def test_progressbar_iter_outside_with_exceptions(runner): - try: + with pytest.raises(RuntimeError, match="with block"): progress = click.progressbar(length=2) iter(progress) - except RuntimeError as e: - assert str(e) == "You need to use progress bars in a with block." - else: - assert False, "Expected an exception because of abort-related inputs." def test_progressbar_is_iterator(runner, monkeypatch): @@ -247,7 +239,7 @@ def cli_without_choices(g): @pytest.mark.parametrize( - "file_kwargs", [{"mode": "rt"}, {"mode": "rb"}, {"lazy": True},] + "file_kwargs", [{"mode": "rt"}, {"mode": "rb"}, {"lazy": True}] ) def test_file_prompt_default_format(runner, file_kwargs): @click.command() @@ -318,16 +310,17 @@ def test_getchar_special_key_windows(runner, monkeypatch, special_key_char, key_ assert click.getchar() == special_key_char + key_char -@pytest.mark.parametrize("key_char", [u"\x03", u"\x1a"]) +@pytest.mark.parametrize( + ("key_char", "exc"), + [ + (u"\x03", KeyboardInterrupt), + (u"\x1a", EOFError), + ], +) @pytest.mark.skipif(not WIN, reason="Tests user-input using the msvcrt module.") -def test_getchar_windows_exceptions(runner, monkeypatch, key_char): +def test_getchar_windows_exceptions(runner, monkeypatch, key_char, exc): monkeypatch.setattr(click._termui_impl.msvcrt, "getwch", lambda: key_char) monkeypatch.setattr(click.termui, "_getchar", None) - try: + + with pytest.raises(exc): click.getchar() - except KeyboardInterrupt: - assert key_char == u"\x03" - except EOFError: - assert key_char == u"\x1a" - else: - assert False, "Expected an exception because of abort-specific inputs." diff --git a/tests/test_utils.py b/tests/test_utils.py index 832571a29..52472249b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -6,7 +6,6 @@ import click._termui_impl import click.utils -from click._compat import PY2 from click._compat import WIN @@ -84,7 +83,7 @@ def test_styling(styles, ref): assert click.unstyle(ref) == "x y" -@pytest.mark.parametrize(("text", "expect"), [("\x1b[?25lx y\x1b[?25h", "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 @@ -340,7 +339,7 @@ def test_open_file_ignore_no_encoding(runner): @pytest.mark.skipif(WIN, reason="os.chmod() is not fully supported on Windows.") -@pytest.mark.parametrize("permissions", [0o400, 0o444, 0o600, 0o644,]) +@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: From a1de45c1aa4bd87c6516755de61b7ca52e135462 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 6 Mar 2020 14:42:50 -0800 Subject: [PATCH 06/10] more relative imports --- src/click/_unicodefun.py | 11 ++++------- src/click/testing.py | 39 ++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/click/_unicodefun.py b/src/click/_unicodefun.py index 4d0a169e3..01c4bc303 100644 --- a/src/click/_unicodefun.py +++ b/src/click/_unicodefun.py @@ -4,12 +4,6 @@ from ._compat import PY2 -# If someone wants to vendor click, we want to ensure the -# correct package is discovered. Ideally we could use a -# relative import here but unfortunately Python does not -# support that. -click = sys.modules[__name__.rsplit(".", 1)[0]] - def _find_unicode_literals_frame(): import __future__ @@ -32,7 +26,10 @@ def _find_unicode_literals_frame(): def _check_for_unicode_literals(): if not __debug__: return - if not PY2 or click.disable_unicode_literals_warning: + + from . import disable_unicode_literals_warning + + if not PY2 or disable_unicode_literals_warning: return bad_frame = _find_unicode_literals_frame() if bad_frame <= 0: diff --git a/src/click/testing.py b/src/click/testing.py index 6ad06fb1d..8131a7ad7 100644 --- a/src/click/testing.py +++ b/src/click/testing.py @@ -5,16 +5,13 @@ import sys import tempfile +from . import formatting +from . import termui +from . import utils from ._compat import iteritems from ._compat import PY2 from ._compat import string_types -# If someone wants to vendor click, we want to ensure the -# correct package is discovered. Ideally we could use a -# relative import here but unfortunately Python does not -# support that. -clickpkg = sys.modules[__name__.rsplit(".", 1)[0]] - if PY2: from cStringIO import StringIO @@ -183,8 +180,8 @@ def isolation(self, input=None, env=None, color=False): old_stdin = sys.stdin old_stdout = sys.stdout old_stderr = sys.stderr - old_forced_width = clickpkg.formatting.FORCED_WIDTH - clickpkg.formatting.FORCED_WIDTH = 80 + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 env = self.make_env(env) @@ -237,14 +234,14 @@ def should_strip_ansi(stream=None, color=None): return not default_color return not color - old_visible_prompt_func = clickpkg.termui.visible_prompt_func - old_hidden_prompt_func = clickpkg.termui.hidden_prompt_func - old__getchar_func = clickpkg.termui._getchar - old_should_strip_ansi = clickpkg.utils.should_strip_ansi - clickpkg.termui.visible_prompt_func = visible_input - clickpkg.termui.hidden_prompt_func = hidden_input - clickpkg.termui._getchar = _getchar - clickpkg.utils.should_strip_ansi = should_strip_ansi + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi old_env = {} try: @@ -270,11 +267,11 @@ def should_strip_ansi(stream=None, color=None): sys.stdout = old_stdout sys.stderr = old_stderr sys.stdin = old_stdin - clickpkg.termui.visible_prompt_func = old_visible_prompt_func - clickpkg.termui.hidden_prompt_func = old_hidden_prompt_func - clickpkg.termui._getchar = old__getchar_func - clickpkg.utils.should_strip_ansi = old_should_strip_ansi - clickpkg.formatting.FORCED_WIDTH = old_forced_width + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi + formatting.FORCED_WIDTH = old_forced_width def invoke( self, From 8fe37afda8a3fe34f87c9936e72bcd02c0eb77af Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 6 Mar 2020 14:49:46 -0800 Subject: [PATCH 07/10] apply formatting --- .github/ISSUE_TEMPLATE.md | 2 +- CONTRIBUTING.rst | 2 +- docs/arguments.rst | 1 - src/click/_compat.py | 4 +--- src/click/core.py | 8 ++------ tests/test_termui.py | 6 +----- 6 files changed, 6 insertions(+), 17 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 8f31ae94e..f9b0cf9ba 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,5 +1,5 @@ **This issue tracker is a tool to address bugs in Click itself. -Please use the #pocoo IRC channel on freenode, the Discord server or +Please use the #pocoo IRC channel on freenode, the Discord server or Stack Overflow for general questions about using Flask or issues not related to Click.** diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2a2fe73a7..c96223889 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -11,7 +11,7 @@ Please, don't use the issue tracker for this. Check whether the ``#pocoo`` IRC channel on Freenode can help with your issue. If your problem is not strictly Click-specific, ``#python`` on Freenode is generally more active. Also try searching or asking on `Stack Overflow`_ with the -``python-click`` tag. +``python-click`` tag. .. _Stack Overflow: https://stackoverflow.com/questions/tagged/python-click?sort=votes diff --git a/docs/arguments.rst b/docs/arguments.rst index b9d2f461b..6ae35388c 100644 --- a/docs/arguments.rst +++ b/docs/arguments.rst @@ -267,4 +267,3 @@ And from the command line: .. click:run:: invoke(touch, ['-foo.txt', 'bar.txt']) - diff --git a/src/click/_compat.py b/src/click/_compat.py index 5caa5932b..f7b1a8d75 100644 --- a/src/click/_compat.py +++ b/src/click/_compat.py @@ -428,9 +428,7 @@ def _force_correct_text_writer(text_writer, encoding, errors, force_writable=Fal def get_binary_stdin(): reader = _find_binary_reader(sys.stdin) if reader is None: - raise RuntimeError( - "Was not able to determine binary stream for sys.stdin." - ) + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") return reader def get_binary_stdout(): diff --git a/src/click/core.py b/src/click/core.py index 937d747d3..ce6078100 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -712,9 +712,7 @@ def parse_args(self, ctx, args): and parses the arguments, then modifies the context as necessary. This is automatically invoked by :meth:`make_context`. """ - raise NotImplementedError( - "Base commands do not know how to parse arguments." - ) + raise NotImplementedError("Base commands do not know how to parse arguments.") def invoke(self, ctx): """Given a context, this invokes the command. The default @@ -1774,9 +1772,7 @@ def __init__( if not self.is_bool_flag and self.secondary_opts: raise TypeError("Got secondary option for non boolean flag.") if self.is_bool_flag and self.hide_input and self.prompt is not None: - raise TypeError( - "Hidden input does not work with boolean flag prompts." - ) + raise TypeError("Hidden input does not work with boolean flag prompts.") if self.count: if self.multiple: raise TypeError( diff --git a/tests/test_termui.py b/tests/test_termui.py index c1cbd2877..4e33f412f 100644 --- a/tests/test_termui.py +++ b/tests/test_termui.py @@ -311,11 +311,7 @@ def test_getchar_special_key_windows(runner, monkeypatch, special_key_char, key_ @pytest.mark.parametrize( - ("key_char", "exc"), - [ - (u"\x03", KeyboardInterrupt), - (u"\x1a", EOFError), - ], + ("key_char", "exc"), [(u"\x03", KeyboardInterrupt), (u"\x1a", EOFError)], ) @pytest.mark.skipif(not WIN, reason="Tests user-input using the msvcrt module.") def test_getchar_windows_exceptions(runner, monkeypatch, key_char, exc): From 30b82bfdebcc155acc1a63716ae8fd79a23f73e5 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sat, 7 Mar 2020 09:03:16 -0800 Subject: [PATCH 08/10] run pre-commit during tests --- .azure-pipelines.yml | 18 +++--------------- tox.ini | 6 ++++++ 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index d88465a5e..86e701017 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -30,6 +30,8 @@ strategy: python.version: '2.7' Docs: TOXENV: docs + Style: + TOXENV: style pool: vmImage: $(vmImage) @@ -43,19 +45,5 @@ steps: - script: pip --disable-pip-version-check install -U tox displayName: Install tox - - script: tox -s false -- --junit-xml=test-results.xml + - script: tox displayName: Run tox - - - task: PublishTestResults@2 - inputs: - testResultsFiles: test-results.xml - testRunTitle: $(Agent.JobName) - condition: eq(variables['hasTestResults'], 'true') - displayName: Publish test results - - - task: PublishCodeCoverageResults@1 - inputs: - codeCoverageTool: Cobertura - summaryFileLocation: coverage.xml - condition: eq(variables['hasTestResults'], 'true') - displayName: Publish coverage results diff --git a/tox.ini b/tox.ini index 30c16dad3..beed51d92 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,7 @@ [tox] envlist = py{38,37,36,35,27,py3,py} + style docs skip_missing_interpreters = true @@ -10,6 +11,11 @@ deps = colorama commands = pytest --tb=short --basetemp={envtmpdir} {posargs} +[testenv:style] +deps = pre-commit +skip_install = true +commands = pre-commit run --all-files --show-diff-on-failure + [testenv:docs] deps = -r docs/requirements.txt commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html From f8f02bfc63cb6e63b7a3373384758f7226553408 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 8 Mar 2020 08:29:54 -0700 Subject: [PATCH 09/10] apply pyupgrade --- examples/aliases/aliases.py | 2 +- examples/imagepipe/imagepipe.py | 14 +++++----- examples/naval/naval.py | 8 +++--- examples/repo/repo.py | 8 +++--- src/click/_bashcomplete.py | 2 +- src/click/_compat.py | 2 +- src/click/_termui_impl.py | 14 +++++----- src/click/_winconsole.py | 4 ++- src/click/core.py | 15 +++++------ src/click/exceptions.py | 11 ++++---- src/click/parser.py | 2 +- src/click/termui.py | 2 +- src/click/testing.py | 5 ++-- src/click/types.py | 26 +++++++++++++------ src/click/utils.py | 2 +- tests/test_imports.py | 46 ++++++++++++++++----------------- tests/test_options.py | 4 +-- tests/test_termui.py | 2 +- 18 files changed, 89 insertions(+), 80 deletions(-) diff --git a/examples/aliases/aliases.py b/examples/aliases/aliases.py index 8456a1b64..1ebad50c1 100644 --- a/examples/aliases/aliases.py +++ b/examples/aliases/aliases.py @@ -139,4 +139,4 @@ def alias(config, alias_, cmd, config_file): """Adds an alias to the specified configuration file.""" config.add_alias(alias_, cmd) config.write_config(config_file) - click.echo("Added %s as alias for %s" % (alias_, cmd)) + click.echo("Added {} as alias for {}".format(alias_, cmd)) diff --git a/examples/imagepipe/imagepipe.py b/examples/imagepipe/imagepipe.py index 2cde2f67e..5e34405e1 100644 --- a/examples/imagepipe/imagepipe.py +++ b/examples/imagepipe/imagepipe.py @@ -97,7 +97,7 @@ def open_cmd(images): img = Image.open(image) yield img except Exception as e: - click.echo('Could not open image "%s": %s' % (image, e), err=True) + click.echo('Could not open image "{}": {}'.format(image, e), err=True) @cli.command("save") @@ -114,10 +114,12 @@ def save_cmd(images, filename): for idx, image in enumerate(images): try: fn = filename % (idx + 1) - click.echo('Saving "%s" as "%s"' % (image.filename, fn)) + click.echo('Saving "{}" as "{}"'.format(image.filename, fn)) yield image.save(fn) except Exception as e: - click.echo('Could not save image "%s": %s' % (image.filename, e), err=True) + click.echo( + 'Could not save image "{}": {}'.format(image.filename, e), err=True + ) @cli.command("display") @@ -203,7 +205,7 @@ def transpose_cmd(images, rotate, flip): image = copy_filename(image.transpose(mode), image) if flip is not None: mode, direction = flip - click.echo('Flip "%s" %s' % (image.filename, direction)) + click.echo('Flip "{}" {}'.format(image.filename, direction)) image = copy_filename(image.transpose(mode), image) yield image @@ -257,7 +259,7 @@ def emboss_cmd(images): def sharpen_cmd(images, factor): """Sharpens an image.""" for image in images: - click.echo('Sharpen "%s" by %f' % (image.filename, factor)) + click.echo('Sharpen "{}" by {:f}'.format(image.filename, factor)) enhancer = ImageEnhance.Sharpness(image) yield copy_filename(enhancer.enhance(max(1.0, factor)), image) @@ -279,7 +281,7 @@ def paste_cmd(images, left, right): yield image return - click.echo('Paste "%s" on "%s"' % (to_paste.filename, image.filename)) + click.echo('Paste "{}" on "{}"'.format(to_paste.filename, image.filename)) mask = None if to_paste.mode == "RGBA" or "transparency" in to_paste.info: mask = to_paste diff --git a/examples/naval/naval.py b/examples/naval/naval.py index cf9e00c64..a77dea8bc 100644 --- a/examples/naval/naval.py +++ b/examples/naval/naval.py @@ -31,7 +31,7 @@ def ship_new(name): @click.option("--speed", metavar="KN", default=10, help="Speed in knots.") def ship_move(ship, x, y, speed): """Moves SHIP to the new location X,Y.""" - click.echo("Moving ship %s to %s,%s with speed %s" % (ship, x, y, speed)) + click.echo("Moving ship {} to {},{} with speed {}".format(ship, x, y, speed)) @ship.command("shoot") @@ -40,7 +40,7 @@ def ship_move(ship, x, y, speed): @click.argument("y", type=float) def ship_shoot(ship, x, y): """Makes SHIP fire to X,Y.""" - click.echo("Ship %s fires to %s,%s" % (ship, x, y)) + click.echo("Ship {} fires to {},{}".format(ship, x, y)) @cli.group("mine") @@ -61,7 +61,7 @@ def mine(): @click.option("ty", "--drifting", flag_value="drifting", help="Drifting mine.") def mine_set(x, y, ty): """Sets a mine at a specific coordinate.""" - click.echo("Set %s mine at %s,%s" % (ty, x, y)) + click.echo("Set {} mine at {},{}".format(ty, x, y)) @mine.command("remove") @@ -69,4 +69,4 @@ def mine_set(x, y, ty): @click.argument("y", type=float) def mine_remove(x, y): """Removes a mine at a specific coordinate.""" - click.echo("Removed mine at %s,%s" % (x, y)) + click.echo("Removed mine at {},{}".format(x, y)) diff --git a/examples/repo/repo.py b/examples/repo/repo.py index b9bf2f0e7..470296bfd 100644 --- a/examples/repo/repo.py +++ b/examples/repo/repo.py @@ -14,7 +14,7 @@ def __init__(self, home): def set_config(self, key, value): self.config[key] = value if self.verbose: - click.echo(" config[%s] = %s" % (key, value), file=sys.stderr) + click.echo(" config[{}] = {}".format(key, value), file=sys.stderr) def __repr__(self): return "" % self.home @@ -78,7 +78,7 @@ def clone(repo, src, dest, shallow, rev): """ if dest is None: dest = posixpath.split(src)[-1] or "." - click.echo("Cloning repo %s to %s" % (src, os.path.abspath(dest))) + click.echo("Cloning repo {} to {}".format(src, os.path.abspath(dest))) repo.home = dest if shallow: click.echo("Making shallow checkout") @@ -147,7 +147,7 @@ def commit(repo, files, message): return else: msg = "\n".join(message) - click.echo("Files to be committed: %s" % (files,)) + click.echo("Files to be committed: {}".format(files)) click.echo("Commit message:\n" + msg) @@ -163,4 +163,4 @@ def copy(repo, src, dst, force): files from SRC to DST. """ for fn in src: - click.echo("Copy from %s -> %s" % (fn, dst)) + click.echo("Copy from {} -> {}".format(fn, dst)) diff --git a/src/click/_bashcomplete.py b/src/click/_bashcomplete.py index 815eb029f..31138087b 100644 --- a/src/click/_bashcomplete.py +++ b/src/click/_bashcomplete.py @@ -349,7 +349,7 @@ def do_complete_fish(cli, prog_name): for item in get_choices(cli, prog_name, args, incomplete): if item[1]: - echo("%(arg)s\t%(desc)s" % {"arg": item[0], "desc": item[1]}) + echo("{arg}\t{desc}".format(arg=item[0], desc=item[1])) else: echo(item[0]) diff --git a/src/click/_compat.py b/src/click/_compat.py index f7b1a8d75..38ab8c0a7 100644 --- a/src/click/_compat.py +++ b/src/click/_compat.py @@ -565,7 +565,7 @@ def open_stream(filename, mode="r", encoding=None, errors="strict", atomic=False while True: tmp_filename = os.path.join( os.path.dirname(filename), - ".__atomic-write%08x" % (random.randrange(1 << 32),), + ".__atomic-write{:08x}".format(random.randrange(1 << 32)), ) try: fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) diff --git a/src/click/_termui_impl.py b/src/click/_termui_impl.py index 784c64b2d..28a0d9c70 100644 --- a/src/click/_termui_impl.py +++ b/src/click/_termui_impl.py @@ -416,7 +416,7 @@ def _tempfilepager(generator, cmd, color): with open_stream(filename, "wb")[0] as f: f.write(text.encode(encoding)) try: - os.system("%s %s" % (shlex_quote(cmd), shlex_quote(filename))) + os.system("{} {}".format(shlex_quote(cmd), shlex_quote(filename))) finally: os.unlink(filename) @@ -461,7 +461,7 @@ def edit_file(self, filename): environ = None try: c = subprocess.Popen( - "%s %s" % (shlex_quote(editor), shlex_quote(filename)), + "{} {}".format(shlex_quote(editor), shlex_quote(filename)), env=environ, shell=True, ) @@ -469,7 +469,7 @@ def edit_file(self, filename): if exit_code != 0: raise ClickException("%s: Editing failed!" % editor) except OSError as e: - raise ClickException("%s: Editing failed: %s" % (editor, e)) + raise ClickException("{}: Editing failed: {}".format(editor, e)) def edit(self, text): import tempfile @@ -534,16 +534,16 @@ def _unquote_file(url): elif WIN: if locate: url = _unquote_file(url) - args = "explorer /select,%s" % (shlex_quote(url),) + args = "explorer /select,{}".format(shlex_quote(url)) else: - args = 'start %s "" %s' % ("/WAIT" if wait else "", shlex_quote(url)) + args = 'start {} "" {}'.format("/WAIT" if wait else "", shlex_quote(url)) return os.system(args) elif CYGWIN: if locate: url = _unquote_file(url) - args = "cygstart %s" % (shlex_quote(os.path.dirname(url)),) + args = "cygstart {}".format(shlex_quote(os.path.dirname(url))) else: - args = "cygstart %s %s" % ("-w" if wait else "", shlex_quote(url)) + args = "cygstart {} {}".format("-w" if wait else "", shlex_quote(url)) return os.system(args) try: diff --git a/src/click/_winconsole.py b/src/click/_winconsole.py index ca43502e5..7b5be3484 100644 --- a/src/click/_winconsole.py +++ b/src/click/_winconsole.py @@ -223,7 +223,9 @@ def isatty(self): return self.buffer.isatty() def __repr__(self): - return "" % (self.name, self.encoding,) + return "".format( + self.name, self.encoding + ) class WindowsChunkedWriter(object): diff --git a/src/click/core.py b/src/click/core.py index ce6078100..3537e59db 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -379,9 +379,8 @@ def __init__( and parent.auto_envvar_prefix is not None and self.info_name is not None ): - auto_envvar_prefix = "%s_%s" % ( - parent.auto_envvar_prefix, - self.info_name.upper(), + auto_envvar_prefix = "{}_{}".format( + parent.auto_envvar_prefix, self.info_name.upper(), ) else: auto_envvar_prefix = auto_envvar_prefix.upper() @@ -676,7 +675,7 @@ def __init__(self, name, context_settings=None): self.context_settings = context_settings def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, self.name) + return "<{} {}>".format(self.__class__.__name__, self.name) def get_usage(self, ctx): raise NotImplementedError("Base commands cannot get usage") @@ -1510,7 +1509,7 @@ def __init__( self.autocompletion = autocompletion def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, self.name) + return "<{} {}>".format(self.__class__.__name__, self.name) @property def human_readable_name(self): @@ -1883,7 +1882,7 @@ def _write_opts(opts): envvar = self.envvar if envvar is None: if self.allow_from_autoenv and ctx.auto_envvar_prefix is not None: - envvar = "%s_%s" % (ctx.auto_envvar_prefix, self.name.upper()) + envvar = "{}_{}".format(ctx.auto_envvar_prefix, self.name.upper()) if envvar is not None: extra.append( "env var: %s" @@ -1907,7 +1906,7 @@ def _write_opts(opts): if self.required: extra.append("required") if extra: - help = "%s[%s]" % (help and help + " " or "", "; ".join(extra)) + help = "{}[{}]".format(help and help + " " or "", "; ".join(extra)) return ((any_prefix_is_slash and "; " or " / ").join(rv), help) @@ -1952,7 +1951,7 @@ def resolve_envvar_value(self, ctx): if rv is not None: return rv if self.allow_from_autoenv and ctx.auto_envvar_prefix is not None: - envvar = "%s_%s" % (ctx.auto_envvar_prefix, self.name.upper()) + envvar = "{}_{}".format(ctx.auto_envvar_prefix, self.name.upper()) return os.environ.get(envvar) def value_from_envvar(self, ctx): diff --git a/src/click/exceptions.py b/src/click/exceptions.py index 2e2199b28..5f5bf582f 100644 --- a/src/click/exceptions.py +++ b/src/click/exceptions.py @@ -64,9 +64,8 @@ def show(self, file=None): color = None hint = "" if self.cmd is not None and self.cmd.get_help_option(self.ctx) is not None: - hint = 'Try "%s %s" for help.\n' % ( - self.ctx.command_path, - self.ctx.help_option_names[0], + hint = 'Try "{} {}" for help.\n'.format( + self.ctx.command_path, self.ctx.help_option_names[0], ) if self.ctx is not None: color = self.ctx.color @@ -106,7 +105,7 @@ def format_message(self): return "Invalid value: %s" % self.message param_hint = _join_param_hints(param_hint) - return "Invalid value for %s: %s" % (param_hint, self.message) + return "Invalid value for {}: {}".format(param_hint, self.message) class MissingParameter(BadParameter): @@ -149,7 +148,7 @@ def format_message(self): else: msg = msg_extra - return "Missing %s%s%s%s" % ( + return "Missing {}{}{}{}".format( param_type, param_hint and " %s" % param_hint or "", msg and ". " or ".", @@ -234,7 +233,7 @@ def __init__(self, filename, hint=None): self.filename = filename def format_message(self): - return "Could not open file %s: %s" % (self.ui_filename, self.message) + return "Could not open file {}: {}".format(self.ui_filename, self.message) class Abort(RuntimeError): diff --git a/src/click/parser.py b/src/click/parser.py index 16a41eb33..b493ed4ce 100644 --- a/src/click/parser.py +++ b/src/click/parser.py @@ -225,7 +225,7 @@ def __init__(self, ctx=None): self.ignore_unknown_options = ctx.ignore_unknown_options self._short_opt = {} self._long_opt = {} - self._opt_prefixes = set(["-", "--"]) + self._opt_prefixes = {"-", "--"} self._args = [] def add_option(self, opts, dest, action=None, nargs=1, const=None, obj=None): diff --git a/src/click/termui.py b/src/click/termui.py index d5239e778..c39569f37 100644 --- a/src/click/termui.py +++ b/src/click/termui.py @@ -61,7 +61,7 @@ def _build_prompt( if type is not None and show_choices and isinstance(type, Choice): prompt += " (" + ", ".join(map(str, type.choices)) + ")" if default is not None and show_default: - prompt = "%s [%s]" % (prompt, _format_default(default)) + prompt = "{} [{}]".format(prompt, _format_default(default)) return prompt + suffix diff --git a/src/click/testing.py b/src/click/testing.py index 8131a7ad7..c5d1450f2 100644 --- a/src/click/testing.py +++ b/src/click/testing.py @@ -108,9 +108,8 @@ def stderr(self): ) def __repr__(self): - return "<%s %s>" % ( - type(self).__name__, - self.exception and repr(self.exception) or "okay", + return "<{} {}>".format( + type(self).__name__, self.exception and repr(self.exception) or "okay", ) diff --git a/src/click/types.py b/src/click/types.py index 098719031..8f4b9d216 100644 --- a/src/click/types.py +++ b/src/click/types.py @@ -194,7 +194,9 @@ def convert(self, value, param, ctx): return normed_choices[normed_value] self.fail( - "invalid choice: %s. (choose from %s)" % (value, ", ".join(self.choices)), + "invalid choice: {}. (choose from {})".format( + value, ", ".join(self.choices) + ), param, ctx, ) @@ -299,7 +301,9 @@ def convert(self, value, param, ctx): ): if self.min is None: self.fail( - "%s is bigger than the maximum valid value %s." % (rv, self.max), + "{} is bigger than the maximum valid value {}.".format( + rv, self.max + ), param, ctx, ) @@ -320,7 +324,7 @@ def convert(self, value, param, ctx): return rv def __repr__(self): - return "IntRange(%r, %r)" % (self.min, self.max) + return "IntRange({!r}, {!r})".format(self.min, self.max) class FloatParamType(ParamType): @@ -367,7 +371,9 @@ def convert(self, value, param, ctx): ): if self.min is None: self.fail( - "%s is bigger than the maximum valid value %s." % (rv, self.max), + "{} is bigger than the maximum valid value {}.".format( + rv, self.max + ), param, ctx, ) @@ -388,7 +394,7 @@ def convert(self, value, param, ctx): return rv def __repr__(self): - return "FloatRange(%r, %r)" % (self.min, self.max) + return "FloatRange({!r}, {!r})".format(self.min, self.max) class BoolParamType(ParamType): @@ -597,20 +603,24 @@ def convert(self, value, param, ctx): if not self.exists: return self.coerce_path_result(rv) self.fail( - '%s "%s" does not exist.' % (self.path_type, filename_to_ui(value)), + '{} "{}" does not exist.'.format( + self.path_type, filename_to_ui(value) + ), param, ctx, ) if not self.file_okay and stat.S_ISREG(st.st_mode): self.fail( - '%s "%s" is a file.' % (self.path_type, filename_to_ui(value)), + '{} "{}" is a file.'.format(self.path_type, filename_to_ui(value)), param, ctx, ) if not self.dir_okay and stat.S_ISDIR(st.st_mode): self.fail( - '%s "%s" is a directory.' % (self.path_type, filename_to_ui(value)), + '{} "{}" is a directory.'.format( + self.path_type, filename_to_ui(value) + ), param, ctx, ) diff --git a/src/click/utils.py b/src/click/utils.py index 4e97d8487..58517b33b 100644 --- a/src/click/utils.py +++ b/src/click/utils.py @@ -113,7 +113,7 @@ def __getattr__(self, name): def __repr__(self): if self._f is not None: return repr(self._f) - return "" % (self.name, self.mode) + return "".format(self.name, self.mode) def open(self): """Opens the file if it's not yet open. This call might fail with diff --git a/tests/test_imports.py b/tests/test_imports.py index 9d5281182..b99d453d0 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -29,30 +29,28 @@ def tracking_import(module, locals=None, globals=None, fromlist=None, click.echo(json.dumps(rv)) """ -ALLOWED_IMPORTS = set( - [ - "weakref", - "os", - "struct", - "collections", - "sys", - "contextlib", - "functools", - "stat", - "re", - "codecs", - "inspect", - "itertools", - "io", - "threading", - "colorama", - "errno", - "fcntl", - "datetime", - "pipes", - "shlex", - ] -) +ALLOWED_IMPORTS = { + "weakref", + "os", + "struct", + "collections", + "sys", + "contextlib", + "functools", + "stat", + "re", + "codecs", + "inspect", + "itertools", + "io", + "threading", + "colorama", + "errno", + "fcntl", + "datetime", + "pipes", + "shlex", +} if WIN: ALLOWED_IMPORTS.update(["ctypes", "ctypes.wintypes", "msvcrt", "time", "zlib"]) diff --git a/tests/test_options.py b/tests/test_options.py index 0cdb064ff..3974803a3 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -13,7 +13,7 @@ def test_prefixes(runner): @click.option("++foo", is_flag=True, help="das foo") @click.option("--bar", is_flag=True, help="das bar") def cli(foo, bar): - click.echo("foo=%s bar=%s" % (foo, bar)) + click.echo("foo={} bar={}".format(foo, bar)) result = runner.invoke(cli, ["++foo", "--bar"]) assert not result.exception @@ -90,7 +90,7 @@ def cli(): result = runner.invoke(cli, [unknown_flag]) assert result.exception - assert "no such option: {0}".format(unknown_flag) in result.output + assert "no such option: {}".format(unknown_flag) in result.output def test_multiple_required(runner): diff --git a/tests/test_termui.py b/tests/test_termui.py index 4e33f412f..99a6a476f 100644 --- a/tests/test_termui.py +++ b/tests/test_termui.py @@ -123,7 +123,7 @@ def test_progressbar_format_pos(runner, pos, length): with _create_progress(length, length_known=length != 0, pos=pos) as progress: result = progress.format_pos() if progress.length_known: - assert result == "%s/%s" % (pos, length) + assert result == "{}/{}".format(pos, length) else: assert result == str(pos) From 718485be48263056e7036ea9a60ce11b47e2fc26 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 8 Mar 2020 08:57:08 -0700 Subject: [PATCH 10/10] manual cleanup --- README.rst | 5 +- examples/aliases/aliases.py | 6 +- examples/bashcompletion/bashcompletion.py | 6 +- examples/colors/colors.py | 8 +- examples/complex/complex/cli.py | 4 +- examples/imagepipe/imagepipe.py | 41 ++++----- examples/naval/naval.py | 2 +- examples/repo/repo.py | 14 +-- examples/termui/termui.py | 12 +-- examples/validation/validation.py | 10 +-- src/click/_bashcomplete.py | 2 +- src/click/_compat.py | 10 +-- src/click/_termui_impl.py | 20 +++-- src/click/_unicodefun.py | 47 +++++----- src/click/_winconsole.py | 4 +- src/click/core.py | 101 +++++++++++----------- src/click/decorators.py | 4 +- src/click/exceptions.py | 28 +++--- src/click/formatting.py | 14 +-- src/click/parser.py | 14 +-- src/click/termui.py | 26 +++--- src/click/testing.py | 6 +- src/click/types.py | 73 +++++++++------- src/click/utils.py | 12 +-- tests/test_arguments.py | 56 ++++-------- tests/test_basic.py | 55 ++++++------ tests/test_chain.py | 59 ++++--------- tests/test_commands.py | 10 +-- tests/test_context.py | 6 +- tests/test_defaults.py | 14 +-- tests/test_formatting.py | 30 +++---- tests/test_options.py | 21 ++--- tests/test_testing.py | 8 +- tests/test_utils.py | 8 +- 34 files changed, 346 insertions(+), 390 deletions(-) diff --git a/README.rst b/README.rst index 4cade9e57..8c4fea9cb 100644 --- a/README.rst +++ b/README.rst @@ -38,12 +38,11 @@ A Simple Example @click.command() @click.option("--count", default=1, help="Number of greetings.") - @click.option("--name", prompt="Your name", - help="The person to greet.") + @click.option("--name", prompt="Your name", help="The person to greet.") def hello(count, name): """Simple program that greets NAME for a total of COUNT times.""" for _ in range(count): - click.echo("Hello, %s!" % name) + click.echo(f"Hello, {name}!") if __name__ == '__main__': hello() diff --git a/examples/aliases/aliases.py b/examples/aliases/aliases.py index 1ebad50c1..250c646d4 100644 --- a/examples/aliases/aliases.py +++ b/examples/aliases/aliases.py @@ -69,7 +69,7 @@ def get_command(self, ctx, cmd_name): return None elif len(matches) == 1: return click.Group.get_command(self, ctx, matches[0]) - ctx.fail("Too many matches: %s" % ", ".join(sorted(matches))) + ctx.fail("Too many matches: {}".format(", ".join(sorted(matches)))) def read_config(ctx, param, value): @@ -125,7 +125,7 @@ def commit(): @pass_config def status(config): """Shows the status.""" - click.echo("Status for %s" % config.path) + click.echo("Status for {}".format(config.path)) @cli.command() @@ -139,4 +139,4 @@ def alias(config, alias_, cmd, config_file): """Adds an alias to the specified configuration file.""" config.add_alias(alias_, cmd) config.write_config(config_file) - click.echo("Added {} as alias for {}".format(alias_, cmd)) + click.echo("Added '{}' as alias for '{}'".format(alias_, cmd)) diff --git a/examples/bashcompletion/bashcompletion.py b/examples/bashcompletion/bashcompletion.py index 636cf643f..0502dbce1 100644 --- a/examples/bashcompletion/bashcompletion.py +++ b/examples/bashcompletion/bashcompletion.py @@ -18,8 +18,8 @@ def get_env_vars(ctx, args, incomplete): @cli.command(help="A command to print environment variables") @click.argument("envvar", type=click.STRING, autocompletion=get_env_vars) def cmd1(envvar): - click.echo("Environment variable: %s" % envvar) - click.echo("Value: %s" % os.environ[envvar]) + click.echo("Environment variable: {}".format(envvar)) + click.echo("Value: {}".format(os.environ[envvar])) @click.group(help="A group that holds a subcommand") @@ -39,7 +39,7 @@ def list_users(ctx, args, incomplete): @group.command(help="Choose a user") @click.argument("user", type=click.STRING, autocompletion=list_users) def subcmd(user): - click.echo("Chosen user is %s" % user) + click.echo("Chosen user is {}".format(user)) cli.add_command(group) diff --git a/examples/colors/colors.py b/examples/colors/colors.py index 4219c0f87..012538d5f 100644 --- a/examples/colors/colors.py +++ b/examples/colors/colors.py @@ -30,12 +30,14 @@ def cli(): Give it a try! """ for color in all_colors: - click.echo(click.style("I am colored %s" % color, fg=color)) + click.echo(click.style("I am colored {}".format(color), fg=color)) for color in all_colors: - click.echo(click.style("I am colored %s and bold" % color, fg=color, bold=True)) + click.echo( + click.style("I am colored {} and bold".format(color), fg=color, bold=True) + ) for color in all_colors: click.echo( - click.style("I am reverse colored %s" % color, fg=color, reverse=True) + click.style("I am reverse colored {}".format(color), fg=color, reverse=True) ) click.echo(click.style("I am blinking", blink=True)) diff --git a/examples/complex/complex/cli.py b/examples/complex/complex/cli.py index 5e75f583f..c539fe8fe 100644 --- a/examples/complex/complex/cli.py +++ b/examples/complex/complex/cli.py @@ -41,7 +41,9 @@ def get_command(self, ctx, name): try: if sys.version_info[0] == 2: name = name.encode("ascii", "replace") - mod = __import__("complex.commands.cmd_" + name, None, None, ["cli"]) + mod = __import__( + "complex.commands.cmd_{}".format(name), None, None, ["cli"] + ) except ImportError: return return mod.cli diff --git a/examples/imagepipe/imagepipe.py b/examples/imagepipe/imagepipe.py index 5e34405e1..d46c33f59 100644 --- a/examples/imagepipe/imagepipe.py +++ b/examples/imagepipe/imagepipe.py @@ -89,7 +89,7 @@ def open_cmd(images): """ for image in images: try: - click.echo('Opening "%s"' % image) + click.echo("Opening '{}'".format(image)) if image == "-": img = Image.open(click.get_binary_stdin()) img.filename = "-" @@ -97,13 +97,13 @@ def open_cmd(images): img = Image.open(image) yield img except Exception as e: - click.echo('Could not open image "{}": {}'.format(image, e), err=True) + click.echo("Could not open image '{}': {}".format(image, e), err=True) @cli.command("save") @click.option( "--filename", - default="processed-%04d.png", + default="processed-{:04}.png", type=click.Path(), help="The format for the filename.", show_default=True, @@ -113,12 +113,12 @@ def save_cmd(images, filename): """Saves all processed images to a series of files.""" for idx, image in enumerate(images): try: - fn = filename % (idx + 1) - click.echo('Saving "{}" as "{}"'.format(image.filename, fn)) + fn = filename.format(idx + 1) + click.echo("Saving '{}' as '{}'".format(image.filename, fn)) yield image.save(fn) except Exception as e: click.echo( - 'Could not save image "{}": {}'.format(image.filename, e), err=True + "Could not save image '{}': {}".format(image.filename, e), err=True ) @@ -127,7 +127,7 @@ def save_cmd(images, filename): def display_cmd(images): """Opens all images in an image viewer.""" for image in images: - click.echo('Displaying "%s"' % image.filename) + click.echo("Displaying '{}'".format(image.filename)) image.show() yield image @@ -142,7 +142,7 @@ def resize_cmd(images, width, height): """ for image in images: w, h = (width or image.size[0], height or image.size[1]) - click.echo('Resizing "%s" to %dx%d' % (image.filename, w, h)) + click.echo("Resizing '{}' to {}x{}".format(image.filename, w, h)) image.thumbnail((w, h)) yield image @@ -160,7 +160,7 @@ def crop_cmd(images, border): if border is not None: for idx, val in enumerate(box): box[idx] = max(0, val - border) - click.echo('Cropping "%s" by %dpx' % (image.filename, border)) + click.echo("Cropping '{}' by {}px".format(image.filename, border)) yield copy_filename(image.crop(box), image) else: yield image @@ -176,7 +176,7 @@ def convert_rotation(ctx, param, value): return (Image.ROTATE_180, 180) if value in ("-90", "270", "l", "left"): return (Image.ROTATE_270, 270) - raise click.BadParameter('invalid rotation "%s"' % value) + raise click.BadParameter("invalid rotation '{}'".format(value)) def convert_flip(ctx, param, value): @@ -187,7 +187,7 @@ def convert_flip(ctx, param, value): return (Image.FLIP_LEFT_RIGHT, "left to right") if value in ("tb", "topbottom", "upsidedown", "ud"): return (Image.FLIP_LEFT_RIGHT, "top to bottom") - raise click.BadParameter('invalid flip "%s"' % value) + raise click.BadParameter("invalid flip '{}'".format(value)) @cli.command("transpose") @@ -201,11 +201,11 @@ def transpose_cmd(images, rotate, flip): for image in images: if rotate is not None: mode, degrees = rotate - click.echo('Rotate "%s" by %ddeg' % (image.filename, degrees)) + click.echo("Rotate '{}' by {}deg".format(image.filename, degrees)) image = copy_filename(image.transpose(mode), image) if flip is not None: mode, direction = flip - click.echo('Flip "{}" {}'.format(image.filename, direction)) + click.echo("Flip '{}' {}".format(image.filename, direction)) image = copy_filename(image.transpose(mode), image) yield image @@ -217,7 +217,7 @@ def blur_cmd(images, radius): """Applies gaussian blur.""" blur = ImageFilter.GaussianBlur(radius) for image in images: - click.echo('Blurring "%s" by %dpx' % (image.filename, radius)) + click.echo("Blurring '{}' by {}px".format(image.filename, radius)) yield copy_filename(image.filter(blur), image) @@ -234,8 +234,9 @@ def smoothen_cmd(images, iterations): """Applies a smoothening filter.""" for image in images: click.echo( - 'Smoothening "%s" %d time%s' - % (image.filename, iterations, iterations != 1 and "s" or "",) + "Smoothening '{}' {} time{}".format( + image.filename, iterations, "s" if iterations != 1 else "" + ) ) for _ in range(iterations): image = copy_filename(image.filter(ImageFilter.BLUR), image) @@ -247,7 +248,7 @@ def smoothen_cmd(images, iterations): def emboss_cmd(images): """Embosses an image.""" for image in images: - click.echo('Embossing "%s"' % image.filename) + click.echo("Embossing '{}'".format(image.filename)) yield copy_filename(image.filter(ImageFilter.EMBOSS), image) @@ -259,7 +260,7 @@ def emboss_cmd(images): def sharpen_cmd(images, factor): """Sharpens an image.""" for image in images: - click.echo('Sharpen "{}" by {:f}'.format(image.filename, factor)) + click.echo("Sharpen '{}' by {}".format(image.filename, factor)) enhancer = ImageEnhance.Sharpness(image) yield copy_filename(enhancer.enhance(max(1.0, factor)), image) @@ -281,12 +282,12 @@ def paste_cmd(images, left, right): yield image return - click.echo('Paste "{}" on "{}"'.format(to_paste.filename, image.filename)) + click.echo("Paste '{}' on '{}'".format(to_paste.filename, image.filename)) mask = None if to_paste.mode == "RGBA" or "transparency" in to_paste.info: mask = to_paste image.paste(to_paste, (left, right), mask) - image.filename += "+" + to_paste.filename + image.filename += "+{}".format(to_paste.filename) yield image for image in imageiter: diff --git a/examples/naval/naval.py b/examples/naval/naval.py index a77dea8bc..b8d31e17a 100644 --- a/examples/naval/naval.py +++ b/examples/naval/naval.py @@ -21,7 +21,7 @@ def ship(): @click.argument("name") def ship_new(name): """Creates a new ship.""" - click.echo("Created ship %s" % name) + click.echo("Created ship {}".format(name)) @ship.command("move") diff --git a/examples/repo/repo.py b/examples/repo/repo.py index 470296bfd..5fd6ead88 100644 --- a/examples/repo/repo.py +++ b/examples/repo/repo.py @@ -17,7 +17,7 @@ def set_config(self, key, value): click.echo(" config[{}] = {}".format(key, value), file=sys.stderr) def __repr__(self): - return "" % self.home + return "".format(self.home) pass_repo = click.make_pass_decorator(Repo) @@ -82,7 +82,7 @@ def clone(repo, src, dest, shallow, rev): repo.home = dest if shallow: click.echo("Making shallow checkout") - click.echo("Checking out revision %s" % rev) + click.echo("Checking out revision {}".format(rev)) @cli.command() @@ -93,7 +93,7 @@ def delete(repo): This will throw away the current repository. """ - click.echo("Destroying repo %s" % repo.home) + click.echo("Destroying repo {}".format(repo.home)) click.echo("Deleted!") @@ -118,8 +118,8 @@ def setuser(repo, username, email, password): "--message", "-m", multiple=True, - help="The commit message. If provided multiple times each " - "argument gets converted into a new line.", + help="The commit message. If provided multiple times each" + " argument gets converted into a new line.", ) @click.argument("files", nargs=-1, type=click.Path()) @pass_repo @@ -136,7 +136,7 @@ def commit(repo, files, message): marker = "# Files to be committed:" hint = ["", "", marker, "#"] for file in files: - hint.append("# U %s" % file) + hint.append("# U {}".format(file)) message = click.edit("\n".join(hint)) if message is None: click.echo("Aborted!") @@ -148,7 +148,7 @@ def commit(repo, files, message): else: msg = "\n".join(message) click.echo("Files to be committed: {}".format(files)) - click.echo("Commit message:\n" + msg) + click.echo("Commit message:\n{}".format(msg)) @cli.command(short_help="Copies files.") diff --git a/examples/termui/termui.py b/examples/termui/termui.py index 2af114d4a..7b3da4337 100644 --- a/examples/termui/termui.py +++ b/examples/termui/termui.py @@ -16,8 +16,8 @@ def cli(): def colordemo(): """Demonstrates ANSI color support.""" for color in "red", "green", "blue": - click.echo(click.style("I am colored %s" % color, fg=color)) - click.echo(click.style("I am background colored %s" % color, bg=color)) + click.echo(click.style("I am colored {}".format(color), fg=color)) + click.echo(click.style("I am background colored {}".format(color), bg=color)) @cli.command() @@ -25,7 +25,7 @@ def pager(): """Demonstrates using the pager.""" lines = [] for x in range(200): - lines.append("%s. Hello World!" % click.style(str(x), fg="green")) + lines.append("{}. Hello World!".format(click.style(str(x), fg="green"))) click.echo_via_pager("\n".join(lines)) @@ -56,7 +56,7 @@ def filter(items): def show_item(item): if item is not None: - return "Item #%d" % item + return "Item #{}".format(item) with click.progressbar( filter(items), @@ -119,13 +119,13 @@ def locate(url): def edit(): """Opens an editor with some text in it.""" MARKER = "# Everything below is ignored\n" - message = click.edit("\n\n" + MARKER) + message = click.edit("\n\n{}".format(MARKER)) if message is not None: msg = message.split(MARKER, 1)[0].rstrip("\n") if not msg: click.echo("Empty message!") else: - click.echo("Message:\n" + msg) + click.echo("Message:\n{}".format(msg)) else: click.echo("You did not enter anything!") diff --git a/examples/validation/validation.py b/examples/validation/validation.py index 9abb125c7..c4f7352c2 100644 --- a/examples/validation/validation.py +++ b/examples/validation/validation.py @@ -20,8 +20,8 @@ def convert(self, value, param, ctx): value = urlparse.urlparse(value) if value.scheme not in ("http", "https"): self.fail( - "invalid URL scheme (%s). Only HTTP URLs are " - "allowed" % value.scheme, + "invalid URL scheme ({}). Only HTTP URLs are" + " allowed".format(value.scheme), param, ctx, ) @@ -47,6 +47,6 @@ def cli(count, foo, url): 'If a value is provided it needs to be the value "wat".', param_hint=["--foo"], ) - click.echo("count: %s" % count) - click.echo("foo: %s" % foo) - click.echo("url: %s" % repr(url)) + click.echo("count: {}".format(count)) + click.echo("foo: {}".format(foo)) + click.echo("url: {!r}".format(url)) diff --git a/src/click/_bashcomplete.py b/src/click/_bashcomplete.py index 31138087b..8bca24480 100644 --- a/src/click/_bashcomplete.py +++ b/src/click/_bashcomplete.py @@ -98,7 +98,7 @@ def get_completion_script(prog_name, complete_var, shell): return ( script % { - "complete_func": "_%s_completion" % cf_name, + "complete_func": "_{}_completion".format(cf_name), "script_names": prog_name, "autocomplete_var": complete_var, } diff --git a/src/click/_compat.py b/src/click/_compat.py index 38ab8c0a7..ed57a18f9 100644 --- a/src/click/_compat.py +++ b/src/click/_compat.py @@ -510,7 +510,7 @@ def _wrap_io_open(file, mode, encoding, errors): if not PY2 or binary: return io.open(file, mode, **kwargs) - f = io.open(file, "b" + mode.replace("t", "")) + f = io.open(file, "{}b".format(mode.replace("t", ""))) return _make_text_stream(f, **kwargs) @@ -535,10 +535,10 @@ def open_stream(filename, mode="r", encoding=None, errors="strict", atomic=False # Some usability stuff for atomic writes if "a" in mode: raise ValueError( - "Appending to an existing file is not supported, because that " - "would involve an expensive `copy`-operation to a temporary " - "file. Open the file in normal `w`-mode and copy explicitly " - "if that's what you're after." + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." ) if "x" in mode: raise ValueError("Use the `overwrite`-parameter instead.") diff --git a/src/click/_termui_impl.py b/src/click/_termui_impl.py index 28a0d9c70..c6e86cc01 100644 --- a/src/click/_termui_impl.py +++ b/src/click/_termui_impl.py @@ -167,20 +167,19 @@ def format_eta(self): hours = t % 24 t //= 24 if t > 0: - days = t - return "%dd %02d:%02d:%02d" % (days, hours, minutes, seconds) + return "{}d {:02}:{:02}:{:02}".format(t, hours, minutes, seconds) else: - return "%02d:%02d:%02d" % (hours, minutes, seconds) + return "{:02}:{:02}:{:02}".format(hours, minutes, seconds) return "" def format_pos(self): pos = str(self.pos) if self.length_known: - pos += "/%s" % self.length + pos += "/{}".format(self.length) return pos def format_pct(self): - return ("% 4d%%" % int(self.pct * 100))[1:] + return "{: 4}%".format(int(self.pct * 100))[1:] def format_bar(self): if self.length_known: @@ -347,7 +346,10 @@ def pager(generator, color=None): fd, filename = tempfile.mkstemp() os.close(fd) try: - if hasattr(os, "system") and os.system("more %s" % shlex_quote(filename)) == 0: + if ( + hasattr(os, "system") + and os.system("more {}".format(shlex_quote(filename))) == 0 + ): return _pipepager(generator, "more", color) return _nullpager(stdout, generator, color) finally: @@ -366,7 +368,7 @@ def _pipepager(generator, cmd, color): # condition that cmd_detail = cmd.rsplit("/", 1)[-1].split() if color is None and cmd_detail[0] == "less": - less_flags = os.environ.get("LESS", "") + " ".join(cmd_detail[1:]) + less_flags = "{}{}".format(os.environ.get("LESS", ""), " ".join(cmd_detail[1:])) if not less_flags: env["LESS"] = "-R" color = True @@ -446,7 +448,7 @@ def get_editor(self): if WIN: return "notepad" for editor in "sensible-editor", "vim", "nano": - if os.system("which %s >/dev/null 2>&1" % editor) == 0: + if os.system("which {} >/dev/null 2>&1".format(editor)) == 0: return editor return "vi" @@ -467,7 +469,7 @@ def edit_file(self, filename): ) exit_code = c.wait() if exit_code != 0: - raise ClickException("%s: Editing failed!" % editor) + raise ClickException("{}: Editing failed!".format(editor)) except OSError as e: raise ClickException("{}: Editing failed: {}".format(editor, e)) diff --git a/src/click/_unicodefun.py b/src/click/_unicodefun.py index 01c4bc303..781c36522 100644 --- a/src/click/_unicodefun.py +++ b/src/click/_unicodefun.py @@ -38,12 +38,12 @@ def _check_for_unicode_literals(): warn( Warning( - "Click detected the use of the unicode_literals " - "__future__ import. This is heavily discouraged " - "because it can introduce subtle bugs in your " - 'code. You should instead use explicit u"" literals ' - "for your unicode strings. For more information see " - "https://click.palletsprojects.com/en/7.x/python3/" + "Click detected the use of the unicode_literals __future__" + " import. This is heavily discouraged because it can" + " introduce subtle bugs in your code. You should instead" + ' use explicit u"" literals for your unicode strings. For' + " more information see" + " https://click.palletsprojects.com/python3/" ), stacklevel=bad_frame, ) @@ -89,24 +89,25 @@ def _verify_python3_env(): extra += "\n\n" if not good_locales: extra += ( - "Additional information: on this system no suitable UTF-8\n" - "locales were discovered. This most likely requires resolving\n" - "by reconfiguring the locale system." + "Additional information: on this system no suitable" + " UTF-8 locales were discovered. This most likely" + " requires resolving by reconfiguring the locale" + " system." ) elif has_c_utf8: extra += ( - "This system supports the C.UTF-8 locale which is recommended.\n" - "You might be able to resolve your issue by exporting the\n" - "following environment variables:\n\n" + "This system supports the C.UTF-8 locale which is" + " recommended. You might be able to resolve your issue" + " by exporting the following environment variables:\n\n" " export LC_ALL=C.UTF-8\n" " export LANG=C.UTF-8" ) else: extra += ( - "This system lists a couple of UTF-8 supporting locales that\n" - "you can pick from. The following suitable locales were\n" - "discovered: %s" - ) % ", ".join(sorted(good_locales)) + "This system lists a couple of UTF-8 supporting locales" + " that you can pick from. The following suitable" + " locales were discovered: {}".format(", ".join(sorted(good_locales))) + ) bad_locale = None for locale in os.environ.get("LC_ALL"), os.environ.get("LANG"): @@ -116,15 +117,15 @@ def _verify_python3_env(): break if bad_locale is not None: extra += ( - "\n\nClick discovered that you exported a UTF-8 locale\n" - "but the locale system could not pick up from it because\n" - 'it does not exist. The exported locale is "%s" but it\n' - "is not supported" - ) % bad_locale + "\n\nClick discovered that you exported a UTF-8 locale" + " but the locale system could not pick up from it" + " because it does not exist. The exported locale is" + " '{}' but it is not supported".format(bad_locale) + ) raise RuntimeError( "Click will abort further execution because Python 3 was" " configured to use ASCII as encoding for the environment." - " Consult https://click.palletsprojects.com/en/7.x/python3/ for" - " mitigation steps." + extra + " Consult https://click.palletsprojects.com/python3/ for" + " mitigation steps.{}".format(extra) ) diff --git a/src/click/_winconsole.py b/src/click/_winconsole.py index 7b5be3484..b6c4274af 100644 --- a/src/click/_winconsole.py +++ b/src/click/_winconsole.py @@ -155,7 +155,7 @@ def readinto(self, b): # wait for KeyboardInterrupt time.sleep(0.1) if not rv: - raise OSError("Windows error: %s" % GetLastError()) + raise OSError("Windows error: {}".format(GetLastError())) if buffer[0] == EOF: return 0 @@ -172,7 +172,7 @@ def _get_error_message(errno): return "ERROR_SUCCESS" elif errno == ERROR_NOT_ENOUGH_MEMORY: return "ERROR_NOT_ENOUGH_MEMORY" - return "Windows error %s" % errno + return "Windows error {}".format(errno) def write(self, b): bytes_to_be_written = len(b) diff --git a/src/click/core.py b/src/click/core.py index 3537e59db..21026963a 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -62,7 +62,7 @@ def fast_exit(code): def _bashcomplete(cmd, prog_name, complete_var=None): """Internal handler for the bash completion support.""" if complete_var is None: - complete_var = "_%s_COMPLETE" % (prog_name.replace("-", "_")).upper() + complete_var = "_{}_COMPLETE".format(prog_name.replace("-", "_").upper()) complete_instr = os.environ.get(complete_var) if not complete_instr: return @@ -78,20 +78,18 @@ def _check_multicommand(base_command, cmd_name, cmd, register=False): return if register: hint = ( - "It is not possible to add multi commands as children to " - "another multi command that is in chain mode" + "It is not possible to add multi commands as children to" + " another multi command that is in chain mode." ) else: hint = ( - "Found a multi command as subcommand to a multi command " - "that is in chain mode. This is not supported" + "Found a multi command as subcommand to a multi command" + " that is in chain mode. This is not supported." ) raise RuntimeError( - '%s. Command "%s" is set to chain and "%s" was ' - "added as subcommand but it in itself is a " - 'multi command. ("%s" is a %s within a chained ' - '%s named "%s").' - % ( + "{}. Command '{}' is set to chain and '{}' was added as" + " subcommand but it in itself is a multi command. ('{}' is a {}" + " within a chained {} named '{}').".format( hint, base_command.name, cmd_name, @@ -117,9 +115,9 @@ def invoke_param_callback(callback, ctx, param, value): warn( Warning( - 'Invoked legacy parameter callback "%s". The new ' - "signature for such callbacks starting with " - "click 2.0 is (ctx, param, value)." % callback + "Invoked legacy parameter callback '{}'. The new" + " signature for such callbacks starting with Click 2.0" + " is (ctx, param, value).".format(callback) ), stacklevel=3, ) @@ -129,9 +127,7 @@ def invoke_param_callback(callback, ctx, param, value): @contextmanager def augment_usage_errors(ctx, param=None): - """Context manager that attaches extra information to exceptions that - fly. - """ + """Context manager that attaches extra information to exceptions.""" try: yield except BadParameter as e: @@ -380,7 +376,7 @@ def __init__( and self.info_name is not None ): auto_envvar_prefix = "{}_{}".format( - parent.auto_envvar_prefix, self.info_name.upper(), + parent.auto_envvar_prefix, self.info_name.upper() ) else: auto_envvar_prefix = auto_envvar_prefix.upper() @@ -463,7 +459,7 @@ def meta(self): Example usage:: - LANG_KEY = __name__ + '.lang' + LANG_KEY = f'{__name__}.lang' def set_language(value): ctx = get_current_context() @@ -510,7 +506,7 @@ def command_path(self): if self.info_name is not None: rv = self.info_name if self.parent is not None: - rv = self.parent.command_path + " " + rv + rv = "{} {}".format(self.parent.command_path, rv) return rv.lstrip() def find_root(self): @@ -773,7 +769,9 @@ def main( args = list(args) if prog_name is None: - prog_name = make_str(os.path.basename(sys.argv and sys.argv[0] or __file__)) + prog_name = make_str( + os.path.basename(sys.argv[0] if sys.argv else __file__) + ) # Hook for the Bash completion. This only activates if the Bash # completion is actually enabled, otherwise this is quite a fast @@ -1053,8 +1051,9 @@ def parse_args(self, ctx, args): if args and not ctx.allow_extra_args and not ctx.resilient_parsing: ctx.fail( - "Got unexpected extra argument%s (%s)" - % (len(args) != 1 and "s" or "", " ".join(map(make_str, args))) + "Got unexpected extra argument{} ({})".format( + "s" if len(args) != 1 else "", " ".join(map(make_str, args)) + ) ) ctx.args = args @@ -1126,8 +1125,8 @@ def __init__( for param in self.params: if isinstance(param, Argument) and not param.required: raise RuntimeError( - "Multi commands in chain mode cannot " - "have optional arguments." + "Multi commands in chain mode cannot have" + " optional arguments." ) def collect_usage_pieces(self, ctx): @@ -1267,7 +1266,7 @@ def _process_result(value): # set to ``*`` to inform the command that subcommands are executed # but nothing else. with ctx: - ctx.invoked_subcommand = args and "*" or None + ctx.invoked_subcommand = "*" if args else None Command.invoke(self, ctx) # Otherwise we make every single context and invoke them in a @@ -1314,7 +1313,7 @@ def resolve_command(self, ctx, args): if cmd is None and not ctx.resilient_parsing: if split_opt(cmd_name)[0]: self.parse_args(ctx, ctx.args) - ctx.fail('No such command "%s".' % original_cmd_name) + ctx.fail("No such command '{}'.".format(original_cmd_name)) return cmd_name, cmd, args[1:] @@ -1556,10 +1555,9 @@ def type_cast_value(self, ctx, value): if self.type.is_composite: if self.nargs <= 1: raise TypeError( - "Attempted to invoke composite type " - "but nargs has been set to %s. This is " - "not supported; nargs needs to be set to " - "a fixed value > 1." % self.nargs + "Attempted to invoke composite type but nargs has" + " been set to {}. This is not supported; nargs" + " needs to be set to a fixed value > 1.".format(self.nargs) ) if self.multiple: return tuple(self.type(x or (), self, ctx) for x in value or ()) @@ -1652,7 +1650,7 @@ def get_error_hint(self, ctx): indicate which param caused the error. """ hint_list = self.opts or [self.human_readable_name] - return " / ".join('"%s"' % x for x in hint_list) + return " / ".join(repr(x) for x in hint_list) class Option(Parameter): @@ -1794,7 +1792,7 @@ def _parse_decls(self, decls, expose_value): raise TypeError("Name defined twice") name = decl else: - split_char = decl[:1] == "/" and ";" or "/" + split_char = ";" if decl[:1] == "/" else "/" if split_char in decl: first, second = decl.split(split_char, 1) first = first.rstrip() @@ -1821,9 +1819,8 @@ def _parse_decls(self, decls, expose_value): if not opts and not secondary_opts: raise TypeError( - "No options defined but a name was passed (%s). " - "Did you mean to declare an argument instead " - "of an option?" % name + "No options defined but a name was passed ({}). Did you" + " mean to declare an argument instead of an option?".format(name) ) return name, opts, secondary_opts @@ -1844,16 +1841,15 @@ def add_to_parser(self, parser, ctx): if self.is_flag: kwargs.pop("nargs", None) + action_const = "{}_const".format(action) if self.is_bool_flag and self.secondary_opts: + parser.add_option(self.opts, action=action_const, const=True, **kwargs) parser.add_option( - self.opts, action=action + "_const", const=True, **kwargs - ) - parser.add_option( - self.secondary_opts, action=action + "_const", const=False, **kwargs + self.secondary_opts, action=action_const, const=False, **kwargs ) else: parser.add_option( - self.opts, action=action + "_const", const=self.flag_value, **kwargs + self.opts, action=action_const, const=self.flag_value, **kwargs ) else: kwargs["action"] = action @@ -1869,7 +1865,7 @@ def _write_opts(opts): if any_slashes: any_prefix_is_slash[:] = [True] if not self.is_flag and not self.count: - rv += " " + self.make_metavar() + rv += " {}".format(self.make_metavar()) return rv rv = [_write_opts(self.opts)] @@ -1885,18 +1881,17 @@ def _write_opts(opts): envvar = "{}_{}".format(ctx.auto_envvar_prefix, self.name.upper()) if envvar is not None: extra.append( - "env var: %s" - % ( - ", ".join("%s" % d for d in envvar) + "env var: {}".format( + ", ".join(str(d) for d in envvar) if isinstance(envvar, (list, tuple)) - else envvar, + else envvar ) ) if self.default is not None and (self.show_default or ctx.show_default): if isinstance(self.show_default, string_types): default_string = "({})".format(self.show_default) elif isinstance(self.default, (list, tuple)): - default_string = ", ".join("%s" % d for d in self.default) + default_string = ", ".join(str(d) for d in self.default) elif inspect.isfunction(self.default): default_string = "(dynamic)" else: @@ -1906,9 +1901,11 @@ def _write_opts(opts): if self.required: extra.append("required") if extra: - help = "{}[{}]".format(help and help + " " or "", "; ".join(extra)) + help = "{}[{}]".format( + "{} ".format(help) if help else "", "; ".join(extra) + ) - return ((any_prefix_is_slash and "; " or " / ").join(rv), help) + return ("; " if any_prefix_is_slash else " / ").join(rv), help def get_default(self, ctx): # If we're a non boolean flag our default is more complex because @@ -2006,7 +2003,7 @@ def make_metavar(self): if not var: var = self.name.upper() if not self.required: - var = "[%s]" % var + var = "[{}]".format(var) if self.nargs != 1: var += "..." return var @@ -2021,8 +2018,8 @@ def _parse_decls(self, decls, expose_value): name = name.replace("-", "_").lower() else: raise TypeError( - "Arguments take exactly one " - "parameter declaration, got %d" % len(decls) + "Arguments take exactly one parameter declaration, got" + " {}".format(len(decls)) ) return name, [arg], [] @@ -2030,7 +2027,7 @@ def get_usage_pieces(self, ctx): return [self.make_metavar()] def get_error_hint(self, ctx): - return '"%s"' % self.make_metavar() + return repr(self.make_metavar()) def add_to_parser(self, parser, ctx): parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/src/click/decorators.py b/src/click/decorators.py index 7e10adb60..c7b5af6cc 100644 --- a/src/click/decorators.py +++ b/src/click/decorators.py @@ -67,8 +67,8 @@ def new_func(*args, **kwargs): obj = ctx.find_object(object_type) if obj is None: raise RuntimeError( - "Managed to invoke callback without a " - "context object of type %r existing" % object_type.__name__ + "Managed to invoke callback without a context" + " object of type '{}' existing".format(object_type.__name__) ) return ctx.invoke(f, obj, *args, **kwargs) diff --git a/src/click/exceptions.py b/src/click/exceptions.py index 5f5bf582f..9cc2f59d1 100644 --- a/src/click/exceptions.py +++ b/src/click/exceptions.py @@ -6,7 +6,7 @@ def _join_param_hints(param_hint): if isinstance(param_hint, (tuple, list)): - return " / ".join('"%s"' % x for x in param_hint) + return " / ".join(repr(x) for x in param_hint) return param_hint @@ -39,7 +39,7 @@ def __str__(self): def show(self, file=None): if file is None: file = get_text_stderr() - echo("Error: %s" % self.format_message(), file=file) + echo("Error: {}".format(self.format_message(), file=file)) class UsageError(ClickException): @@ -56,7 +56,7 @@ class UsageError(ClickException): def __init__(self, message, ctx=None): ClickException.__init__(self, message) self.ctx = ctx - self.cmd = self.ctx and self.ctx.command or None + self.cmd = self.ctx.command if self.ctx else None def show(self, file=None): if file is None: @@ -64,13 +64,13 @@ def show(self, file=None): color = None hint = "" if self.cmd is not None and self.cmd.get_help_option(self.ctx) is not None: - hint = 'Try "{} {}" for help.\n'.format( - self.ctx.command_path, self.ctx.help_option_names[0], + hint = "Try '{} {}' for help.\n".format( + self.ctx.command_path, self.ctx.help_option_names[0] ) if self.ctx is not None: color = self.ctx.color - echo(self.ctx.get_usage() + "\n%s" % hint, file=file, color=color) - echo("Error: %s" % self.format_message(), file=file, color=color) + echo("{}\n{}".format(self.ctx.get_usage(), hint), file=file, color=color) + echo("Error: {}".format(self.format_message()), file=file, color=color) class BadParameter(UsageError): @@ -102,7 +102,7 @@ def format_message(self): elif self.param is not None: param_hint = self.param.get_error_hint(self.ctx) else: - return "Invalid value: %s" % self.message + return "Invalid value: {}".format(self.message) param_hint = _join_param_hints(param_hint) return "Invalid value for {}: {}".format(param_hint, self.message) @@ -144,14 +144,14 @@ def format_message(self): msg_extra = self.param.type.get_missing_message(self.param) if msg_extra: if msg: - msg += ". " + msg_extra + msg += ". {}".format(msg_extra) else: msg = msg_extra return "Missing {}{}{}{}".format( param_type, - param_hint and " %s" % param_hint or "", - msg and ". " or ".", + " {}".format(param_hint) if param_hint else "", + ". " if msg else ".", msg or "", ) @@ -178,7 +178,7 @@ class NoSuchOption(UsageError): def __init__(self, option_name, message=None, possibilities=None, ctx=None): if message is None: - message = "no such option: %s" % option_name + message = "no such option: {}".format(option_name) UsageError.__init__(self, message, ctx) self.option_name = option_name self.possibilities = possibilities @@ -187,10 +187,10 @@ def format_message(self): bits = [self.message] if self.possibilities: if len(self.possibilities) == 1: - bits.append("Did you mean %s?" % self.possibilities[0]) + bits.append("Did you mean {}?".format(self.possibilities[0])) else: possibilities = sorted(self.possibilities) - bits.append("(Possible options: %s)" % ", ".join(possibilities)) + bits.append("(Possible options: {})".format(", ".join(possibilities))) return " ".join(bits) diff --git a/src/click/formatting.py b/src/click/formatting.py index de90d6bde..319c7f616 100644 --- a/src/click/formatting.py +++ b/src/click/formatting.py @@ -134,7 +134,7 @@ def write_usage(self, prog, args="", prefix="Usage: "): :param args: whitespace separated list of arguments. :param prefix: the prefix for the first line. """ - usage_prefix = "%*s%s " % (self.current_indent, prefix, prog) + usage_prefix = "{:>{w}}{} ".format(prefix, prog, w=self.current_indent) text_width = self.width - self.current_indent if text_width >= (term_len(usage_prefix) + 20): @@ -163,7 +163,7 @@ def write_usage(self, prog, args="", prefix="Usage: "): def write_heading(self, heading): """Writes a heading into the buffer.""" - self.write("%*s%s:\n" % (self.current_indent, "", heading)) + self.write("{:>{w}}{}:\n".format("", heading, w=self.current_indent)) def write_paragraph(self): """Writes a paragraph into the buffer.""" @@ -204,7 +204,7 @@ def write_dl(self, rows, col_max=30, col_spacing=2): first_col = min(widths[0], col_max) + col_spacing for first, second in iter_rows(rows, len(widths)): - self.write("%*s%s" % (self.current_indent, "", first)) + self.write("{:>{w}}{}".format("", first, w=self.current_indent)) if not second: self.write("\n") continue @@ -219,10 +219,14 @@ def write_dl(self, rows, col_max=30, col_spacing=2): lines = wrapped_text.splitlines() if lines: - self.write(lines[0] + "\n") + self.write("{}\n".format(lines[0])) for line in lines[1:]: - self.write("%*s%s\n" % (first_col + self.current_indent, "", line)) + self.write( + "{:>{w}}{}\n".format( + "", line, w=first_col + self.current_indent + ) + ) if len(lines) > 1: # separate long help from next option diff --git a/src/click/parser.py b/src/click/parser.py index b493ed4ce..f43ebfe9f 100644 --- a/src/click/parser.py +++ b/src/click/parser.py @@ -80,8 +80,8 @@ def _fetch(c): def _error_opt_args(nargs, opt): if nargs == 1: - raise BadOptionUsage(opt, "%s option requires an argument" % opt) - raise BadOptionUsage(opt, "%s option requires %d arguments" % (opt, nargs)) + raise BadOptionUsage(opt, "{} option requires an argument".format(opt)) + raise BadOptionUsage(opt, "{} option requires {} arguments".format(opt, nargs)) def split_opt(opt): @@ -128,7 +128,7 @@ def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None): for opt in opts: prefix, value = split_opt(opt) if not prefix: - raise ValueError("Invalid start character for option (%s)" % opt) + raise ValueError("Invalid start character for option ({})".format(opt)) self.prefixes.add(prefix[0]) if len(prefix) == 1 and len(value) == 1: self._short_opts.append(opt) @@ -161,7 +161,7 @@ def process(self, value, state): elif self.action == "count": state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 else: - raise ValueError("unknown action %r" % self.action) + raise ValueError("unknown action '{}'".format(self.action)) state.order.append(self.obj) @@ -178,7 +178,7 @@ def process(self, value, state): value = None elif holes != 0: raise BadArgumentUsage( - "argument %s takes %d values" % (self.dest, self.nargs) + "argument {} takes {} values".format(self.dest, self.nargs) ) state.opts[self.dest] = value state.order.append(self.obj) @@ -344,7 +344,7 @@ def _match_long_opt(self, opt, explicit_value, state): del state.rargs[:nargs] elif explicit_value is not None: - raise BadOptionUsage(opt, "%s option does not take a value" % opt) + raise BadOptionUsage(opt, "{} option does not take a value".format(opt)) else: value = None @@ -396,7 +396,7 @@ def _match_short_opt(self, arg, state): # to the state as new larg. This way there is basic combinatorics # that can be achieved while still ignoring unknown arguments. if self.ignore_unknown_options and unknown_options: - state.largs.append(prefix + "".join(unknown_options)) + state.largs.append("{}{}".format(prefix, "".join(unknown_options))) def _process_opts(self, arg, state): explicit_value = None diff --git a/src/click/termui.py b/src/click/termui.py index c39569f37..02ef9e9f0 100644 --- a/src/click/termui.py +++ b/src/click/termui.py @@ -59,7 +59,7 @@ def _build_prompt( ): prompt = text if type is not None and show_choices and isinstance(type, Choice): - prompt += " (" + ", ".join(map(str, type.choices)) + ")" + prompt += " ({})".format(", ".join(map(str, type.choices))) if default is not None and show_default: prompt = "{} [{}]".format(prompt, _format_default(default)) return prompt + suffix @@ -121,7 +121,7 @@ def prompt( result = None def prompt_func(text): - f = hide_input and hidden_prompt_func or visible_prompt_func + f = hidden_prompt_func if hide_input else visible_prompt_func try: # Write the prompt separately so that we get nice # coloring through colorama on Windows @@ -156,7 +156,7 @@ def prompt_func(text): try: result = value_proc(value) except UsageError as e: - echo("Error: %s" % e.message, err=err) # noqa: B306 + echo("Error: {}".format(e.message), err=err) # noqa: B306 continue if not confirmation_prompt: return result @@ -190,7 +190,7 @@ def confirm( ``stdout``, the same as with echo. """ prompt = _build_prompt( - text, prompt_suffix, show_default, default and "Y/n" or "y/N" + text, prompt_suffix, show_default, "Y/n" if default else "y/N" ) while 1: try: @@ -495,24 +495,24 @@ def style( bits = [] if fg: try: - bits.append("\033[%dm" % (_ansi_colors[fg])) + bits.append("\033[{}m".format(_ansi_colors[fg])) except KeyError: - raise TypeError("Unknown color %r" % fg) + raise TypeError("Unknown color '{}'".format(fg)) if bg: try: - bits.append("\033[%dm" % (_ansi_colors[bg] + 10)) + bits.append("\033[{}m".format(_ansi_colors[bg] + 10)) except KeyError: - raise TypeError("Unknown color %r" % bg) + raise TypeError("Unknown color '{}'".format(bg)) if bold is not None: - bits.append("\033[%dm" % (1 if bold else 22)) + bits.append("\033[{}m".format(1 if bold else 22)) if dim is not None: - bits.append("\033[%dm" % (2 if dim else 22)) + bits.append("\033[{}m".format(2 if dim else 22)) if underline is not None: - bits.append("\033[%dm" % (4 if underline else 24)) + bits.append("\033[{}m".format(4 if underline else 24)) if blink is not None: - bits.append("\033[%dm" % (5 if blink else 25)) + bits.append("\033[{}m".format(5 if blink else 25)) if reverse is not None: - bits.append("\033[%dm" % (7 if reverse else 27)) + bits.append("\033[{}m".format(7 if reverse else 27)) bits.append(text) if reset: bits.append(_ansi_reset_all) diff --git a/src/click/testing.py b/src/click/testing.py index c5d1450f2..a3dba3b30 100644 --- a/src/click/testing.py +++ b/src/click/testing.py @@ -109,7 +109,7 @@ def stderr(self): def __repr__(self): return "<{} {}>".format( - type(self).__name__, self.exception and repr(self.exception) or "okay", + type(self).__name__, repr(self.exception) if self.exception else "okay" ) @@ -210,12 +210,12 @@ def isolation(self, input=None, env=None, color=False): def visible_input(prompt=None): sys.stdout.write(prompt or "") val = input.readline().rstrip("\r\n") - sys.stdout.write(val + "\n") + sys.stdout.write("{}\n".format(val)) sys.stdout.flush() return val def hidden_input(prompt=None): - sys.stdout.write((prompt or "") + "\n") + sys.stdout.write("{}\n".format(prompt or "")) sys.stdout.flush() return input.readline().rstrip("\r\n") diff --git a/src/click/types.py b/src/click/types.py index 8f4b9d216..505c39f85 100644 --- a/src/click/types.py +++ b/src/click/types.py @@ -158,10 +158,10 @@ def __init__(self, choices, case_sensitive=True): self.case_sensitive = case_sensitive def get_metavar(self, param): - return "[%s]" % "|".join(self.choices) + return "[{}]".format("|".join(self.choices)) def get_missing_message(self, param): - return "Choose from:\n\t%s." % ",\n\t".join(self.choices) + return "Choose from:\n\t{}.".format(",\n\t".join(self.choices)) def convert(self, value, param, ctx): # Match through normalization and case sensitivity @@ -202,7 +202,7 @@ def convert(self, value, param, ctx): ) def __repr__(self): - return "Choice(%r)" % list(self.choices) + return "Choice('{}')".format(list(self.choices)) class DateTime(ParamType): @@ -264,7 +264,7 @@ def convert(self, value, param, ctx): try: return int(value) except ValueError: - self.fail("%s is not a valid integer" % value, param, ctx) + self.fail("{} is not a valid integer".format(value), param, ctx) def __repr__(self): return "INT" @@ -309,22 +309,24 @@ def convert(self, value, param, ctx): ) elif self.max is None: self.fail( - "%s is smaller than the minimum valid value " - "%s." % (rv, self.min), + "{} is smaller than the minimum valid value {}.".format( + rv, self.min + ), param, ctx, ) else: self.fail( - "%s is not in the valid range of %s to %s." - % (rv, self.min, self.max), + "{} is not in the valid range of {} to {}.".format( + rv, self.min, self.max + ), param, ctx, ) return rv def __repr__(self): - return "IntRange({!r}, {!r})".format(self.min, self.max) + return "IntRange({}, {})".format(self.min, self.max) class FloatParamType(ParamType): @@ -334,7 +336,9 @@ def convert(self, value, param, ctx): try: return float(value) except ValueError: - self.fail("%s is not a valid floating point value" % value, param, ctx) + self.fail( + "{} is not a valid floating point value".format(value), param, ctx + ) def __repr__(self): return "FLOAT" @@ -379,22 +383,24 @@ def convert(self, value, param, ctx): ) elif self.max is None: self.fail( - "%s is smaller than the minimum valid value " - "%s." % (rv, self.min), + "{} is smaller than the minimum valid value {}.".format( + rv, self.min + ), param, ctx, ) else: self.fail( - "%s is not in the valid range of %s to %s." - % (rv, self.min, self.max), + "{} is not in the valid range of {} to {}.".format( + rv, self.min, self.max + ), param, ctx, ) return rv def __repr__(self): - return "FloatRange({!r}, {!r})".format(self.min, self.max) + return "FloatRange({}, {})".format(self.min, self.max) class BoolParamType(ParamType): @@ -408,7 +414,7 @@ def convert(self, value, param, ctx): return True elif value in ("false", "f", "0", "no", "n"): return False - self.fail("%s is not a valid boolean" % value, param, ctx) + self.fail("{} is not a valid boolean".format(value), param, ctx) def __repr__(self): return "BOOL" @@ -425,7 +431,7 @@ def convert(self, value, param, ctx): value = value.encode("ascii") return uuid.UUID(value) except ValueError: - self.fail("%s is not a valid UUID value" % value, param, ctx) + self.fail("{} is not a valid UUID value".format(value), param, ctx) def __repr__(self): return "UUID" @@ -510,8 +516,9 @@ def convert(self, value, param, ctx): return f except (IOError, OSError) as e: # noqa: B014 self.fail( - "Could not open file: %s: %s" - % (filename_to_ui(value), get_streerror(e),), + "Could not open file: {}: {}".format( + filename_to_ui(value), get_streerror(e) + ), param, ctx, ) @@ -603,7 +610,7 @@ def convert(self, value, param, ctx): if not self.exists: return self.coerce_path_result(rv) self.fail( - '{} "{}" does not exist.'.format( + "{} '{}' does not exist.".format( self.path_type, filename_to_ui(value) ), param, @@ -612,13 +619,13 @@ def convert(self, value, param, ctx): if not self.file_okay and stat.S_ISREG(st.st_mode): self.fail( - '{} "{}" is a file.'.format(self.path_type, filename_to_ui(value)), + "{} '{}' is a file.".format(self.path_type, filename_to_ui(value)), param, ctx, ) if not self.dir_okay and stat.S_ISDIR(st.st_mode): self.fail( - '{} "{}" is a directory.'.format( + "{} '{}' is a directory.".format( self.path_type, filename_to_ui(value) ), param, @@ -626,15 +633,17 @@ def convert(self, value, param, ctx): ) if self.writable and not os.access(value, os.W_OK): self.fail( - '%s "%s" is not writable.' - % (self.path_type, filename_to_ui(value)), + "{} '{}' is not writable.".format( + self.path_type, filename_to_ui(value) + ), param, ctx, ) if self.readable and not os.access(value, os.R_OK): self.fail( - '%s "%s" is not readable.' - % (self.path_type, filename_to_ui(value)), + "{} '{}' is not readable.".format( + self.path_type, filename_to_ui(value) + ), param, ctx, ) @@ -661,7 +670,7 @@ def __init__(self, types): @property def name(self): - return "<" + " ".join(ty.name for ty in self.types) + ">" + return "<{}>".format(" ".join(ty.name for ty in self.types)) @property def arity(self): @@ -670,15 +679,15 @@ def arity(self): def convert(self, value, param, ctx): if len(value) != len(self.types): raise TypeError( - "It would appear that nargs is set to conflict " - "with the composite type arity." + "It would appear that nargs is set to conflict with the" + " composite type arity." ) return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) def convert_type(ty, default=None): - """Converts a callable or python ty into the most appropriate param - ty. + """Converts a callable or python type into the most appropriate + param type. """ guessed_type = False if ty is None and default is not None: @@ -712,7 +721,7 @@ def convert_type(ty, default=None): try: if issubclass(ty, ParamType): raise AssertionError( - "Attempted to use an uninstantiated parameter type (%s)." % ty + "Attempted to use an uninstantiated parameter type ({}).".format(ty) ) except TypeError: pass diff --git a/src/click/utils.py b/src/click/utils.py index 58517b33b..79265e732 100644 --- a/src/click/utils.py +++ b/src/click/utils.py @@ -65,7 +65,7 @@ def make_default_short_help(help, max_length=45): for word in words: if word[-1:] == ".": done = True - new_length = result and 1 + len(word) or len(word) + new_length = 1 + len(word) if result else len(word) if total_length + new_length > max_length: result.append("...") done = True @@ -113,7 +113,7 @@ def __getattr__(self, name): def __repr__(self): if self._f is not None: return repr(self._f) - return "".format(self.name, self.mode) + return "".format(self.name, self.mode) def open(self): """Opens the file if it's not yet open. This call might fail with @@ -285,7 +285,7 @@ def get_binary_stream(name): """ opener = binary_streams.get(name) if opener is None: - raise TypeError("Unknown standard stream %r" % name) + raise TypeError("Unknown standard stream '{}'".format(name)) return opener() @@ -302,7 +302,7 @@ def get_text_stream(name, encoding=None, errors="strict"): """ opener = text_streams.get(name) if opener is None: - raise TypeError("Unknown standard stream %r" % name) + raise TypeError("Unknown standard stream '{}'".format(name)) return opener(encoding, errors) @@ -413,13 +413,13 @@ def get_app_dir(app_name, roaming=True, force_posix=False): application support folder. """ if WIN: - key = roaming and "APPDATA" or "LOCALAPPDATA" + key = "APPDATA" if roaming else "LOCALAPPDATA" folder = os.environ.get(key) if folder is None: folder = os.path.expanduser("~") return os.path.join(folder, app_name) if force_posix: - return os.path.join(os.path.expanduser("~/." + _posixify(app_name))) + return os.path.join(os.path.expanduser("~/.{}".format(_posixify(app_name)))) if sys.platform == "darwin": return os.path.join( os.path.expanduser("~/Library/Application Support"), app_name diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 85ba21246..0b510c00b 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -13,15 +13,12 @@ def test_nargs_star(runner): @click.argument("src", nargs=-1) @click.argument("dst") def copy(src, dst): - click.echo("src=%s" % "|".join(src)) - click.echo("dst=%s" % dst) + click.echo("src={}".format("|".join(src))) + click.echo("dst={}".format(dst)) result = runner.invoke(copy, ["foo.txt", "bar.txt", "dir"]) assert not result.exception - assert result.output.splitlines() == [ - "src=foo.txt|bar.txt", - "dst=dir", - ] + assert result.output.splitlines() == ["src=foo.txt|bar.txt", "dst=dir"] def test_nargs_default(runner): @@ -38,15 +35,12 @@ def test_nargs_tup(runner): @click.argument("name", nargs=1) @click.argument("point", nargs=2, type=click.INT) def copy(name, point): - click.echo("name=%s" % name) - click.echo("point=%d/%d" % point) + click.echo("name={}".format(name)) + click.echo("point={0[0]}/{0[1]}".format(point)) result = runner.invoke(copy, ["peter", "1", "2"]) assert not result.exception - assert result.output.splitlines() == [ - "name=peter", - "point=1/2", - ] + assert result.output.splitlines() == ["name=peter", "point=1/2"] def test_nargs_tup_composite(runner): @@ -62,13 +56,11 @@ def test_nargs_tup_composite(runner): @click.command() @click.argument("item", **opts) def copy(item): - click.echo("name=%s id=%d" % item) + click.echo("name={0[0]} id={0[1]:d}".format(item)) result = runner.invoke(copy, ["peter", "1"]) assert not result.exception - assert result.output.splitlines() == [ - "name=peter id=1", - ] + assert result.output.splitlines() == ["name=peter id=1"] def test_nargs_err(runner): @@ -203,7 +195,7 @@ def test_empty_nargs(runner): @click.command() @click.argument("arg", nargs=-1) def cmd(arg): - click.echo("arg:" + "|".join(arg)) + click.echo("arg:{}".format("|".join(arg))) result = runner.invoke(cmd, []) assert result.exit_code == 0 @@ -212,22 +204,22 @@ def cmd(arg): @click.command() @click.argument("arg", nargs=-1, required=True) def cmd2(arg): - click.echo("arg:" + "|".join(arg)) + click.echo("arg:{}".format("|".join(arg))) result = runner.invoke(cmd2, []) assert result.exit_code == 2 - assert 'Missing argument "ARG..."' in result.output + assert "Missing argument 'ARG...'" in result.output def test_missing_arg(runner): @click.command() @click.argument("arg") def cmd(arg): - click.echo("arg:" + arg) + click.echo("arg:{}".format(arg)) result = runner.invoke(cmd, []) assert result.exit_code == 2 - assert 'Missing argument "ARG".' in result.output + assert "Missing argument 'ARG'." in result.output def test_missing_argument_string_cast(): @@ -260,18 +252,10 @@ def cmd(f, files): click.echo(f) result = runner.invoke(cmd, ["--", "-foo", "bar"]) - assert result.output.splitlines() == [ - "-foo", - "bar", - "", - ] + assert result.output.splitlines() == ["-foo", "bar", ""] result = runner.invoke(cmd, ["-f", "-x", "--", "-foo", "bar"]) - assert result.output.splitlines() == [ - "-foo", - "bar", - "-x", - ] + assert result.output.splitlines() == ["-foo", "bar", "-x"] def test_nargs_star_ordering(runner): @@ -284,11 +268,7 @@ def cmd(a, b, c): click.echo(arg) result = runner.invoke(cmd, ["a", "b", "c"]) - assert result.output.splitlines() == [ - PY2 and "(u'a',)" or "('a',)", - "b", - "c", - ] + assert result.output.splitlines() == ["(u'a',)" if PY2 else "('a',)", "b", "c"] def test_nargs_specified_plus_star_ordering(runner): @@ -302,9 +282,9 @@ def cmd(a, b, c): result = runner.invoke(cmd, ["a", "b", "c", "d", "e", "f"]) assert result.output.splitlines() == [ - PY2 and "(u'a', u'b', u'c')" or "('a', 'b', 'c')", + "(u'a', u'b', u'c')" if PY2 else "('a', 'b', 'c')", "d", - PY2 and "(u'e', u'f')" or "('e', 'f')", + "(u'e', u'f')" if PY2 else "('e', 'f')", ] diff --git a/tests/test_basic.py b/tests/test_basic.py index 59566cbea..f07b6d1f5 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -82,7 +82,7 @@ def test_basic_option(runner): @click.command() @click.option("--foo", default="no value") def cli(foo): - click.echo("FOO:[%s]" % foo) + click.echo(u"FOO:[{}]".format(foo)) result = runner.invoke(cli, []) assert not result.exception @@ -109,7 +109,7 @@ def test_int_option(runner): @click.command() @click.option("--foo", default=42) def cli(foo): - click.echo("FOO:[%s]" % (foo * 2)) + click.echo("FOO:[{}]".format(foo * 2)) result = runner.invoke(cli, []) assert not result.exception @@ -121,7 +121,7 @@ def cli(foo): result = runner.invoke(cli, ["--foo=bar"]) assert result.exception - assert 'Invalid value for "--foo": bar is not a valid integer' in result.output + assert "Invalid value for '--foo': bar is not a valid integer" in result.output def test_uuid_option(runner): @@ -131,7 +131,7 @@ def test_uuid_option(runner): ) def cli(u): assert type(u) is uuid.UUID - click.echo("U:[%s]" % u) + click.echo("U:[{}]".format(u)) result = runner.invoke(cli, []) assert not result.exception @@ -143,7 +143,7 @@ def cli(u): result = runner.invoke(cli, ["--u=bar"]) assert result.exception - assert 'Invalid value for "--u": bar is not a valid UUID value' in result.output + assert "Invalid value for '--u': bar is not a valid UUID value" in result.output def test_float_option(runner): @@ -151,7 +151,7 @@ def test_float_option(runner): @click.option("--foo", default=42, type=click.FLOAT) def cli(foo): assert type(foo) is float - click.echo("FOO:[%s]" % foo) + click.echo("FOO:[{}]".format(foo)) result = runner.invoke(cli, []) assert not result.exception @@ -163,7 +163,7 @@ def cli(foo): result = runner.invoke(cli, ["--foo=bar"]) assert result.exception - assert 'Invalid value for "--foo": bar is not a valid float' in result.output + assert "Invalid value for '--foo': bar is not a valid float" in result.output def test_boolean_option(runner): @@ -182,7 +182,7 @@ def cli(with_foo): assert result.output == "False\n" result = runner.invoke(cli, []) assert not result.exception - assert result.output == "%s\n" % default + assert result.output == "{}\n".format(default) for default in True, False: @@ -193,10 +193,10 @@ def cli(flag): result = runner.invoke(cli, ["--flag"]) assert not result.exception - assert result.output == "%s\n" % (not default) + assert result.output == "{}\n".format(not default) result = runner.invoke(cli, []) assert not result.exception - assert result.output == "%s\n" % (default) + assert result.output == "{}\n".format(default) def test_boolean_conversion(runner): @@ -219,7 +219,7 @@ def cli(flag): result = runner.invoke(cli, []) assert not result.exception - assert result.output == "%s\n" % default + assert result.output == "{}\n".format(default) def test_file_option(runner): @@ -281,7 +281,7 @@ def input_non_lazy(file): result_in = runner.invoke(input_non_lazy, ["--file=example.txt"]) assert result_in.exit_code == 2 assert ( - 'Invalid value for "--file": Could not open file: example.txt' + "Invalid value for '--file': Could not open file: example.txt" in result_in.output ) @@ -303,22 +303,17 @@ def write_to_dir(o): assert f.read() == b"meh\n" result = runner.invoke(write_to_dir, ["-O", "test/foo.txt"]) - assert ( - 'Invalid value for "-O": Directory "test/foo.txt" is a file.' - in result.output - ) + assert "is a file" in result.output @click.command() @click.option("-f", type=click.Path(exists=True)) def showtype(f): - click.echo("is_file=%s" % os.path.isfile(f)) - click.echo("is_dir=%s" % os.path.isdir(f)) + click.echo("is_file={}".format(os.path.isfile(f))) + click.echo("is_dir={}".format(os.path.isdir(f))) with runner.isolated_filesystem(): result = runner.invoke(showtype, ["-f", "xxx"]) - assert ( - 'Error: Invalid value for "-f": Path "xxx" does not exist' in result.output - ) + assert "does not exist" in result.output result = runner.invoke(showtype, ["-f", "."]) assert "is_file=False" in result.output @@ -327,7 +322,7 @@ def showtype(f): @click.command() @click.option("-f", type=click.Path()) def exists(f): - click.echo("exists=%s" % os.path.exists(f)) + click.echo("exists={}".format(os.path.exists(f))) with runner.isolated_filesystem(): result = runner.invoke(exists, ["-f", "xxx"]) @@ -350,8 +345,8 @@ def cli(method): result = runner.invoke(cli, ["--method=meh"]) assert result.exit_code == 2 assert ( - 'Invalid value for "--method": invalid choice: meh. ' - "(choose from foo, bar, baz)" in result.output + "Invalid value for '--method': invalid choice: meh." + " (choose from foo, bar, baz)" in result.output ) result = runner.invoke(cli, ["--help"]) @@ -375,9 +370,9 @@ def cli(start_date): result = runner.invoke(cli, ["--start_date=2015-09"]) assert result.exit_code == 2 assert ( - 'Invalid value for "--start_date": ' - "invalid datetime format: 2015-09. " - "(choose from %Y-%m-%d, %Y-%m-%dT%H:%M:%S, %Y-%m-%d %H:%M:%S)" + "Invalid value for '--start_date':" + " invalid datetime format: 2015-09." + " (choose from %Y-%m-%d, %Y-%m-%dT%H:%M:%S, %Y-%m-%d %H:%M:%S)" ) in result.output result = runner.invoke(cli, ["--help"]) @@ -410,7 +405,7 @@ def cli(x): result = runner.invoke(cli, ["--x=6"]) assert result.exit_code == 2 assert ( - 'Invalid value for "--x": 6 is not in the valid range of 0 to 5.\n' + "Invalid value for '--x': 6 is not in the valid range of 0 to 5.\n" in result.output ) @@ -445,7 +440,7 @@ def cli(x): result = runner.invoke(cli, ["--x=6.0"]) assert result.exit_code == 2 assert ( - 'Invalid value for "--x": 6.0 is not in the valid range of 0 to 5.\n' + "Invalid value for '--x': 6.0 is not in the valid range of 0 to 5.\n" in result.output ) @@ -475,7 +470,7 @@ def cli(foo): result = runner.invoke(cli, []) assert result.exit_code == 2 - assert 'Missing option "--foo"' in result.output + assert "Missing option '--foo'" in result.output def test_evaluation_order(runner): diff --git a/tests/test_chain.py b/tests/test_chain.py index 6aa452c4f..c227270f2 100644 --- a/tests/test_chain.py +++ b/tests/test_chain.py @@ -7,8 +7,9 @@ def debug(): click.echo( - "%s=%s" - % (sys._getframe(1).f_code.co_name, "|".join(click.get_current_context().args),) + "{}={}".format( + sys._getframe(1).f_code.co_name, "|".join(click.get_current_context().args) + ) ) @@ -76,19 +77,16 @@ def cli(): @cli.command("sdist") @click.option("--format") def sdist(format): - click.echo("sdist called %s" % format) + click.echo("sdist called {}".format(format)) @cli.command("bdist") @click.option("--format") def bdist(format): - click.echo("bdist called %s" % format) + click.echo("bdist called {}".format(format)) result = runner.invoke(cli, ["bdist", "--format=1", "sdist", "--format=2"]) assert not result.exception - assert result.output.splitlines() == [ - "bdist called 1", - "sdist called 2", - ] + assert result.output.splitlines() == ["bdist called 1", "sdist called 2"] def test_chaining_with_arguments(runner): @@ -99,19 +97,16 @@ def cli(): @cli.command("sdist") @click.argument("format") def sdist(format): - click.echo("sdist called %s" % format) + click.echo("sdist called {}".format(format)) @cli.command("bdist") @click.argument("format") def bdist(format): - click.echo("bdist called %s" % format) + click.echo("bdist called {}".format(format)) result = runner.invoke(cli, ["bdist", "1", "sdist", "2"]) assert not result.exception - assert result.output.splitlines() == [ - "bdist called 1", - "sdist called 2", - ] + assert result.output.splitlines() == ["bdist called 1", "sdist called 2"] def test_pipeline(runner): @@ -146,24 +141,15 @@ def processor(iterator): result = runner.invoke(cli, ["-i", "-"], input="foo\nbar") assert not result.exception - assert result.output.splitlines() == [ - "foo", - "bar", - ] + assert result.output.splitlines() == ["foo", "bar"] result = runner.invoke(cli, ["-i", "-", "strip"], input="foo \n bar") assert not result.exception - assert result.output.splitlines() == [ - "foo", - "bar", - ] + assert result.output.splitlines() == ["foo", "bar"] result = runner.invoke(cli, ["-i", "-", "strip", "uppercase"], input="foo \n bar") assert not result.exception - assert result.output.splitlines() == [ - "FOO", - "BAR", - ] + assert result.output.splitlines() == ["FOO", "BAR"] def test_args_and_chain(runner): @@ -185,12 +171,7 @@ def c(): result = runner.invoke(cli, ["a", "b", "c"]) assert not result.exception - assert result.output.splitlines() == [ - "cli=", - "a=", - "b=", - "c=", - ] + assert result.output.splitlines() == ["cli=", "a=", "b=", "c="] def test_multicommand_arg_behavior(runner): @@ -211,7 +192,7 @@ def bad_cli2(): @click.group(chain=True) @click.argument("arg") def cli(arg): - click.echo("cli:%s" % arg) + click.echo("cli:{}".format(arg)) @cli.command() def a(): @@ -219,10 +200,7 @@ def a(): result = runner.invoke(cli, ["foo", "a"]) assert not result.exception - assert result.output.splitlines() == [ - "cli:foo", - "a", - ] + assert result.output.splitlines() == ["cli:foo", "a"] @pytest.mark.xfail @@ -249,9 +227,4 @@ def l1b(): result = runner.invoke(cli, ["l1a", "l2a", "l1b"]) assert not result.exception - assert result.output.splitlines() == [ - "cli=", - "l1a=", - "l2a=", - "l1b=", - ] + assert result.output.splitlines() == ["cli=", "l1a=", "l2a=", "l1b="] diff --git a/tests/test_commands.py b/tests/test_commands.py index bcb30bd14..1d99218cd 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -26,7 +26,7 @@ def test_other_command_forward(runner): @cli.command() @click.option("--count", default=1) def test(count): - click.echo("Count: %d" % count) + click.echo("Count: {:d}".format(count)) @cli.command() @click.option("--count", default=1) @@ -102,7 +102,7 @@ def test_group_with_args(runner): @click.group() @click.argument("obj") def cli(obj): - click.echo("obj=%s" % obj) + click.echo("obj={}".format(obj)) @cli.command() def move(): @@ -211,7 +211,7 @@ def cli(ctx, debug): @cli.command() @click.pass_context def sync(ctx): - click.echo("Debug is %s" % (ctx.obj["DEBUG"] and "on" or "off")) + click.echo("Debug is {}".format("on" if ctx.obj["DEBUG"] else "off")) result = runner.invoke(cli, ["sync"]) assert result.exception is None @@ -264,8 +264,8 @@ def test_unprocessed_options(runner): @click.argument("args", nargs=-1, type=click.UNPROCESSED) @click.option("--verbose", "-v", count=True) def cli(verbose, args): - click.echo("Verbosity: %s" % verbose) - click.echo("Args: %s" % "|".join(args)) + click.echo("Verbosity: {}".format(verbose)) + click.echo("Args: {}".format("|".join(args))) result = runner.invoke(cli, ["-foo", "-vvvvx", "--muhaha", "x", "y", "-x"]) assert not result.exception diff --git a/tests/test_context.py b/tests/test_context.py index 4d131e74f..b3a20c6a5 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -90,8 +90,8 @@ def test(foo): assert result.exception is not None assert isinstance(result.exception, RuntimeError) assert ( - "Managed to invoke callback without a context object " - "of type 'Foo' existing" in str(result.exception) + "Managed to invoke callback without a context object of type" + " 'Foo' existing" in str(result.exception) ) @@ -129,7 +129,7 @@ def cli(ctx): def test_context_meta(runner): - LANG_KEY = __name__ + ".lang" + LANG_KEY = "{}.lang".format(__name__) def set_language(value): click.get_current_context().meta[LANG_KEY] = value diff --git a/tests/test_defaults.py b/tests/test_defaults.py index 7b1d7e563..ce4490331 100644 --- a/tests/test_defaults.py +++ b/tests/test_defaults.py @@ -6,7 +6,7 @@ def test_basic_defaults(runner): @click.option("--foo", default=42, type=click.FLOAT) def cli(foo): assert type(foo) is float - click.echo("FOO:[%s]" % foo) + click.echo("FOO:[{}]".format(foo)) result = runner.invoke(cli, []) assert not result.exception @@ -23,10 +23,7 @@ def cli(foo): result = runner.invoke(cli, []) assert not result.exception - assert result.output.splitlines() == [ - "23.0", - "42.0", - ] + assert result.output.splitlines() == ["23.0", "42.0"] def test_nargs_plus_multiple(runner): @@ -36,11 +33,8 @@ def test_nargs_plus_multiple(runner): ) def cli(arg): for item in arg: - click.echo("<%d|%d>" % item) + click.echo("<{0[0]:d}|{0[1]:d}>".format(item)) result = runner.invoke(cli, []) assert not result.exception - assert result.output.splitlines() == [ - "<1|2>", - "<3|4>", - ] + assert result.output.splitlines() == ["<1|2>", "<3|4>"] diff --git a/tests/test_formatting.py b/tests/test_formatting.py index 30ab288e6..4fabbb20f 100644 --- a/tests/test_formatting.py +++ b/tests/test_formatting.py @@ -151,15 +151,15 @@ def test_formatting_usage_error(runner): @click.command() @click.argument("arg") def cmd(arg): - click.echo("arg:" + arg) + click.echo("arg:{}".format(arg)) result = runner.invoke(cmd, []) assert result.exit_code == 2 assert result.output.splitlines() == [ "Usage: cmd [OPTIONS] ARG", - 'Try "cmd --help" for help.', + "Try 'cmd --help' for help.", "", - 'Error: Missing argument "ARG".', + "Error: Missing argument 'ARG'.", ] @@ -178,9 +178,9 @@ def cmd(arg): assert result.exit_code == 2 assert result.output.splitlines() == [ "Usage: cmd [OPTIONS] metavar", - 'Try "cmd --help" for help.', + "Try 'cmd --help' for help.", "", - 'Error: Missing argument "metavar".', + "Error: Missing argument 'metavar'.", ] @@ -194,9 +194,9 @@ def cmd(arg): assert result.exit_code == 2 assert result.output.splitlines() == [ "Usage: cmd [OPTIONS] metavar", - 'Try "cmd --help" for help.', + "Try 'cmd --help' for help.", "", - 'Error: Invalid value for "metavar": 3.14 is not a valid integer', + "Error: Invalid value for 'metavar': 3.14 is not a valid integer", ] @@ -208,15 +208,15 @@ def cmd(): @cmd.command() @click.argument("bar") def foo(bar): - click.echo("foo:" + bar) + click.echo("foo:{}".format(bar)) result = runner.invoke(cmd, ["foo"]) assert result.exit_code == 2 assert result.output.splitlines() == [ "Usage: cmd foo [OPTIONS] BAR", - 'Try "cmd foo --help" for help.', + "Try 'cmd foo --help' for help.", "", - 'Error: Missing argument "BAR".', + "Error: Missing argument 'BAR'.", ] @@ -224,14 +224,14 @@ def test_formatting_usage_error_no_help(runner): @click.command(add_help_option=False) @click.argument("arg") def cmd(arg): - click.echo("arg:" + arg) + click.echo("arg:{}".format(arg)) result = runner.invoke(cmd, []) assert result.exit_code == 2 assert result.output.splitlines() == [ "Usage: cmd [OPTIONS] ARG", "", - 'Error: Missing argument "ARG".', + "Error: Missing argument 'ARG'.", ] @@ -239,15 +239,15 @@ def test_formatting_usage_custom_help(runner): @click.command(context_settings=dict(help_option_names=["--man"])) @click.argument("arg") def cmd(arg): - click.echo("arg:" + arg) + click.echo("arg:{}".format(arg)) result = runner.invoke(cmd, []) assert result.exit_code == 2 assert result.output.splitlines() == [ "Usage: cmd [OPTIONS] ARG", - 'Try "cmd --man" for help.', + "Try 'cmd --man' for help.", "", - 'Error: Missing argument "ARG".', + "Error: Missing argument 'ARG'.", ] diff --git a/tests/test_options.py b/tests/test_options.py index 3974803a3..4baa37451 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -47,21 +47,18 @@ def test_nargs_tup_composite_mult(runner): @click.option("--item", type=(str, int), multiple=True) def copy(item): for item in item: - click.echo("name=%s id=%d" % item) + click.echo("name={0[0]} id={0[1]:d}".format(item)) result = runner.invoke(copy, ["--item", "peter", "1", "--item", "max", "2"]) assert not result.exception - assert result.output.splitlines() == [ - "name=peter id=1", - "name=max id=2", - ] + assert result.output.splitlines() == ["name=peter id=1", "name=max id=2"] def test_counting(runner): @click.command() @click.option("-v", count=True, help="Verbosity", type=click.IntRange(0, 3)) def cli(v): - click.echo("verbosity=%d" % v) + click.echo("verbosity={:d}".format(v)) result = runner.invoke(cli, ["-vvv"]) assert not result.exception @@ -70,7 +67,7 @@ def cli(v): result = runner.invoke(cli, ["-vvvv"]) assert result.exception assert ( - 'Invalid value for "-v": 4 is not in the valid range of 0 to 3.' + "Invalid value for '-v': 4 is not in the valid range of 0 to 3." in result.output ) @@ -105,14 +102,14 @@ def cli(message): result = runner.invoke(cli, []) assert result.exception - assert 'Error: Missing option "-m" / "--message".' in result.output + assert "Error: Missing option '-m' / '--message'." in result.output def test_empty_envvar(runner): @click.command() @click.option("--mypath", type=click.Path(exists=True), envvar="MYPATH") def cli(mypath): - click.echo("mypath: %s" % mypath) + click.echo("mypath: {}".format(mypath)) result = runner.invoke(cli, [], env={"MYPATH": ""}) assert result.exit_code == 0 @@ -149,7 +146,7 @@ def cmd(arg): cmd, [], auto_envvar_prefix="TEST", - env={"TEST_ARG": "foo%sbar" % os.path.pathsep}, + env={"TEST_ARG": "foo{}bar".format(os.path.pathsep)}, ) assert not result.exception assert result.output == "foo|bar\n" @@ -306,7 +303,7 @@ def cmd(foo): click.echo(foo) result = runner.invoke(cmd, ["--foo", "-1"]) - assert 'Invalid value for "--foo": Value needs to be positive' in result.output + assert "Invalid value for '--foo': Value needs to be positive" in result.output result = runner.invoke(cmd, ["--foo", "42"]) assert result.output == "42\n" @@ -359,7 +356,7 @@ def cmd(foo): result = runner.invoke(cmd) assert result.exit_code == 2 error, separator, choices = result.output.partition("Choose from") - assert 'Error: Missing option "--foo". ' in error + assert "Error: Missing option '--foo'. " in error assert "Choose from" in separator assert "foo" in choices assert "bar" in choices diff --git a/tests/test_testing.py b/tests/test_testing.py index f2d2d9a21..22a285d90 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -65,7 +65,7 @@ def test_prompts(): @click.command() @click.option("--foo", prompt=True) def test(foo): - click.echo("foo=%s" % foo) + click.echo("foo={}".format(foo)) runner = CliRunner() result = runner.invoke(test, input="wau wau\n") @@ -75,7 +75,7 @@ def test(foo): @click.command() @click.option("--foo", prompt=True, hide_input=True) def test(foo): - click.echo("foo=%s" % foo) + click.echo("foo={}".format(foo)) runner = CliRunner() result = runner.invoke(test, input="wau wau\n") @@ -131,7 +131,7 @@ def cli(): assert not result.exception result = runner.invoke(cli, color=True) - assert result.output == click.style("hello world", fg="blue") + "\n" + assert result.output == "{}\n".format(click.style("hello world", fg="blue")) assert not result.exception @@ -219,7 +219,7 @@ def cli_no_error(): def test_env(): @click.command() def cli_env(): - click.echo("ENV=%s" % os.environ["TEST_CLICK_ENV"]) + click.echo("ENV={}".format(os.environ["TEST_CLICK_ENV"])) runner = CliRunner() diff --git a/tests/test_utils.py b/tests/test_utils.py index 52472249b..d86f59b81 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -203,21 +203,21 @@ def test_echo_color_flag(monkeypatch, capfd): click.echo(styled_text, color=False) out, err = capfd.readouterr() - assert out == text + "\n" + assert out == "{}\n".format(text) click.echo(styled_text, color=True) out, err = capfd.readouterr() - assert out == styled_text + "\n" + assert out == "{}\n".format(styled_text) isatty = True click.echo(styled_text) out, err = capfd.readouterr() - assert out == styled_text + "\n" + assert out == "{}\n".format(styled_text) isatty = False click.echo(styled_text) out, err = capfd.readouterr() - assert out == text + "\n" + assert out == "{}\n".format(text) @pytest.mark.skipif(WIN, reason="Test too complex to make work windows.")