diff --git a/build/common_virtualenv_packages.txt b/build/common_virtualenv_packages.txt index a33ad2d8ad36c..3079b689aa715 100644 --- a/build/common_virtualenv_packages.txt +++ b/build/common_virtualenv_packages.txt @@ -62,7 +62,7 @@ vendored:third_party/python/blessings vendored:third_party/python/cbor2 vendored:third_party/python/certifi vendored:third_party/python/chardet -vendored:third_party/python/click +vendored:third_party/python/Click vendored:third_party/python/colorama vendored:third_party/python/compare_locales vendored:third_party/python/cookies diff --git a/third_party/python/Click/Click-7.0.dist-info/LICENSE.txt b/third_party/python/Click/Click-7.0.dist-info/LICENSE.txt new file mode 100644 index 0000000000000..87ce152aafba7 --- /dev/null +++ b/third_party/python/Click/Click-7.0.dist-info/LICENSE.txt @@ -0,0 +1,39 @@ +Copyright © 2014 by the Pallets team. + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as +well as documentation, with or without modification, are permitted +provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +---- + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright © 2001-2006 Gregory P. Ward. All rights reserved. +Copyright © 2002-2006 Python Software Foundation. All rights reserved. diff --git a/third_party/python/click/click-7.1.2.dist-info/METADATA b/third_party/python/Click/Click-7.0.dist-info/METADATA similarity index 63% rename from third_party/python/click/click-7.1.2.dist-info/METADATA rename to third_party/python/Click/Click-7.0.dist-info/METADATA index 00d697493a2fa..625bdaddbba16 100644 --- a/third_party/python/click/click-7.1.2.dist-info/METADATA +++ b/third_party/python/Click/Click-7.0.dist-info/METADATA @@ -1,11 +1,13 @@ Metadata-Version: 2.1 -Name: click -Version: 7.1.2 +Name: Click +Version: 7.0 Summary: Composable command line interface toolkit Home-page: https://palletsprojects.com/p/click/ -Maintainer: Pallets +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets Team Maintainer-email: contact@palletsprojects.com -License: BSD-3-Clause +License: BSD Project-URL: Documentation, https://click.palletsprojects.com/ Project-URL: Code, https://github.com/pallets/click Project-URL: Issue tracker, https://github.com/pallets/click/issues @@ -16,8 +18,13 @@ Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* \$ click\_ ========== @@ -45,7 +52,9 @@ Install and update using `pip`_: .. code-block:: text - $ pip install -U click + $ pip install click + +Click supports Python 3.4 and newer, Python 2.7, and PyPy. .. _pip: https://pip.pypa.io/en/stable/quickstart/ @@ -53,21 +62,26 @@ Install and update using `pip`_: A Simple Example ---------------- +What does it look like? Here is an example of a simple Click program: + .. code-block:: python import click @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(f"Hello, {name}!") + click.echo("Hello, %s!" % name) if __name__ == '__main__': hello() +And what it looks like when run: + .. code-block:: text $ python hello.py --count=3 @@ -91,12 +105,17 @@ donate today`_. Links ----- -- Website: https://palletsprojects.com/p/click/ -- Documentation: https://click.palletsprojects.com/ -- Releases: https://pypi.org/project/click/ -- Code: https://github.com/pallets/click -- Issue tracker: https://github.com/pallets/click/issues -- Test status: https://dev.azure.com/pallets/click/_build -- Official chat: https://discord.gg/t6rrQZH +* Website: https://palletsprojects.com/p/click/ +* Documentation: https://click.palletsprojects.com/ +* License: `BSD `_ +* Releases: https://pypi.org/project/click/ +* Code: https://github.com/pallets/click +* Issue tracker: https://github.com/pallets/click/issues +* Test status: + + * Linux, Mac: https://travis-ci.org/pallets/click + * Windows: https://ci.appveyor.com/project/pallets/click + +* Test coverage: https://codecov.io/gh/pallets/click diff --git a/third_party/python/Click/Click-7.0.dist-info/RECORD b/third_party/python/Click/Click-7.0.dist-info/RECORD new file mode 100644 index 0000000000000..370e8ce1b0ab4 --- /dev/null +++ b/third_party/python/Click/Click-7.0.dist-info/RECORD @@ -0,0 +1,22 @@ +Click-7.0.dist-info/LICENSE.txt,sha256=4hIxn676T0Wcisk3_chVcECjyrivKTZsoqSNI5AlIlw,1876 +Click-7.0.dist-info/METADATA,sha256=-r8jeke3Zer4diRvT1MjFZuiJ6yTT_qFP39svLqdaLI,3516 +Click-7.0.dist-info/RECORD,, +Click-7.0.dist-info/WHEEL,sha256=gduuPyBvFJQSQ0zdyxF7k0zynDXbIbvg5ZBHoXum5uk,110 +Click-7.0.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=HjGThQ7tef9kkwCV371TBnrf0SAi6fKfU_jtEnbYTvQ,2789 +click/_bashcomplete.py,sha256=iaNUmtxag0YPfxba3TDYCNietiTMQIrvhRLj-H8okFU,11014 +click/_compat.py,sha256=vYmvoj4opPxo-c-2GMQQjYT_r_QkOKybkfGoeVrt0dA,23399 +click/_termui_impl.py,sha256=xHmLtOJhKUCVD6168yucJ9fknUJPAMs0eUTPgVUO-GQ,19611 +click/_textwrap.py,sha256=gwS4m7bdQiJnzaDG8osFcRb-5vn4t4l2qSCy-5csCEc,1198 +click/_unicodefun.py,sha256=QHy2_5jYlX-36O-JVrTHNnHOqg8tquUR0HmQFev7Ics,4364 +click/_winconsole.py,sha256=PPWVak8Iikm_gAPsxMrzwsVFCvHgaW3jPaDWZ1JBl3U,8965 +click/core.py,sha256=q8FLcDZsagBGSRe5Y9Hi_FGvAeZvusNfoO5EkhkSQ8Y,75305 +click/decorators.py,sha256=idKt6duLUUfAFftrHoREi8MJSd39XW36pUVHthdglwk,11226 +click/exceptions.py,sha256=CNpAjBAE7qjaV4WChxQeak95e5yUOau8AsvT-8m6wss,7663 +click/formatting.py,sha256=eh-cypTUAhpI3HD-K4ZpR3vCiURIO62xXvKkR3tNUTM,8889 +click/globals.py,sha256=oQkou3ZQ5DgrbVM6BwIBirwiqozbjfirzsLGAlLRRdg,1514 +click/parser.py,sha256=m-nGZz4VwprM42_qtFlWFGo7yRJQxkBlRcZodoH593Y,15510 +click/termui.py,sha256=o_ZXB2jyvL2Rce7P_bFGq452iyBq9ykJyRApIPMCZO0,23207 +click/testing.py,sha256=aYGqY_iWLu2p4k7lkuJ6t3fqpf6aPGqTsyLzNY_ngKg,13062 +click/types.py,sha256=2Q929p-aBP_ZYuMFJqJR-Ipucofv3fmDc5JzBDPmzJU,23287 +click/utils.py,sha256=6-D0WkAxvv9FkgHXSHwDIv0l9Gdx9Mm6Z5vuKNLIfZI,15763 diff --git a/third_party/python/click/click-7.1.2.dist-info/WHEEL b/third_party/python/Click/Click-7.0.dist-info/WHEEL similarity index 70% rename from third_party/python/click/click-7.1.2.dist-info/WHEEL rename to third_party/python/Click/Click-7.0.dist-info/WHEEL index ef99c6cf3283b..1316c41d0706f 100644 --- a/third_party/python/click/click-7.1.2.dist-info/WHEEL +++ b/third_party/python/Click/Click-7.0.dist-info/WHEEL @@ -1,5 +1,5 @@ Wheel-Version: 1.0 -Generator: bdist_wheel (0.34.2) +Generator: bdist_wheel (0.31.1) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any diff --git a/third_party/python/click/click-7.1.2.dist-info/top_level.txt b/third_party/python/Click/Click-7.0.dist-info/top_level.txt similarity index 100% rename from third_party/python/click/click-7.1.2.dist-info/top_level.txt rename to third_party/python/Click/Click-7.0.dist-info/top_level.txt diff --git a/third_party/python/Click/click/__init__.py b/third_party/python/Click/click/__init__.py new file mode 100644 index 0000000000000..d3c33660a9e03 --- /dev/null +++ b/third_party/python/Click/click/__init__.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +""" +click +~~~~~ + +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. + +:copyright: © 2014 by the Pallets team. +:license: BSD, see LICENSE.rst for more details. +""" + +# Core classes +from .core import Context, BaseCommand, Command, MultiCommand, Group, \ + CommandCollection, Parameter, Option, Argument + +# Globals +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', +] + + +# Controls if click should emit the warning about the use of unicode +# literals. +disable_unicode_literals_warning = False + + +__version__ = '7.0' diff --git a/third_party/python/click/click/_bashcomplete.py b/third_party/python/Click/click/_bashcomplete.py similarity index 59% rename from third_party/python/click/click/_bashcomplete.py rename to third_party/python/Click/click/_bashcomplete.py index 8bca24480f751..a5f1084c9abb4 100644 --- a/third_party/python/click/click/_bashcomplete.py +++ b/third_party/python/Click/click/_bashcomplete.py @@ -2,22 +2,20 @@ import os import re -from .core import Argument -from .core import MultiCommand -from .core import Option +from .utils import echo 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 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[*]}" \\ @@ -30,8 +28,7 @@ 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 @@ -39,17 +36,13 @@ } %(complete_func)setup -""" - -COMPLETION_SCRIPT_ZSH = """ -#compdef %(script_names)s +''' +COMPLETION_SCRIPT_ZSH = ''' %(complete_func)s() { local -a completions local -a completions_with_descriptions local -a response - (( ! $+commands[%(script_names)s] )) && return 1 - response=("${(@f)$( env COMP_WORDS=\"${words[*]}\" \\ COMP_CWORD=$((CURRENT-1)) \\ %(autocomplete_var)s=\"complete_zsh\" \\ @@ -64,51 +57,34 @@ done if [ -n "$completions_with_descriptions" ]; then - _describe -V unsorted completions_with_descriptions -U + _describe -V unsorted completions_with_descriptions -U -Q fi if [ -n "$completions" ]; then - compadd -U -V unsorted -a completions + compadd -U -V unsorted -Q -a completions fi compstate[insert]="automenu" } 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_scripts = { - "bash": COMPLETION_SCRIPT_BASH, - "zsh": COMPLETION_SCRIPT_ZSH, - "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("-", "_")) - script = _completion_scripts.get(shell, COMPLETION_SCRIPT_BASH) - return ( - script - % { - "complete_func": "_{}_completion".format(cf_name), - "script_names": prog_name, - "autocomplete_var": complete_var, - } - ).strip() + ";" + cf_name = _invalid_ident_char_re.sub('', prog_name.replace('-', '_')) + script = COMPLETION_SCRIPT_ZSH if shell == 'zsh' else COMPLETION_SCRIPT_BASH + return (script % { + 'complete_func': '_%s_completion' % cf_name, + 'script_names': prog_name, + 'autocomplete_var': complete_var, + }).strip() + ';' 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 @@ -122,9 +98,8 @@ 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. @@ -132,14 +107,10 @@ 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 @@ -151,29 +122,25 @@ 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] == "-" + return param_str and param_str[:1] == '-' 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 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): @@ -184,12 +151,10 @@ 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 @@ -198,11 +163,8 @@ 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 @@ -218,16 +180,14 @@ 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 @@ -248,25 +208,15 @@ 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) - ] - ) - - # Walk up the context list and add any other completion - # possibilities from chained commands + [(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): @@ -283,30 +233,23 @@ def get_choices(cli, prog_name, args, incomplete): if ctx is None: return [] - 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]) incomplete = partition_incomplete[2] elif incomplete == WORDBREAK: - incomplete = "" + incomplete = '' completions = [] - if not has_double_dash and start_of_option(incomplete): + if 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: @@ -323,53 +266,28 @@ 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]) if 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 "_") - - return True - - -def do_complete_fish(cli, prog_name): - cwords = split_arg_string(os.environ["COMP_WORDS"]) - incomplete = os.environ["COMP_CWORD"] - args = cwords[1:] - - for item in get_choices(cli, prog_name, args, incomplete): - if item[1]: - echo("{arg}\t{desc}".format(arg=item[0], desc=item[1])) - else: - echo(item[0]) + # ZSH has trouble dealing with empty array parameters when returned from commands, so use a well defined character '_' to indicate no description is present. + echo(item[1] if item[1] else '_') return True def bashcomplete(cli, prog_name, complete_var, complete_instr): - if "_" in complete_instr: - command, shell = complete_instr.split("_", 1) - else: - command = complete_instr - shell = "bash" - - if command == "source": + if complete_instr.startswith('source'): + shell = 'zsh' if complete_instr == 'source_zsh' else 'bash' echo(get_completion_script(prog_name, complete_var, shell)) return True - elif command == "complete": - if shell == "fish": - return do_complete_fish(cli, prog_name) - elif shell in {"bash", "zsh"}: - return do_complete(cli, prog_name, shell == "zsh") - + elif complete_instr == 'complete' or complete_instr == 'complete_zsh': + return do_complete(cli, prog_name, complete_instr == 'complete_zsh') return False diff --git a/third_party/python/click/click/_compat.py b/third_party/python/Click/click/_compat.py similarity index 62% rename from third_party/python/click/click/_compat.py rename to third_party/python/Click/click/_compat.py index 60cb115bc5091..937e2301d4935 100644 --- a/third_party/python/click/click/_compat.py +++ b/third_party/python/Click/click/_compat.py @@ -1,80 +1,67 @@ -# flake8: noqa -import codecs +import re 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) +CYGWIN = sys.platform.startswith('cygwin') # 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['SERVER_SOFTWARE']) +WIN = sys.platform.startswith('win') and not APP_ENGINE DEFAULT_COLUMNS = 80 -_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") +_ansi_re = re.compile(r'\033\[((?:\d|;)*)([a-zA-Z])') 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: @@ -118,7 +105,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 @@ -131,7 +118,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: @@ -143,20 +130,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: @@ -168,6 +155,7 @@ def seekable(self): if PY2: text_type = unicode + bytes = str raw_input = raw_input string_types = (str, unicode) int_types = (int, long) @@ -177,7 +165,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 @@ -205,7 +193,6 @@ def set_binary_mode(f): except ImportError: pass else: - def set_binary_mode(f): try: fileno = f.fileno() @@ -220,7 +207,6 @@ def set_binary_mode(f): except ImportError: pass else: - def set_binary_mode(f): try: fileno = f.fileno() @@ -238,42 +224,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,) @@ -295,10 +281,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 @@ -313,7 +299,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. @@ -328,7 +314,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. @@ -341,142 +327,136 @@ 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") - - def _is_compat_stream_attr(stream, attr, value): - """A stream attribute is compatible if it is equal to the - desired value or the desired value is unset and the attribute - has a value. - """ - stream_value = getattr(stream, attr, None) - return stream_value == value or (value is None and stream_value is not None) + return is_ascii_encoding(getattr(stream, 'encoding', None) or 'ascii') 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) - - def _force_correct_text_stream( - text_stream, - encoding, - errors, - is_binary, - find_binary, - force_readable=False, - force_writable=False, - ): - if is_binary(text_stream, False): - binary_reader = text_stream + stream_encoding = getattr(stream, 'encoding', None) + stream_errors = getattr(stream, 'errors', None) + + # Perfect match. + if stream_encoding == encoding and stream_errors == errors: + return True + + # Otherwise, it's only a compatible stream if we did not ask for + # an encoding. + if encoding is None: + return stream_encoding is not None + + return False + + def _force_correct_text_reader(text_reader, encoding, errors, + force_readable=False): + if _is_binary_reader(text_reader, False): + binary_reader = text_reader 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) - ): - return text_stream - - # Otherwise, get the underlying binary reader. - binary_reader = find_binary(text_stream) - - # If that's not possible, silently use the original reader - # and get mojibake instead of exceptions. + # If there is no target encoding set, we need to verify that the + # reader is not actually misconfigured. + if encoding is None and not _stream_is_misconfigured(text_reader): + return text_reader + + if _is_compatible_text_stream(text_reader, encoding, errors): + return text_reader + + # If the reader has no encoding, we try to find the underlying + # binary reader for it. If that fails because the environment is + # misconfigured, we silently go with the same reader because this + # is too common to happen. In that case, mojibake is better than + # exceptions. + binary_reader = _find_binary_reader(text_reader) if binary_reader is None: - return text_stream + return text_reader - # Default errors to replace instead of strict in order to get - # something that works. + # At this point, we default the errors to replace instead of strict + # because nobody handles those errors anyways and at this point + # we're so fundamentally fucked that nothing can repair it. if errors is None: - errors = "replace" - - # Wrap the binary stream in a text stream with the correct - # encoding parameters. - return _make_text_stream( - binary_reader, - encoding, - errors, - force_readable=force_readable, - force_writable=force_writable, - ) - - def _force_correct_text_reader(text_reader, encoding, errors, force_readable=False): - return _force_correct_text_stream( - text_reader, - encoding, - errors, - _is_binary_reader, - _find_binary_reader, - force_readable=force_readable, - ) - - def _force_correct_text_writer(text_writer, encoding, errors, force_writable=False): - return _force_correct_text_stream( - text_writer, - encoding, - errors, - _is_binary_writer, - _find_binary_writer, - force_writable=force_writable, - ) + errors = 'replace' + return _make_text_stream(binary_reader, encoding, errors, + force_readable=force_readable) + + def _force_correct_text_writer(text_writer, encoding, errors, + force_writable=False): + if _is_binary_writer(text_writer, False): + binary_writer = text_writer + else: + # If there is no target encoding set, we need to verify that the + # writer is not actually misconfigured. + if encoding is None and not _stream_is_misconfigured(text_writer): + return text_writer + + if _is_compatible_text_stream(text_writer, encoding, errors): + return text_writer + + # If the writer has no encoding, we try to find the underlying + # binary writer for it. If that fails because the environment is + # misconfigured, we silently go with the same writer because this + # is too common to happen. In that case, mojibake is better than + # exceptions. + binary_writer = _find_binary_writer(text_writer) + if binary_writer is None: + return text_writer + + # At this point, we default the errors to replace instead of strict + # because nobody handles those errors anyways and at this point + # we're so fundamentally fucked that nothing can repair it. + if errors is None: + errors = 'replace' + return _make_text_stream(binary_writer, encoding, errors, + force_writable=force_writable) 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: @@ -484,107 +464,60 @@ 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 -def _wrap_io_open(file, mode, encoding, errors): - """On Python 2, :func:`io.open` returns a text file wrapper that - requires passing ``unicode`` to ``write``. Need to open the file in - binary mode then wrap it in a subclass that can write ``str`` and - ``unicode``. - - Also handles not passing ``encoding`` and ``errors`` in binary mode. - """ - binary = "b" in mode - - if binary: - kwargs = {} - else: - kwargs = {"encoding": encoding, "errors": errors} - - if not PY2 or binary: - return io.open(file, mode, **kwargs) - - f = io.open(file, "{}b".format(mode.replace("t", ""))) - return _make_text_stream(f, **kwargs) - - -def open_stream(filename, mode="r", encoding=None, errors="strict", atomic=False): - binary = "b" in mode - +def open_stream(filename, mode='r', encoding=None, errors='strict', + atomic=False): # 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 binary: + if filename == '-': + if any(m in mode for m in ['w', 'a', 'x']): + if 'b' in mode: return get_binary_stdout(), False return get_text_stdout(encoding=encoding, errors=errors), False - if binary: + if 'b' in mode: return get_binary_stdin(), False return get_text_stdin(encoding=encoding, errors=errors), False # Non-atomic writes directly go out through the regular open functions. if not atomic: - return _wrap_io_open(filename, mode, encoding, errors), True + if encoding is None: + return open(filename, mode), True + return io.open(filename, mode, encoding=encoding, errors=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 # functionality to wrap it in a Python file. Then we wrap it in an # atomic file that moves the file over on close. - import errno - import random + import tempfile + fd, tmp_filename = tempfile.mkstemp(dir=os.path.dirname(filename), + prefix='.__atomic-write') - try: - perm = os.stat(filename).st_mode - except OSError: - perm = None - - flags = os.O_RDWR | os.O_CREAT | os.O_EXCL - - if binary: - flags |= getattr(os, "O_BINARY", 0) + if encoding is not None: + f = io.open(fd, mode, encoding=encoding, errors=errors) + else: + f = os.fdopen(fd, mode) - while True: - tmp_filename = os.path.join( - os.path.dirname(filename), - ".__atomic-write{:08x}".format(random.randrange(1 << 32)), - ) - try: - fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) - break - except OSError as e: - if e.errno == errno.EEXIST or ( - os.name == "nt" - and e.errno == errno.EACCES - and os.path.isdir(e.filename) - and os.access(e.filename, os.W_OK) - ): - continue - raise - - if perm is not None: - os.chmod(tmp_filename, perm) # in case perm includes bits in umask - - f = _wrap_io_open(fd, mode, encoding, errors) return _AtomicFile(f, tmp_filename, os.path.realpath(filename)), True # 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: @@ -593,6 +526,7 @@ 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 @@ -634,26 +568,14 @@ def __repr__(self): def strip_ansi(value): - return _ansi_re.sub("", value) - - -def _is_jupyter_kernel_output(stream): - if WIN: - # TODO: Couldn't test on Windows, should't try to support until - # someone tests the details wrt colorama. - return - - while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): - stream = stream._stream - - return stream.__class__.__module__.startswith("ipykernel.") + return _ansi_re.sub('', value) def should_strip_ansi(stream=None, color=None): if color is None: if stream is None: stream = sys.stdin - return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not isatty(stream) return not color @@ -668,18 +590,16 @@ 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 @@ -721,15 +641,11 @@ 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 @@ -748,7 +664,6 @@ def isatty(stream): def _make_cached_stream_func(src_func, wrapper_func): cache = WeakKeyDictionary() - def func(): stream = src_func() try: @@ -764,23 +679,25 @@ 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/third_party/python/click/click/_termui_impl.py b/third_party/python/Click/click/_termui_impl.py similarity index 70% rename from third_party/python/click/click/_termui_impl.py rename to third_party/python/Click/click/_termui_impl.py index 88bec37701cf5..00a8e5ef1ca81 100644 --- a/third_party/python/click/click/_termui_impl.py +++ b/third_party/python/Click/click/_termui_impl.py @@ -1,34 +1,34 @@ # -*- coding: utf-8 -*- """ +click._termui_impl +~~~~~~~~~~~~~~~~~~ + This module contains implementations for the termui module. To keep the import time of Click down, some infrequently used functionality is placed in this module and only imported as needed. + +:copyright: © 2014 by the Pallets team. +:license: BSD, see LICENSE.rst for more details. """ -import contextlib -import math + import os import sys import time - -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 strip_ansi -from ._compat import term_len -from ._compat import WIN -from .exceptions import ClickException +import math +import contextlib +from ._compat import _default_text_stdout, range_type, PY2, isatty, \ + open_stream, strip_ansi, term_len, get_best_encoding, WIN, int_types, \ + CYGWIN from .utils import echo +from .exceptions import ClickException + -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): @@ -44,29 +44,19 @@ 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 @@ -75,7 +65,7 @@ def __init__( 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 @@ -87,7 +77,7 @@ def __init__( 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 @@ -114,21 +104,10 @@ 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() - def __next__(self): - # Iteration is defined in terms of a generator function, - # returned by iter(self); use that to define next(). This works - # because `self.iter` is an iterable consumed by that generator, - # so it is re-entry safe. Calling `next(self.generator())` - # twice works and does "what you want". - return next(iter(self)) - - # Python 2 compat - next = __next__ - def is_fast(self): return time.time() - self.start <= self.short_limit @@ -166,19 +145,20 @@ def format_eta(self): hours = t % 24 t //= 24 if t > 0: - return "{}d {:02}:{:02}:{:02}".format(t, hours, minutes, seconds) + days = t + return '%dd %02d:%02d:%02d' % (days, hours, minutes, seconds) else: - return "{:02}:{:02}:{:02}".format(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 += "/{}".format(self.length) + pos += '/%s' % self.length return pos def format_pct(self): - return "{: 4}%".format(int(self.pct * 100))[1:] + return ('% 4d%%' % int(self.pct * 100))[1:] def format_bar(self): if self.length_known: @@ -190,13 +170,9 @@ 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): @@ -217,14 +193,11 @@ 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 @@ -241,7 +214,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 @@ -256,8 +229,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(): @@ -297,19 +270,13 @@ def finish(self): self.finished = True def generator(self): - """Return a generator which yields the items added to the bar - during construction, and updates the progress bar *after* the - yielded block returns. """ - # WARNING: the iterator interface for `ProgressBar` relies on - # this and only works because this is a simple generator which - # doesn't create or manage additional state. If this function - # changes, the impact should be evaluated both against - # `iter(bar)` and `next(bar)`. `next()` in particular may call - # `self.generator()` repeatedly, and this must remain safe in - # order for that interface to work. + Returns a generator which yields the items added to the bar during + construction, and updates the progress bar *after* the yielded block + returns. + """ 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: @@ -328,25 +295,24 @@ 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 "{}"'.format(filename)) == 0: - return _pipepager(generator, "more", color) + if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0: + return _pipepager(generator, 'more', color) return _nullpager(stdout, generator, color) finally: os.unlink(filename) @@ -357,28 +323,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 = "{}{}".format(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: @@ -404,17 +370,16 @@ 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('{} "{}"'.format(cmd, filename)) + os.system(cmd + ' "' + filename + '"') finally: os.unlink(filename) @@ -428,7 +393,9 @@ 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 @@ -437,20 +404,19 @@ def __init__(self, editor=None, env=None, require_save=True, extension=".txt"): 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 {} >/dev/null 2>&1".format(editor)) == 0: + return 'notepad' + for editor in '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() @@ -458,47 +424,47 @@ def edit_file(self, filename): else: environ = None try: - c = subprocess.Popen( - '{} "{}"'.format(editor, filename), env=environ, shell=True, - ) + c = subprocess.Popen('%s "%s"' % (editor, filename), + env=environ, shell=True) exit_code = c.wait() if exit_code != 0: - raise ClickException("{}: Editing failed!".format(editor)) + raise ClickException('%s: Editing failed!' % editor) except OSError as e: - raise ClickException("{}: Editing failed: {}".format(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) @@ -511,18 +477,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: @@ -530,44 +496,44 @@ def _unquote_file(url): elif WIN: if locate: url = _unquote_file(url) - args = 'explorer /select,"{}"'.format(_unquote_file(url.replace('"', ""))) + args = 'explorer /select,"%s"' % _unquote_file( + url.replace('"', '')) else: - args = 'start {} "" "{}"'.format( - "/WAIT" if wait else "", url.replace('"', "") - ) + args = 'start %s "" "%s"' % ( + wait and '/WAIT' or '', url.replace('"', '')) return os.system(args) elif CYGWIN: if locate: url = _unquote_file(url) - args = 'cygstart "{}"'.format(os.path.dirname(url).replace('"', "")) + args = 'cygstart "%s"' % (os.path.dirname(url).replace('"', '')) else: - args = 'cygstart {} "{}"'.format("-w" if wait else "", url.replace('"', "")) + args = 'cygstart %s "%s"' % ( + wait and '-w' or '', url.replace('"', '')) return os.system(args) 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() @@ -614,14 +580,12 @@ 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 @@ -629,7 +593,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() @@ -650,7 +614,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/third_party/python/click/click/_textwrap.py b/third_party/python/Click/click/_textwrap.py similarity index 97% rename from third_party/python/click/click/_textwrap.py rename to third_party/python/Click/click/_textwrap.py index 6959087b7f317..7e776031eaa92 100644 --- a/third_party/python/click/click/_textwrap.py +++ b/third_party/python/Click/click/_textwrap.py @@ -3,6 +3,7 @@ class TextWrapper(textwrap.TextWrapper): + def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): space_left = max(width - cur_len, 1) @@ -34,4 +35,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/third_party/python/Click/click/_unicodefun.py b/third_party/python/Click/click/_unicodefun.py new file mode 100644 index 0000000000000..620edff37e260 --- /dev/null +++ b/third_party/python/Click/click/_unicodefun.py @@ -0,0 +1,125 @@ +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 +# support that. +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 + return 0 + frm = sys._getframe(1) + idx = 1 + while frm is not None: + 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: + return idx + else: + break + return 0 + + +def _check_for_unicode_literals(): + if not __debug__: + return + if not PY2 or click.disable_unicode_literals_warning: + return + bad_frame = _find_unicode_literals_frame() + 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/python3/'), + stacklevel=bad_frame) + + +def _verify_python3_env(): + """Ensures that the environment is good for unicode on Python 3.""" + if PY2: + return + try: + import locale + fs_enc = codecs.lookup(locale.getpreferredencoding()).name + except Exception: + fs_enc = 'ascii' + if fs_enc != 'ascii': + return + + extra = '' + if os.name == 'posix': + import subprocess + try: + rv = subprocess.Popen(['locale', '-a'], stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate()[0] + except OSError: + 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') + + for line in rv.splitlines(): + locale = line.strip() + if locale.lower().endswith(('.utf-8', '.utf8')): + good_locales.add(locale) + if locale.lower() in ('c.utf8', 'c.utf-8'): + has_c_utf8 = True + + 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.' + ) + 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' + ) + 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)) + + bad_locale = None + 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' + 'it does not exist. The exported locale is "%s" but it\n' + '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 + ) diff --git a/third_party/python/click/click/_winconsole.py b/third_party/python/Click/click/_winconsole.py similarity index 65% rename from third_party/python/click/click/_winconsole.py rename to third_party/python/Click/click/_winconsole.py index b6c4274af0e86..bbb080ddaea2b 100644 --- a/third_party/python/click/click/_winconsole.py +++ b/third_party/python/Click/click/_winconsole.py @@ -7,42 +7,24 @@ # 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 time 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 time +import ctypes import msvcrt - -from ._compat import _NonClosingTextIOWrapper -from ._compat import PY2 -from ._compat import text_type - +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 try: from ctypes import pythonapi - PyObject_GetBuffer = pythonapi.PyObject_GetBuffer PyBuffer_Release = pythonapi.PyBuffer_Release except ImportError: pythonapi = None +from ctypes.wintypes import LPWSTR, LPCWSTR c_ssize_p = POINTER(c_ssize_t) @@ -51,15 +33,12 @@ GetStdHandle = kernel32.GetStdHandle ReadConsoleW = kernel32.ReadConsoleW 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) -) -LocalFree = WINFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)( - ("LocalFree", windll.kernel32) -) +GetCommandLineW = WINFUNCTYPE(LPWSTR)( + ('GetCommandLineW', windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE( + POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ('CommandLineToArgvW', windll.shell32)) STDIN_HANDLE = GetStdHandle(-10) @@ -78,27 +57,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 @@ -106,7 +85,6 @@ 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 @@ -119,6 +97,7 @@ def get_buffer(obj, writable=False): class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle): self.handle = handle @@ -128,6 +107,7 @@ def isatty(self): class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self): return True @@ -136,26 +116,20 @@ 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(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: {}".format(GetLastError())) + raise OSError('Windows error: %s' % GetLastError()) if buffer[0] == EOF: return 0 @@ -163,30 +137,27 @@ 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 {}".format(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(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: @@ -195,6 +166,7 @@ def write(self, b): class ConsoleStream(object): + def __init__(self, text_stream, byte_stream): self._text_stream = text_stream self.buffer = byte_stream @@ -223,8 +195,9 @@ def isatty(self): return self.buffer.isatty() def __repr__(self): - return "".format( - self.name, self.encoding + return '' % ( + self.name, + self.encoding, ) @@ -234,7 +207,6 @@ 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. @@ -249,7 +221,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 @@ -258,11 +230,7 @@ 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) @@ -270,59 +238,43 @@ 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() def _get_windows_argv(): argc = c_int(0) argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) - if not argv_unicode: - raise WinError() - try: - argv = [argv_unicode[i] for i in range(0, argc.value)] - finally: - LocalFree(argv_unicode) - del argv_unicode + argv = [argv_unicode[i] for i in range(0, argc.value)] - 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:] @@ -335,30 +287,15 @@ def _get_windows_argv(): } -def _is_console(f): - if not hasattr(f, "fileno"): - return False - - try: - fileno = f.fileno() - except OSError: - return False - - handle = msvcrt.get_osfhandle(fileno) - return bool(GetConsoleMode(handle, byref(DWORD()))) - - 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 \ + hasattr(f, 'isatty') and f.isatty(): 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/third_party/python/click/click/core.py b/third_party/python/Click/click/core.py similarity index 80% rename from third_party/python/click/click/core.py rename to third_party/python/Click/click/core.py index f58bf26d2f988..7a1e3422bec8c 100644 --- a/third_party/python/click/click/core.py +++ b/third_party/python/Click/click/core.py @@ -3,51 +3,37 @@ import os import sys from contextlib import contextmanager -from functools import update_wrapper from itertools import repeat +from functools import update_wrapper + +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]...]..." -DEPRECATED_HELP_NOTICE = " (DEPRECATED)" -DEPRECATED_INVOKE_NOTICE = "DeprecationWarning: The command %(name)s is deprecated." +SUBCOMMAND_METAVAR = 'COMMAND [ARGS]...' +SUBCOMMANDS_METAVAR = 'COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...' + +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): @@ -62,13 +48,12 @@ 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 = "_{}_COMPLETE".format(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,28 +62,19 @@ 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( - "{}. 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, - 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): @@ -106,26 +82,25 @@ 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( - "Parameter callbacks take 3 args, (ctx, param, value). The" - " 2-arg style is deprecated and will be removed in 8.0.".format(callback), - DeprecationWarning, - 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) @contextmanager def augment_usage_errors(ctx, param=None): - """Context manager that attaches extra information to exceptions.""" + """Context manager that attaches extra information to exceptions that + fly. + """ try: yield except BadParameter as e: @@ -145,12 +120,11 @@ 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) @@ -180,9 +154,6 @@ class Context(object): Added the `color`, `ignore_unknown_options`, and `max_content_width` parameters. - .. versionadded:: 7.1 - Added the `show_default` parameter. - :param command: the command class for this context. :param parent: the parent context. :param info_name: the info name for this invocation. Generally this @@ -237,30 +208,15 @@ class Context(object): codes are used in texts that Click prints which is by default not the case. This for instance would affect help output. - :param show_default: if True, shows defaults for all options. - Even if an option is later created with show_default=False, - 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): #: the parent context or `None` if none exists. self.parent = parent #: the :class:`Command` for this context. @@ -281,14 +237,12 @@ def __init__( 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 @@ -347,7 +301,7 @@ def __init__( 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 @@ -368,18 +322,13 @@ def __init__( # 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 = "{}_{}".format( - 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: - auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") self.auto_envvar_prefix = auto_envvar_prefix if color is None and parent is not None: @@ -388,8 +337,6 @@ def __init__( #: Controls if styling output is wanted or not. self.color = color - self.show_default = show_default - self._close_callbacks = [] self._depth = 0 @@ -457,7 +404,7 @@ def meta(self): Example usage:: - LANG_KEY = f'{__name__}.lang' + LANG_KEY = __name__ + '.lang' def set_language(value): ctx = get_current_context() @@ -472,9 +419,8 @@ 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 @@ -500,11 +446,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 = "{} {}".format(self.parent.command_path, rv) + rv = self.parent.command_path + ' ' + rv return rv.lstrip() def find_root(self): @@ -569,7 +515,7 @@ def get_help(self): """ return self.command.get_help(self) - def invoke(*args, **kwargs): # noqa: B902 + def invoke(*args, **kwargs): """Invokes a command callback in exactly the way it expects. There are two ways to invoke this method: @@ -596,9 +542,8 @@ def invoke(*args, **kwargs): # noqa: B902 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: @@ -609,7 +554,7 @@ def invoke(*args, **kwargs): # noqa: B902 with ctx: return callback(*args, **kwargs) - def forward(*args, **kwargs): # noqa: B902 + def forward(*args, **kwargs): """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. @@ -619,7 +564,7 @@ def forward(*args, **kwargs): # noqa: B902 # 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: @@ -649,7 +594,6 @@ 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. @@ -668,14 +612,11 @@ def __init__(self, name, context_settings=None): #: an optional dictionary with defaults passed to the context. self.context_settings = context_settings - def __repr__(self): - return "<{} {}>".format(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 @@ -705,22 +646,17 @@ 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`` @@ -767,9 +703,8 @@ def main( args = list(args) if prog_name is None: - prog_name = make_str( - os.path.basename(sys.argv[0] if sys.argv else __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 @@ -821,7 +756,7 @@ def main( 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): @@ -836,8 +771,6 @@ class Command(BaseCommand): .. versionchanged:: 2.0 Added the `context_settings` parameter. - .. versionchanged:: 7.1 - Added the `no_args_is_help` parameter. :param name: the name of the command to use unless a group overrides it. :param context_settings: an optional dictionary with defaults that are @@ -852,31 +785,16 @@ class Command(BaseCommand): shown on the command listing of the parent command. :param add_help_option: by default each command registers a ``--help`` option. This can be disabled by this parameter. - :param no_args_is_help: this controls what happens if no arguments are - provided. This option is disabled by default. - If enabled this will add ``--help`` as argument - if no arguments are passed :param hidden: hide this command from help outputs. :param deprecated: issues a message indicating that 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, + 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. @@ -887,25 +805,20 @@ def __init__( 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 self.short_help = short_help self.add_help_option = add_help_option - self.no_args_is_help = no_args_is_help self.hidden = hidden self.deprecated = deprecated def get_usage(self, ctx): - """Formats the usage line into a string and returns it. - - Calls :meth:`format_usage` internally. - """ 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 @@ -915,12 +828,9 @@ def get_params(self, ctx): return rv def format_usage(self, ctx, formatter): - """Writes the usage line into the formatter. - - This is a low-level method called by :meth:`get_usage`. - """ + """Writes the usage line into the formatter.""" 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 @@ -949,15 +859,10 @@ 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.""" @@ -967,31 +872,21 @@ def make_parser(self, ctx): return parser def get_help(self, ctx): - """Formats the help into a string and returns it. - - Calls :meth:`format_help` internally. + """Formats the help into a string and returns it. This creates a + formatter and will call into the following formatting methods: """ 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 "" - ) + """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 '' def format_help(self, ctx, formatter): """Writes the help into the formatter if it exists. - This is a low-level method called by :meth:`get_help`. - - This calls the following methods: + This calls into the following methods: - :meth:`format_usage` - :meth:`format_help_text` @@ -1026,7 +921,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): @@ -1037,22 +932,17 @@ def format_epilog(self, ctx, formatter): formatter.write_text(self.epilog) def parse_args(self, ctx, args): - if not args and self.no_args_is_help and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - 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{} ({})".format( - "s" if len(args) != 1 else "", " ".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 @@ -1089,20 +979,12 @@ 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 @@ -1122,10 +1004,8 @@ def __init__( 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) @@ -1161,19 +1041,16 @@ 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): @@ -1201,7 +1078,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): @@ -1221,7 +1098,8 @@ 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: @@ -1237,7 +1115,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 @@ -1264,7 +1142,7 @@ def _process_result(value): # set to ``*`` to inform the command that subcommands are executed # but nothing else. with ctx: - ctx.invoked_subcommand = "*" if args else None + ctx.invoked_subcommand = args and '*' or None Command.invoke(self, ctx) # Otherwise we make every single context and invoke them in a @@ -1273,13 +1151,9 @@ 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, [] @@ -1311,7 +1185,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 '{}'.".format(original_cmd_name)) + ctx.fail('No such command "%s".' % original_cmd_name) return cmd_name, cmd, args[1:] @@ -1346,7 +1220,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 @@ -1356,13 +1230,10 @@ 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) self.add_command(cmd) return cmd - return decorator def group(self, *args, **kwargs): @@ -1371,13 +1242,10 @@ 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) self.add_command(cmd) return cmd - return decorator def get_command(self, ctx, cmd_name): @@ -1426,6 +1294,12 @@ class Parameter(object): Some settings are supported by both options and arguments. + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. In Click 2.0, the old callback format will still work, + but it will raise a warning to give you change to migrate the + code easier. + :param param_decls: the parameter declarations for this option or argument. This is a list of flags or argument names. @@ -1438,7 +1312,8 @@ class Parameter(object): without any arguments. :param callback: a callback that should be executed after the parameter was matched. This is called as ``fn(ctx, param, - value)`` and needs to return the value. + value)`` and needs to return the value. Before Click + 2.0, the signature was ``(ctx, value)``. :param nargs: the number of arguments to match. If not ``1`` the return value is a tuple instead of single value. The default for nargs is ``1`` (except if the type is a tuple, then it's @@ -1452,36 +1327,15 @@ class Parameter(object): order of processing. :param envvar: a string or list of strings that are environment variables that should be checked. - - .. versionchanged:: 7.1 - Empty environment variables are ignored rather than taking the - empty string value. This makes it possible for scripts to clear - variables if they can't unset them. - - .. versionchanged:: 2.0 - Changed signature for parameter callback to also be passed the - 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) @@ -1504,9 +1358,6 @@ def __init__( self.envvar = envvar self.autocompletion = autocompletion - def __repr__(self): - return "<{} {}>".format(self.__class__.__name__, self.name) - @property def human_readable_name(self): """Returns the human readable name of this parameter. This is the @@ -1521,7 +1372,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): @@ -1551,11 +1402,10 @@ 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 {}. This is not supported; nargs" - " needs to be set to a fixed value > 1.".format(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) @@ -1564,7 +1414,6 @@ 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): @@ -1605,10 +1454,7 @@ def resolve_envvar_value(self, ctx): if rv is not None: return rv else: - rv = os.environ.get(self.envvar) - - if rv != "": - return rv + return os.environ.get(self.envvar) def value_from_envvar(self, ctx): rv = self.resolve_envvar_value(ctx) @@ -1627,7 +1473,8 @@ 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 @@ -1647,7 +1494,7 @@ def get_error_hint(self, ctx): indicate which param caused the error. """ hint_list = self.opts or [self.human_readable_name] - return " / ".join(repr(x) for x in hint_list) + return ' / '.join('"%s"' % x for x in hint_list) class Option(Parameter): @@ -1688,33 +1535,19 @@ 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: @@ -1736,7 +1569,8 @@ def __init__( 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 is None: self.type = BOOL self.is_bool_flag = True else: @@ -1760,22 +1594,22 @@ def __init__( # 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 = [] @@ -1786,10 +1620,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 = ";" if decl[:1] == "/" else "/" + split_char = decl[:1] == '/' and ';' or '/' if split_char in decl: first, second = decl.split(split_char, 1) first = first.rstrip() @@ -1805,51 +1639,49 @@ 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 ({}). Did you" - " mean to declare an argument instead of an option?".format(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) - action_const = "{}_const".format(action) + 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): @@ -1862,50 +1694,46 @@ def _write_opts(opts): if any_slashes: any_prefix_is_slash[:] = [True] if not self.is_flag and not self.count: - rv += " {}".format(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 = "{}_{}".format(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: {}".format( - ", ".join(str(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: 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(str(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 = "{}[{}]".format( - "{} ".format(help) if help else "", "; ".join(extra) - ) + help = '%s[%s]' % (help and help + ' ' or '', '; '.join(extra)) - return ("; " if any_prefix_is_slash else " / ").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 + # If we're a non boolean flag out default is more complex because # we need to look at all flags in the same group to figure out # if we're the the default one in which case we return the flag # value as default. @@ -1930,22 +1758,18 @@ 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 = "{}_{}".format(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): @@ -1960,7 +1784,8 @@ 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) @@ -1972,20 +1797,18 @@ 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): @@ -2000,31 +1823,34 @@ def make_metavar(self): if not var: var = self.name.upper() if not self.required: - var = "[{}]".format(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" - " {}".format(len(decls)) - ) + raise TypeError('Arguments take exactly one ' + 'parameter declaration, got %d' % len(decls)) return name, [arg], [] def get_usage_pieces(self, ctx): return [self.make_metavar()] def get_error_hint(self, ctx): - return repr(self.make_metavar()) + 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 +from .decorators import command, group diff --git a/third_party/python/click/click/decorators.py b/third_party/python/Click/click/decorators.py similarity index 75% rename from third_party/python/click/click/decorators.py rename to third_party/python/Click/click/decorators.py index c7b5af6cc57fd..c57c5308613ec 100644 --- a/third_party/python/click/click/decorators.py +++ b/third_party/python/Click/click/decorators.py @@ -1,25 +1,20 @@ -import inspect import sys +import inspect + from functools import update_wrapper 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 +from .globals import get_current_context 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) @@ -28,10 +23,8 @@ 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) @@ -57,7 +50,6 @@ 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() @@ -66,41 +58,35 @@ 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 '{}' existing".format(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): @@ -108,9 +94,9 @@ def command(name=None, cls=None, **attrs): callback. This will also automatically attach all decorated :func:`option`\s and :func:`argument`\s as parameters to the command. - The name of the command defaults to the name of the function with - underscores replaced by dashes. If you want to change that, you can - pass the intended name as the first argument. + The name of the command defaults to the name of the function. If you + want to change that, you can pass the intended name as the first + argument. All keyword arguments are forwarded to the underlying command class. @@ -125,12 +111,10 @@ 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 @@ -139,7 +123,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) @@ -147,7 +131,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) @@ -162,12 +146,10 @@ 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 @@ -181,17 +163,15 @@ 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 @@ -212,19 +192,16 @@ 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 @@ -240,13 +217,11 @@ 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 @@ -263,14 +238,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: @@ -286,23 +261,25 @@ def callback(ctx, param, value): pass else: for dist in pkg_resources.working_set: - scripts = dist.get_entry_map().get("console_scripts") or {} - for _, entry_point in iteritems(scripts): + 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 @@ -316,18 +293,19 @@ 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) + return decorator - 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 +# Circular dependencies between core and decorators +from .core import Command, Group, Argument, Option diff --git a/third_party/python/click/click/exceptions.py b/third_party/python/Click/click/exceptions.py similarity index 74% rename from third_party/python/click/click/exceptions.py rename to third_party/python/Click/click/exceptions.py index 592ee38f0dec5..6fa17658cb20c 100644 --- a/third_party/python/click/click/exceptions.py +++ b/third_party/python/Click/click/exceptions.py @@ -1,12 +1,10 @@ -from ._compat import filename_to_ui -from ._compat import get_text_stderr -from ._compat import PY2 +from ._compat import PY2, filename_to_ui, get_text_stderr from .utils import echo def _join_param_hints(param_hint): if isinstance(param_hint, (tuple, list)): - return " / ".join(repr(x) for x in param_hint) + return ' / '.join('"%s"' % x for x in param_hint) return param_hint @@ -20,7 +18,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 +32,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: {}".format(self.format_message()), file=file) + echo('Error: %s' % self.format_message(), file=file) class UsageError(ClickException): @@ -50,27 +48,26 @@ 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): ClickException.__init__(self, message) self.ctx = ctx - self.cmd = self.ctx.command if self.ctx else None + self.cmd = self.ctx and self.ctx.command or None 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 '{} {}' for help.\n".format( - 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("{}\n{}".format(self.ctx.get_usage(), hint), file=file, color=color) - echo("Error: {}".format(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): @@ -91,7 +88,8 @@ 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 +100,10 @@ def format_message(self): elif self.param is not None: param_hint = self.param.get_error_hint(self.ctx) else: - return "Invalid value: {}".format(self.message) + return 'Invalid value: %s' % self.message param_hint = _join_param_hints(param_hint) - return "Invalid value for {}: {}".format(param_hint, self.message) + return 'Invalid value for %s: %s' % (param_hint, self.message) class MissingParameter(BadParameter): @@ -120,9 +118,8 @@ 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 @@ -144,30 +141,17 @@ def format_message(self): msg_extra = self.param.type.get_missing_message(self.param) if msg_extra: if msg: - msg += ". {}".format(msg_extra) + msg += '. ' + msg_extra else: msg = msg_extra - return "Missing {}{}{}{}".format( + return 'Missing %s%s%s%s' % ( param_type, - " {}".format(param_hint) if param_hint else "", - ". " if msg else ".", - msg or "", + param_hint and ' %s' % param_hint or '', + msg and '. ' or '.', + msg or '', ) - def __str__(self): - if self.message is None: - param_name = self.param.name if self.param else None - return "missing parameter: {}".format(param_name) - else: - return self.message - - if PY2: - __unicode__ = __str__ - - def __str__(self): - return self.__unicode__().encode("utf-8") - class NoSuchOption(UsageError): """Raised if click attempted to handle an option that does not @@ -176,9 +160,10 @@ 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: {}".format(option_name) + message = 'no such option: %s' % option_name UsageError.__init__(self, message, ctx) self.option_name = option_name self.possibilities = possibilities @@ -187,11 +172,11 @@ def format_message(self): bits = [self.message] if self.possibilities: if len(self.possibilities) == 1: - bits.append("Did you mean {}?".format(self.possibilities[0])) + bits.append('Did you mean %s?' % self.possibilities[0]) else: possibilities = sorted(self.possibilities) - bits.append("(Possible options: {})".format(", ".join(possibilities))) - return " ".join(bits) + bits.append('(Possible options: %s)' % ', '.join(possibilities)) + return ' '.join(bits) class BadOptionUsage(UsageError): @@ -227,13 +212,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 {}: {}".format(self.ui_filename, self.message) + return 'Could not open file %s: %s' % (self.ui_filename, self.message) class Abort(RuntimeError): @@ -246,8 +231,5 @@ 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/third_party/python/click/click/formatting.py b/third_party/python/Click/click/formatting.py similarity index 70% rename from third_party/python/click/click/formatting.py rename to third_party/python/Click/click/formatting.py index 319c7f6163e26..a3d6a4d389019 100644 --- a/third_party/python/click/click/formatting.py +++ b/third_party/python/Click/click/formatting.py @@ -1,8 +1,8 @@ from contextlib import contextmanager - -from ._compat import term_len -from .parser import split_opt from .termui import get_terminal_size +from .parser import split_opt +from ._compat import term_len + # Can force a width. This is used by the test system FORCED_WIDTH = None @@ -19,12 +19,11 @@ 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 @@ -44,14 +43,10 @@ def wrap_text( 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) @@ -62,10 +57,10 @@ def wrap_text( 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(): @@ -82,13 +77,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): @@ -127,65 +122,53 @@ 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 = "{:>{w}}{} ".format(prefix, prog, w=self.current_indent) + 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("{:>{w}}{}:\n".format("", heading, w=self.current_indent)) + 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 @@ -199,40 +182,30 @@ 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("{:>{w}}{}".format("", first, w=self.current_indent)) + 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() - + lines = iter(wrap_text(second, text_width).splitlines()) if lines: - self.write("{}\n".format(lines[0])) - - for line in lines[1:]: - self.write( - "{:>{w}}{}\n".format( - "", line, w=first_col + self.current_indent - ) - ) - - if len(lines) > 1: - # separate long help from next option - self.write("\n") + self.write(next(lines) + '\n') + for line in lines: + self.write('%*s%s\n' % ( + first_col + self.current_indent, '', line)) else: - self.write("\n") + self.write('\n') @contextmanager def section(self, name): @@ -260,7 +233,7 @@ def indentation(self): def getvalue(self): """Returns the buffer contents.""" - return "".join(self.buffer) + return ''.join(self.buffer) def join_options(options): @@ -273,11 +246,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/third_party/python/click/click/globals.py b/third_party/python/Click/click/globals.py similarity index 84% rename from third_party/python/click/click/globals.py rename to third_party/python/Click/click/globals.py index 1649f9a0bfbe6..843b594abe40b 100644 --- a/third_party/python/click/click/globals.py +++ b/third_party/python/Click/click/globals.py @@ -1,5 +1,6 @@ from threading import local + _local = local() @@ -14,20 +15,20 @@ def get_current_context(silent=False): .. versionadded:: 5.0 - :param silent: if set to `True` the return value is `None` if no context + :param silent: is set to `True` the return value is `None` if no context is available. The default behavior is to raise a :exc:`RuntimeError`. """ try: - return _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/third_party/python/click/click/parser.py b/third_party/python/Click/click/parser.py similarity index 85% rename from third_party/python/click/click/parser.py rename to third_party/python/Click/click/parser.py index f43ebfe9fc095..1c3ae9c8efd45 100644 --- a/third_party/python/click/click/parser.py +++ b/third_party/python/Click/click/parser.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- """ +click.parser +~~~~~~~~~~~~ + This module started out as largely a copy paste from the stdlib's optparse module with the features removed that we do not need from optparse because we implement them in Click on a higher level (for @@ -11,20 +14,12 @@ is that there are differences in 2.x and 3.x about the error messages generated and optparse in the stdlib uses gettext for no good reason and might cause us issues. - -Click uses parts of optparse written by Gregory P. Ward and maintained -by the Python Software Foundation. This is limited to code in parser.py. - -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 BadArgumentUsage -from .exceptions import BadOptionUsage -from .exceptions import NoSuchOption -from .exceptions import UsageError +from .exceptions import UsageError, NoSuchOption, BadOptionUsage, \ + BadArgumentUsage def _unpack_args(args, nargs_spec): @@ -64,7 +59,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 +68,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, "{} option requires an argument".format(opt)) - raise BadOptionUsage(opt, "{} option requires {} arguments".format(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,14 +98,13 @@ 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"('([^'\\]*(?:\\.[^'\\]*)*)'|\"([^\"\\]*(?:\\.[^\"\\]*)*)\"|\S+)\s*", - string, - re.S, - ): + for match in re.finditer(r"('([^'\\]*(?:\\.[^'\\]*)*)'" + r'|"([^"\\]*(?:\\.[^"\\]*)*)"' + 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: @@ -120,6 +114,7 @@ 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,7 +123,8 @@ 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 ({})".format(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) @@ -137,7 +133,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 @@ -147,25 +143,26 @@ 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 '{}'".format(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 @@ -177,14 +174,14 @@ def process(self, value, state): if holes == len(value): value = None elif holes != 0: - raise BadArgumentUsage( - "argument {} takes {} values".format(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 = [] @@ -225,10 +222,11 @@ def __init__(self, ctx=None): self.ignore_unknown_options = ctx.ignore_unknown_options self._short_opt = {} self._long_opt = {} - self._opt_prefixes = {"-", "--"} + 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``, @@ -240,7 +238,8 @@ def add_option(self, opts, dest, action=None, nargs=1, const=None, obj=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 @@ -274,9 +273,8 @@ 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) @@ -290,7 +288,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) @@ -322,7 +320,8 @@ 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] @@ -344,7 +343,7 @@ def _match_long_opt(self, opt, explicit_value, state): del state.rargs[:nargs] elif explicit_value is not None: - raise BadOptionUsage(opt, "{} option does not take a value".format(opt)) + raise BadOptionUsage(opt, '%s option does not take a value' % opt) else: value = None @@ -396,15 +395,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("{}{}".format(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/third_party/python/click/click/termui.py b/third_party/python/Click/click/termui.py similarity index 80% rename from third_party/python/click/click/termui.py rename to third_party/python/Click/click/termui.py index 02ef9e9f045cd..bf9a3aa163f92 100644 --- a/third_party/python/click/click/termui.py +++ b/third_party/python/Click/click/termui.py @@ -1,89 +1,60 @@ -import inspect -import io -import itertools import os -import struct import sys +import struct +import inspect +import itertools -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 ._compat import raw_input, text_type, string_types, \ + isatty, strip_ansi, get_winterm_size, DEFAULT_COLUMNS, WIN from .utils import echo -from .utils import LazyFile +from .exceptions import Abort, UsageError +from .types import convert_type, Choice, Path +from .globals import resolve_color_default + # The prompt functions to use. The doc tools currently override these # functions to customize how they work. 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 += " ({})".format(", ".join(map(str, type.choices))) + prompt += ' (' + ", ".join(map(str, type.choices)) + ')' if default is not None and show_default: - prompt = "{} [{}]".format(prompt, _format_default(default)) + prompt = '%s [%s]' % (prompt, default) return prompt + suffix -def _format_default(default): - if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): - return default.name - - 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. @@ -121,12 +92,12 @@ def prompt( result = None def prompt_func(text): - f = hidden_prompt_func if hide_input else visible_prompt_func + f = hide_input and hidden_prompt_func or visible_prompt_func try: # 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). @@ -138,9 +109,7 @@ 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: @@ -156,22 +125,21 @@ def prompt_func(text): try: result = value_proc(value) except UsageError as e: - echo("Error: {}".format(e.message), err=err) # noqa: B306 + 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 @@ -189,25 +157,24 @@ def confirm( :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, "Y/n" if default else "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: @@ -222,8 +189,7 @@ 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 @@ -241,8 +207,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 @@ -258,7 +224,8 @@ 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]) @@ -284,29 +251,18 @@ 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 @@ -316,17 +272,11 @@ def progressbar( will not be rendered if the file is not a terminal. The context manager creates the progress bar. When the context - manager is entered the progress bar is already created. With every + manager is entered the progress bar is already displayed. With every iteration over the progress bar, the iterable passed to the bar is advanced and the bar is updated. When the context manager exits, a newline is printed and the progress bar is finalized on screen. - Note: The progress bar is currently designed for use cases where the - total progress can be expected to take at least several seconds. - Because of this, the ProgressBar class object won't display - progress that is considered too fast, and progress where the time - between steps is less than a second. - No printing must happen or the progress bar will be unintentionally destroyed. @@ -392,24 +342,13 @@ def progressbar( 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(): @@ -425,22 +364,13 @@ 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 @@ -495,28 +425,28 @@ def style( bits = [] if fg: try: - bits.append("\033[{}m".format(_ansi_colors[fg])) + bits.append('\033[%dm' % (_ansi_colors[fg])) except KeyError: - raise TypeError("Unknown color '{}'".format(fg)) + raise TypeError('Unknown color %r' % fg) if bg: try: - bits.append("\033[{}m".format(_ansi_colors[bg] + 10)) + bits.append('\033[%dm' % (_ansi_colors[bg] + 10)) except KeyError: - raise TypeError("Unknown color '{}'".format(bg)) + raise TypeError('Unknown color %r' % bg) if bold is not None: - bits.append("\033[{}m".format(1 if bold else 22)) + bits.append('\033[%dm' % (1 if bold else 22)) if dim is not None: - bits.append("\033[{}m".format(2 if dim else 22)) + bits.append('\033[%dm' % (2 if dim else 22)) if underline is not None: - bits.append("\033[{}m".format(4 if underline else 24)) + bits.append('\033[%dm' % (4 if underline else 24)) if blink is not None: - bits.append("\033[{}m".format(5 if blink else 25)) + bits.append('\033[%dm' % (5 if blink else 25)) if reverse is not None: - bits.append("\033[{}m".format(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): @@ -548,9 +478,8 @@ 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 @@ -579,10 +508,8 @@ def edit( 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) @@ -611,7 +538,6 @@ def launch(url, wait=False, locate=False): the filesystem. """ from ._termui_impl import open_url - return open_url(url, wait=wait, locate=locate) @@ -648,11 +574,10 @@ 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/third_party/python/click/click/testing.py b/third_party/python/Click/click/testing.py similarity index 77% rename from third_party/python/click/click/testing.py rename to third_party/python/Click/click/testing.py index a3dba3b3014b6..1b2924e0b1399 100644 --- a/third_party/python/click/click/testing.py +++ b/third_party/python/Click/click/testing.py @@ -1,16 +1,18 @@ -import contextlib import os -import shlex -import shutil import sys +import shutil import tempfile +import contextlib +import shlex + +from ._compat import iteritems, PY2, string_types + -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: @@ -21,6 +23,7 @@ class EchoingStdin(object): + def __init__(self, input, output): self._input = input self._output = output @@ -50,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: @@ -70,14 +73,13 @@ 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. self.stdout_bytes = stdout_bytes - #: The standard error as bytes, or None if not available + #: The standard error as bytes, or False(y) if not available self.stderr_bytes = stderr_bytes #: The exit code as integer. self.exit_code = exit_code @@ -94,22 +96,22 @@ 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: + if not self.stderr_bytes: 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 "<{} {}>".format( - type(self).__name__, repr(self.exception) if self.exception else "okay" + return '<%s %s>' % ( + type(self).__name__, + self.exception and repr(self.exception) or 'okay', ) @@ -134,9 +136,10 @@ 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 @@ -147,7 +150,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.""" @@ -179,8 +182,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 = formatting.FORCED_WIDTH - formatting.FORCED_WIDTH = 80 + old_forced_width = clickpkg.formatting.FORCED_WIDTH + clickpkg.formatting.FORCED_WIDTH = 80 env = self.make_env(env) @@ -197,10 +200,12 @@ 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 @@ -208,16 +213,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("{}\n".format(val)) + 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("{}\n".format(prompt or "")) + 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) @@ -233,14 +238,14 @@ def should_strip_ansi(stream=None, color=None): return not default_color return not color - 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_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_env = {} try: @@ -266,22 +271,14 @@ def should_strip_ansi(stream=None, color=None): sys.stdout = old_stdout sys.stderr = old_stderr sys.stdin = old_stdin - 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, - cli, - args=None, - input=None, - env=None, - catch_exceptions=True, - color=False, - **extra - ): + 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 + + def invoke(self, cli, args=None, input=None, env=None, + catch_exceptions=True, color=False, mix_stderr=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 @@ -338,7 +335,7 @@ def invoke( 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: @@ -350,19 +347,14 @@ def invoke( finally: sys.stdout.flush() stdout = outstreams[0].getvalue() - if self.mix_stderr: - stderr = 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, - ) + stderr = outstreams[1] and outstreams[1].getvalue() + + 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): @@ -378,5 +370,5 @@ def isolated_filesystem(self): os.chdir(cwd) try: shutil.rmtree(t) - except (OSError, IOError): # noqa: B014 + except (OSError, IOError): pass diff --git a/third_party/python/click/click/types.py b/third_party/python/Click/click/types.py similarity index 69% rename from third_party/python/click/click/types.py rename to third_party/python/Click/click/types.py index 505c39f850922..1f88032f5485d 100644 --- a/third_party/python/click/click/types.py +++ b/third_party/python/Click/click/types.py @@ -2,16 +2,10 @@ import stat from datetime import datetime -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 ._compat import open_stream, text_type, filename_to_ui, \ + get_filesystem_encoding, get_streerror, _get_argv_encoding, PY2 from .exceptions import BadParameter -from .utils import LazyFile -from .utils import safecall +from .utils import safecall, LazyFile class ParamType(object): @@ -27,7 +21,6 @@ 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 @@ -69,7 +62,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.""" @@ -85,6 +78,7 @@ def arity(self): class FuncParamType(ParamType): + def __init__(self, func): self.name = func.__name__ self.func = func @@ -96,22 +90,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 +118,12 @@ def convert(self, value, param, ctx): try: value = value.decode(fs_enc) except UnicodeError: - 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): @@ -141,68 +133,54 @@ class Choice(ParamType): You should only pass a list or tuple of choices. Other iterables (like generators) may lead to surprising results. - The resulting value will always be one of the originally passed choices - regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` - being specified. - See :ref:`choice-opts` for an example. :param case_sensitive: Set to false to make choices case 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 "[{}]".format("|".join(self.choices)) + return '[%s]' % '|'.join(self.choices) def get_missing_message(self, param): - return "Choose from:\n\t{}.".format(",\n\t".join(self.choices)) + return 'Choose from:\n\t%s.' % ',\n\t'.join(self.choices) def convert(self, value, param, ctx): + # Exact match + if value in self.choices: + return value + # Match through normalization and case sensitivity # first do token_normalize_func, then lowercase # preserve original `value` to produce an accurate message in # `self.fail` normed_value = value - normed_choices = {choice: choice for choice in self.choices} + normed_choices = self.choices - if ctx is not None and ctx.token_normalize_func is not None: + if ctx is not None and \ + ctx.token_normalize_func is not None: normed_value = ctx.token_normalize_func(value) - normed_choices = { - ctx.token_normalize_func(normed_choice): original - for normed_choice, original in normed_choices.items() - } + normed_choices = [ctx.token_normalize_func(choice) for choice in + self.choices] if not self.case_sensitive: - if PY2: - lower = str.lower - else: - lower = str.casefold - - normed_value = lower(normed_value) - normed_choices = { - lower(normed_choice): original - for normed_choice, original in normed_choices.items() - } + normed_value = normed_value.lower() + normed_choices = [choice.lower() for choice in normed_choices] if normed_value in normed_choices: - return normed_choices[normed_value] + return normed_value - self.fail( - "invalid choice: {}. (choose from {})".format( - 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('{}')".format(list(self.choices)) + return 'Choice(%r)' % list(self.choices) class DateTime(ParamType): @@ -225,14 +203,17 @@ 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: @@ -248,26 +229,24 @@ 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: - self.fail("{} is not a valid integer".format(value), param, ctx) + except (ValueError, UnicodeError): + self.fail('%s is not a valid integer' % value, param, ctx) def __repr__(self): - return "INT" + return 'INT' class IntRange(IntParamType): @@ -278,8 +257,7 @@ 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 @@ -293,55 +271,35 @@ 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( - "{} is bigger than the maximum valid value {}.".format( - 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( - "{} is smaller than the minimum valid value {}.".format( - rv, self.min - ), - param, - ctx, - ) + self.fail('%s is smaller than the minimum valid value ' + '%s.' % (rv, self.min), param, ctx) else: - self.fail( - "{} is not in the valid range of {} to {}.".format( - 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({}, {})".format(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 ValueError: - self.fail( - "{} is not a valid floating point value".format(value), param, ctx - ) + except (UnicodeError, ValueError): + self.fail('%s is not a valid floating point value' % + value, param, ctx) def __repr__(self): - return "FLOAT" + return 'FLOAT' class FloatRange(FloatParamType): @@ -352,8 +310,7 @@ 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 @@ -367,74 +324,54 @@ 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( - "{} is bigger than the maximum valid value {}.".format( - 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( - "{} is smaller than the minimum valid value {}.".format( - rv, self.min - ), - param, - ctx, - ) + self.fail('%s is smaller than the minimum valid value ' + '%s.' % (rv, self.min), param, ctx) else: - self.fail( - "{} is not in the valid range of {} to {}.".format( - 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({}, {})".format(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("{} is not a valid boolean".format(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 ValueError: - self.fail("{} is not a valid UUID value".format(value), param, ctx) + except (UnicodeError, ValueError): + self.fail('%s is not a valid UUID value' % value, param, ctx) def __repr__(self): - return "UUID" + return 'UUID' class File(ParamType): @@ -463,13 +400,11 @@ 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 @@ -479,30 +414,29 @@ def __init__( 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 @@ -514,14 +448,11 @@ def convert(self, value, param, ctx): else: ctx.call_on_close(safecall(f.flush)) return f - except (IOError, OSError) as e: # noqa: B014 - self.fail( - "Could not open file: {}: {}".format( - filename_to_ui(value), get_streerror(e) - ), - param, - ctx, - ) + except (IOError, OSError) as e: + self.fail('Could not open file: %s: %s' % ( + filename_to_ui(value), + get_streerror(e), + ), param, ctx) class Path(ParamType): @@ -554,20 +485,11 @@ 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 @@ -578,14 +500,14 @@ def __init__( 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): @@ -598,7 +520,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: @@ -609,44 +531,31 @@ def convert(self, value, param, ctx): except OSError: if not self.exists: return self.coerce_path_result(rv) - self.fail( - "{} '{}' does not exist.".format( - 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( - "{} '{}' is a file.".format(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( - "{} '{}' is a directory.".format( - 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( - "{} '{}' is not writable.".format( - 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( - "{} '{}' is not readable.".format( - 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) @@ -670,7 +579,7 @@ def __init__(self, types): @property def name(self): - return "<{}>".format(" ".join(ty.name for ty in self.types)) + return "<" + " ".join(ty.name for ty in self.types) + ">" @property def arity(self): @@ -678,16 +587,14 @@ 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)) def convert_type(ty, default=None): - """Converts a callable or python type into the most appropriate - param type. + """Converts a callable or python ty into the most appropriate param + ty. """ guessed_type = False if ty is None and default is not None: @@ -720,9 +627,8 @@ def convert_type(ty, default=None): if __debug__: try: if issubclass(ty, ParamType): - raise AssertionError( - "Attempted to use an uninstantiated parameter type ({}).".format(ty) - ) + raise AssertionError('Attempted to use an uninstantiated ' + 'parameter type (%s).' % ty) except TypeError: pass return FuncParamType(ty) diff --git a/third_party/python/click/click/utils.py b/third_party/python/Click/click/utils.py similarity index 85% rename from third_party/python/click/click/utils.py rename to third_party/python/Click/click/utils.py index 79265e732d49a..fc84369fc9540 100644 --- a/third_party/python/click/click/utils.py +++ b/third_party/python/Click/click/utils.py @@ -1,47 +1,34 @@ 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 - from ._winconsole import _hash_py_argv - from ._winconsole import _initial_argv_hash + from ._winconsole import _get_windows_argv, \ + _hash_py_argv, _initial_argv_hash + echo_native_types = string_types + (bytes, bytearray) 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 @@ -51,7 +38,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) @@ -63,21 +50,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 = 1 + len(word) if result else len(word) + 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): @@ -87,19 +74,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. @@ -113,7 +100,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 '' % (self.name, self.mode) def open(self): """Opens the file if it's not yet open. This call might fail with @@ -123,12 +110,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 - ) - except (IOError, OSError) as e: # noqa: E402 + 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 @@ -157,6 +144,7 @@ def __iter__(self): class KeepOpenFile(object): + def __init__(self, file): self._file = file @@ -234,11 +222,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 @@ -285,11 +273,11 @@ def get_binary_stream(name): """ opener = binary_streams.get(name) if opener is None: - raise TypeError("Unknown standard stream '{}'".format(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 @@ -302,13 +290,12 @@ def get_text_stream(name, encoding=None, errors="strict"): """ opener = text_streams.get(name) if opener is None: - raise TypeError("Unknown standard stream '{}'".format(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. @@ -333,7 +320,8 @@ def open_file( """ 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 @@ -413,21 +401,19 @@ def get_app_dir(app_name, roaming=True, force_posix=False): application support folder. """ if WIN: - key = "APPDATA" if roaming else "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("~/.{}".format(_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): @@ -447,7 +433,6 @@ def flush(self): self.wrapped.flush() except IOError as e: import errno - if e.errno != errno.EPIPE: raise diff --git a/third_party/python/click/click-7.1.2.dist-info/LICENSE.rst b/third_party/python/click/click-7.1.2.dist-info/LICENSE.rst deleted file mode 100644 index d12a849186982..0000000000000 --- a/third_party/python/click/click-7.1.2.dist-info/LICENSE.rst +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2014 Pallets - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/python/click/click-7.1.2.dist-info/RECORD b/third_party/python/click/click-7.1.2.dist-info/RECORD deleted file mode 100644 index 847406774ca85..0000000000000 --- a/third_party/python/click/click-7.1.2.dist-info/RECORD +++ /dev/null @@ -1,22 +0,0 @@ -click/__init__.py,sha256=FkyGDQ-cbiQxP_lxgUspyFYS48f2S_pTcfKPz-d_RMo,2463 -click/_bashcomplete.py,sha256=9J98IHQYmCAr2Jup6TDshUr5FJEen-AoQCZR0K5nKxQ,12309 -click/_compat.py,sha256=AoMaYnZ-3pwtNXuHtlb6_UXsayoG0QZiHKIRy2VFezc,24169 -click/_termui_impl.py,sha256=yNktUMAdjYOU1HMkq915jR3zgAzUNtGSQqSTSSMn3eQ,20702 -click/_textwrap.py,sha256=ajCzkzFly5tjm9foQ5N9_MOeaYJMBjAltuFa69n4iXY,1197 -click/_unicodefun.py,sha256=apLSNEBZgUsQNPMUv072zJ1swqnm0dYVT5TqcIWTt6w,4201 -click/_winconsole.py,sha256=6YDu6Rq1Wxx4w9uinBMK2LHvP83aerZM9GQurlk3QDo,10010 -click/core.py,sha256=V6DJzastGhrC6WTDwV9MSLwcJUdX2Uf1ypmgkjBdn_Y,77650 -click/decorators.py,sha256=3TvEO_BkaHl7k6Eh1G5eC7JK4LKPdpFqH9JP0QDyTlM,11215 -click/exceptions.py,sha256=3pQAyyMFzx5A3eV0Y27WtDTyGogZRbrC6_o5DjjKBbw,8118 -click/formatting.py,sha256=Wb4gqFEpWaKPgAbOvnkCl8p-bEZx5KpM5ZSByhlnJNk,9281 -click/globals.py,sha256=ht7u2kUGI08pAarB4e4yC8Lkkxy6gJfRZyzxEj8EbWQ,1501 -click/parser.py,sha256=mFK-k58JtPpqO0AC36WAr0t5UfzEw1mvgVSyn7WCe9M,15691 -click/termui.py,sha256=G7QBEKIepRIGLvNdGwBTYiEtSImRxvTO_AglVpyHH2s,23998 -click/testing.py,sha256=EUEsDUqNXFgCLhZ0ZFOROpaVDA5I_rijwnNPE6qICgA,12854 -click/types.py,sha256=wuubik4VqgqAw5dvbYFkDt-zSAx97y9TQXuXcVaRyQA,25045 -click/utils.py,sha256=4VEcJ7iEHwjnFuzEuRtkT99o5VG3zqSD7Q2CVzv13WU,15940 -click-7.1.2.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 -click-7.1.2.dist-info/METADATA,sha256=LrRgakZKV7Yg3qJqX_plu2WhFW81MzP3EqQmZhHIO8M,2868 -click-7.1.2.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 -click-7.1.2.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 -click-7.1.2.dist-info/RECORD,, diff --git a/third_party/python/click/click/__init__.py b/third_party/python/click/click/__init__.py deleted file mode 100644 index 2b6008f2dd417..0000000000000 --- a/third_party/python/click/click/__init__.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -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. -""" -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 -from .parser import 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.2" diff --git a/third_party/python/click/click/_unicodefun.py b/third_party/python/click/click/_unicodefun.py deleted file mode 100644 index 781c365227263..0000000000000 --- a/third_party/python/click/click/_unicodefun.py +++ /dev/null @@ -1,131 +0,0 @@ -import codecs -import os -import sys - -from ._compat import PY2 - - -def _find_unicode_literals_frame(): - import __future__ - - 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."): - frm = frm.f_back - idx += 1 - elif frm.f_code.co_flags & __future__.unicode_literals.compiler_flag: - return idx - else: - break - return 0 - - -def _check_for_unicode_literals(): - if not __debug__: - return - - 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: - 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/python3/" - ), - stacklevel=bad_frame, - ) - - -def _verify_python3_env(): - """Ensures that the environment is good for unicode on Python 3.""" - if PY2: - return - try: - import locale - - fs_enc = codecs.lookup(locale.getpreferredencoding()).name - except Exception: - fs_enc = "ascii" - if fs_enc != "ascii": - return - - extra = "" - if os.name == "posix": - import subprocess - - try: - rv = subprocess.Popen( - ["locale", "-a"], stdout=subprocess.PIPE, stderr=subprocess.PIPE - ).communicate()[0] - except OSError: - 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") - - for line in rv.splitlines(): - locale = line.strip() - if locale.lower().endswith((".utf-8", ".utf8")): - good_locales.add(locale) - if locale.lower() in ("c.utf8", "c.utf-8"): - has_c_utf8 = True - - extra += "\n\n" - if not good_locales: - extra += ( - "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. 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 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"): - 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" - " 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/python3/ for" - " mitigation steps.{}".format(extra) - ) diff --git a/third_party/python/pathspec/pathspec-0.9.0.dist-info/LICENSE b/third_party/python/pathspec/pathspec-0.8.0.dist-info/LICENSE similarity index 100% rename from third_party/python/pathspec/pathspec-0.9.0.dist-info/LICENSE rename to third_party/python/pathspec/pathspec-0.8.0.dist-info/LICENSE diff --git a/third_party/python/pathspec/pathspec-0.9.0.dist-info/METADATA b/third_party/python/pathspec/pathspec-0.8.0.dist-info/METADATA similarity index 90% rename from third_party/python/pathspec/pathspec-0.9.0.dist-info/METADATA rename to third_party/python/pathspec/pathspec-0.8.0.dist-info/METADATA index 2d38736204901..bf80c8d746171 100644 --- a/third_party/python/pathspec/pathspec-0.9.0.dist-info/METADATA +++ b/third_party/python/pathspec/pathspec-0.8.0.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pathspec -Version: 0.9.0 +Version: 0.8.0 Summary: Utility library for gitignore style pattern matching of file paths. Home-page: https://github.com/cpburnz/python-path-specification Author: Caleb P. Burns @@ -19,14 +19,11 @@ Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Utilities -Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7 -Description-Content-Type: text/x-rst - +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* *pathspec*: Path Specification ============================== @@ -181,46 +178,21 @@ Other Languages .. _`Ruby gem`: https://github.com/highb/pathspec-ruby - Change History ============== -0.9.0 (2021-07-17) ------------------- - -- `Issue #44`_/`Issue #50`_: Raise `GitWildMatchPatternError` for invalid git patterns. -- `Issue #45`_: Fix for duplicate leading double-asterisk, and edge cases. -- `Issue #46`_: Fix matching absolute paths. -- API change: `util.normalize_files()` now returns a `Dict[str, List[pathlike]]` instead of a `Dict[str, pathlike]`. -- Added type hinting. - -.. _`Issue #44`: https://github.com/cpburnz/python-path-specification/issues/44 -.. _`Issue #45`: https://github.com/cpburnz/python-path-specification/pull/45 -.. _`Issue #46`: https://github.com/cpburnz/python-path-specification/issues/46 -.. _`Issue #50`: https://github.com/cpburnz/python-path-specification/pull/50 - - -0.8.1 (2020-11-07) ------------------- - -- `Issue #43`_: Add support for addition operator. - -.. _`Issue #43`: https://github.com/cpburnz/python-path-specification/pull/43 - 0.8.0 (2020-04-09) ------------------ - `Issue #30`_: Expose what patterns matched paths. Added `util.detailed_match_files()`. - `Issue #31`_: `match_tree()` doesn't return symlinks. -- `Issue #34`_: Support `pathlib.Path`\ s. - Add `PathSpec.match_tree_entries` and `util.iter_tree_entries()` to support directories and symlinks. - API change: `match_tree()` has been renamed to `match_tree_files()`. The old name `match_tree()` is still available as an alias. - API change: `match_tree_files()` now returns symlinks. This is a bug fix but it will change the returned results. .. _`Issue #30`: https://github.com/cpburnz/python-path-specification/issues/30 .. _`Issue #31`: https://github.com/cpburnz/python-path-specification/issues/31 -.. _`Issue #34`: https://github.com/cpburnz/python-path-specification/issues/34 0.7.0 (2019-12-27) @@ -408,4 +380,3 @@ Change History - Initial release. - diff --git a/third_party/python/pathspec/pathspec-0.8.0.dist-info/RECORD b/third_party/python/pathspec/pathspec-0.8.0.dist-info/RECORD new file mode 100644 index 0000000000000..b87d4adc25384 --- /dev/null +++ b/third_party/python/pathspec/pathspec-0.8.0.dist-info/RECORD @@ -0,0 +1,16 @@ +pathspec/__init__.py,sha256=Ey5z-0FuHJfuIxu6l_9l_sK5ZGtn0l_jR68H1zlzxbo,2164 +pathspec/compat.py,sha256=OZJedLvgxHbuKKn5Rnia_UDcyDZfnqCQerLoIvSR_Qc,777 +pathspec/pathspec.py,sha256=IEkQSqyelGvOVpX2k9SZ58qw7sZPIxmRM7D4k-X-xAw,6519 +pathspec/pattern.py,sha256=p-ibgRLK9kIkiXWoCtq4SangTZEBUaNIO0hgKQY4KEI,4405 +pathspec/util.py,sha256=smB9T1UuQ7ZBvZZvdtRBr1C0W6BssfeFkNA-4c-cXRY,17929 +pathspec/patterns/__init__.py,sha256=Falv9rzI0S-Sjc-t-vCS9nUPcKwBptmdNderY9Kok50,184 +pathspec/patterns/gitwildmatch.py,sha256=cJfGcaj9rL9YhVhItJv-xuwYsveraCXFVQm_Xg_iMTE,9947 +pathspec/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pathspec/tests/test_gitwildmatch.py,sha256=JFRyR2JJ7ihb1r3JUmqnPx5pKrl2Q2yDuPPOZC1jr7w,12786 +pathspec/tests/test_pathspec.py,sha256=_hBnTNUY5R_eL0t_qmXp5V98HAnSE1rS9CHkYzfVCEA,2948 +pathspec/tests/test_util.py,sha256=IjvDbKYyEnQOaa07ebx-qD4bb-YFSm3qm0PE-VhKUa0,7906 +pathspec-0.8.0.dist-info/LICENSE,sha256=-rPda9qyJvHAhjCx3ZF-Efy07F4eAg4sFvg6ChOGPoU,16726 +pathspec-0.8.0.dist-info/METADATA,sha256=gx9xmpi3hW5hUjkqumL6nUEqb4WV2vR61_DVGdyHdco,11058 +pathspec-0.8.0.dist-info/WHEEL,sha256=8zNYZbwQSXoB9IfXOjPfeNwvAsALAjffgk27FqvCWbo,110 +pathspec-0.8.0.dist-info/top_level.txt,sha256=0NA6IbW6iCUZ4p402IRSyZpvx4yqoltHzMoAxVQHI1M,9 +pathspec-0.8.0.dist-info/RECORD,, diff --git a/third_party/python/pathspec/pathspec-0.9.0.dist-info/WHEEL b/third_party/python/pathspec/pathspec-0.8.0.dist-info/WHEEL similarity index 100% rename from third_party/python/pathspec/pathspec-0.9.0.dist-info/WHEEL rename to third_party/python/pathspec/pathspec-0.8.0.dist-info/WHEEL diff --git a/third_party/python/pathspec/pathspec-0.9.0.dist-info/top_level.txt b/third_party/python/pathspec/pathspec-0.8.0.dist-info/top_level.txt similarity index 100% rename from third_party/python/pathspec/pathspec-0.9.0.dist-info/top_level.txt rename to third_party/python/pathspec/pathspec-0.8.0.dist-info/top_level.txt diff --git a/third_party/python/pathspec/pathspec-0.9.0.dist-info/RECORD b/third_party/python/pathspec/pathspec-0.9.0.dist-info/RECORD deleted file mode 100644 index 328aa58483d1b..0000000000000 --- a/third_party/python/pathspec/pathspec-0.9.0.dist-info/RECORD +++ /dev/null @@ -1,17 +0,0 @@ -pathspec/__init__.py,sha256=72Wc9H_-xRaisgbnz3pNf8bJJlFu6ZP3MMEYVvlIowk,1085 -pathspec/_meta.py,sha256=MyOTCY28N1FOIxhPQBNxXei9oiQEvzVISrwnyCIAb-0,1617 -pathspec/compat.py,sha256=G0_QT3NRlFTcrV1bhx9G1qIvBdmnaWh-f4VrcpXzfhc,830 -pathspec/pathspec.py,sha256=J2lrhvT1iqkZ5rSrTvMEOjuLBkdzl2QVZkJYWIoMVmw,8048 -pathspec/pattern.py,sha256=sWBuHJNnDKa6tqpKU50ZeCUYXSkKz2dWPLtSHpIyP5U,4811 -pathspec/util.py,sha256=XayuxOvE0LaTZDyffhpel-1ew1_MP_FTCeqXj0M3C8I,19889 -pathspec/patterns/__init__.py,sha256=Falv9rzI0S-Sjc-t-vCS9nUPcKwBptmdNderY9Kok50,184 -pathspec/patterns/gitwildmatch.py,sha256=NzrN7IqFvJJT3x7jcXIMZBmeWF6n0nDjUMoPWpGmvZs,11899 -pathspec/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pathspec/tests/test_gitwildmatch.py,sha256=m9a5-dJ1-MXZ-SVzci3JqtHShfEP3ty-kaYMkVGWS9U,15160 -pathspec/tests/test_pathspec.py,sha256=UB_aETIpTxpuei79zI2_kbjI2XnM2tF_xTQOFD4IYL8,4845 -pathspec/tests/test_util.py,sha256=IjvDbKYyEnQOaa07ebx-qD4bb-YFSm3qm0PE-VhKUa0,7906 -pathspec-0.9.0.dist-info/LICENSE,sha256=-rPda9qyJvHAhjCx3ZF-Efy07F4eAg4sFvg6ChOGPoU,16726 -pathspec-0.9.0.dist-info/METADATA,sha256=nmGOFs7gDO9imfQOjDP2Mhu-NX1xK2DoDv4B7D37D80,12141 -pathspec-0.9.0.dist-info/WHEEL,sha256=8zNYZbwQSXoB9IfXOjPfeNwvAsALAjffgk27FqvCWbo,110 -pathspec-0.9.0.dist-info/top_level.txt,sha256=0NA6IbW6iCUZ4p402IRSyZpvx4yqoltHzMoAxVQHI1M,9 -pathspec-0.9.0.dist-info/RECORD,, diff --git a/third_party/python/pathspec/pathspec/__init__.py b/third_party/python/pathspec/pathspec/__init__.py index 790d0a33bb717..2400402197042 100644 --- a/third_party/python/pathspec/pathspec/__init__.py +++ b/third_party/python/pathspec/pathspec/__init__.py @@ -23,18 +23,43 @@ """ from __future__ import unicode_literals +__author__ = "Caleb P. Burns" +__copyright__ = "Copyright © 2013-2020 Caleb P. Burns" +__created__ = "2013-10-12" +__credits__ = [ + "dahlia ", + "highb ", + "029xue ", + "mikexstudios ", + "nhumrich ", + "davidfraser ", + "demurgos ", + "ghickman ", + "nvie ", + "adrienverge ", + "AndersBlomdell ", + "highb ", + "thmxv ", + "wimglenn ", + "hugovk ", + "dcecile ", + "mroutis ", + "jdufresne ", + "groodt ", + "ftrofin ", + "pykong ", +] +__email__ = "cpburnz@gmail.com" +__license__ = "MPL 2.0" +__project__ = "pathspec" +__status__ = "Development" +__updated__ = "2020-04-09" +__version__ = "0.8.0" + from .pathspec import PathSpec from .pattern import Pattern, RegexPattern from .util import iter_tree, lookup_pattern, match_files, RecursionError -from ._meta import ( - __author__, - __copyright__, - __credits__, - __license__, - __version__, -) - # Load pattern implementations. from . import patterns diff --git a/third_party/python/pathspec/pathspec/_meta.py b/third_party/python/pathspec/pathspec/_meta.py deleted file mode 100644 index ff4c3239a976e..0000000000000 --- a/third_party/python/pathspec/pathspec/_meta.py +++ /dev/null @@ -1,43 +0,0 @@ -# encoding: utf-8 -""" -This module contains the project meta-data. -""" - -__author__ = "Caleb P. Burns" -__copyright__ = "Copyright © 2013-2021 Caleb P. Burns" -__credits__ = [ - "dahlia ", - "highb ", - "029xue ", - "mikexstudios ", - "nhumrich ", - "davidfraser ", - "demurgos ", - "ghickman ", - "nvie ", - "adrienverge ", - "AndersBlomdell ", - "highb ", - "thmxv ", - "wimglenn ", - "hugovk ", - "dcecile ", - "mroutis ", - "jdufresne ", - "groodt ", - "ftrofin ", - "pykong ", - "nhhollander ", - "KOLANICH ", - "JonjonHays ", - "Isaac0616 ", - "SebastiaanZ ", - "RoelAdriaans ", - "raviselker ", - "johanvergeer ", - "danjer ", - "jhbuhrman ", - "WPDOrdina ", -] -__license__ = "MPL 2.0" -__version__ = "0.9.0" diff --git a/third_party/python/pathspec/pathspec/compat.py b/third_party/python/pathspec/pathspec/compat.py index f5d17bf3ced78..37c6480510f49 100644 --- a/third_party/python/pathspec/pathspec/compat.py +++ b/third_party/python/pathspec/pathspec/compat.py @@ -36,6 +36,3 @@ def iterkeys(mapping): except ImportError: # Python 2.7 - 3.5. from collections import Container as Collection - -CollectionType = Collection -IterableType = Iterable diff --git a/third_party/python/pathspec/pathspec/pathspec.py b/third_party/python/pathspec/pathspec/pathspec.py index ff40089d2c58a..be058ffc87d8a 100644 --- a/third_party/python/pathspec/pathspec/pathspec.py +++ b/third_party/python/pathspec/pathspec/pathspec.py @@ -4,34 +4,8 @@ of files. """ -try: - from typing import ( - Any, - AnyStr, - Callable, - Iterable, - Iterator, - Optional, - Text, - Union) -except ImportError: - pass - -try: - # Python 3.6+ type hints. - from os import PathLike - from typing import Collection -except ImportError: - pass - from . import util -from .compat import ( - CollectionType, - iterkeys, - izip_longest, - string_types) -from .pattern import Pattern -from .util import TreeEntry +from .compat import Collection, iterkeys, izip_longest, string_types, unicode class PathSpec(object): @@ -41,7 +15,6 @@ class PathSpec(object): """ def __init__(self, patterns): - # type: (Iterable[Pattern]) -> None """ Initializes the :class:`PathSpec` instance. @@ -49,14 +22,13 @@ def __init__(self, patterns): yields each compiled pattern (:class:`.Pattern`). """ - self.patterns = patterns if isinstance(patterns, CollectionType) else list(patterns) + self.patterns = patterns if isinstance(patterns, Collection) else list(patterns) """ *patterns* (:class:`~collections.abc.Collection` of :class:`.Pattern`) contains the compiled patterns. """ def __eq__(self, other): - # type: (PathSpec) -> bool """ Tests the equality of this path-spec with *other* (:class:`PathSpec`) by comparing their :attr:`~PathSpec.patterns` attributes. @@ -74,32 +46,8 @@ def __len__(self): """ return len(self.patterns) - def __add__(self, other): - # type: (PathSpec) -> PathSpec - """ - Combines the :attr:`Pathspec.patterns` patterns from two - :class:`PathSpec` instances. - """ - if isinstance(other, PathSpec): - return PathSpec(self.patterns + other.patterns) - else: - return NotImplemented - - def __iadd__(self, other): - # type: (PathSpec) -> PathSpec - """ - Adds the :attr:`Pathspec.patterns` patterns from one :class:`PathSpec` - instance to this instance. - """ - if isinstance(other, PathSpec): - self.patterns += other.patterns - return self - else: - return NotImplemented - @classmethod def from_lines(cls, pattern_factory, lines): - # type: (Union[Text, Callable[[AnyStr], Pattern]], Iterable[AnyStr]) -> PathSpec """ Compiles the pattern lines. @@ -123,11 +71,10 @@ def from_lines(cls, pattern_factory, lines): if not util._is_iterable(lines): raise TypeError("lines:{!r} is not an iterable.".format(lines)) - patterns = [pattern_factory(line) for line in lines if line] - return cls(patterns) + lines = [pattern_factory(line) for line in lines if line] + return cls(lines) def match_file(self, file, separators=None): - # type: (Union[Text, PathLike], Optional[Collection[Text]]) -> bool """ Matches the file to this path-spec. @@ -144,7 +91,6 @@ def match_file(self, file, separators=None): return util.match_file(self.patterns, norm_file) def match_entries(self, entries, separators=None): - # type: (Iterable[TreeEntry], Optional[Collection[Text]]) -> Iterator[TreeEntry] """ Matches the entries to this path-spec. @@ -156,7 +102,7 @@ def match_entries(self, entries, separators=None): normalize. See :func:`~pathspec.util.normalize_file` for more information. - Returns the matched entries (:class:`~collections.abc.Iterator` of + Returns the matched entries (:class:`~collections.abc.Iterable` of :class:`~util.TreeEntry`). """ if not util._is_iterable(entries): @@ -168,7 +114,6 @@ def match_entries(self, entries, separators=None): yield entry_map[path] def match_files(self, files, separators=None): - # type: (Iterable[Union[Text, PathLike]], Optional[Collection[Text]]) -> Iterator[Union[Text, PathLike]] """ Matches the files to this path-spec. @@ -181,20 +126,18 @@ def match_files(self, files, separators=None): normalize. See :func:`~pathspec.util.normalize_file` for more information. - Returns the matched files (:class:`~collections.abc.Iterator` of - :class:`str` or :class:`pathlib.PurePath`). + Returns the matched files (:class:`~collections.abc.Iterable` of + :class:`str`). """ if not util._is_iterable(files): raise TypeError("files:{!r} is not an iterable.".format(files)) file_map = util.normalize_files(files, separators=separators) matched_files = util.match_files(self.patterns, iterkeys(file_map)) - for norm_file in matched_files: - for orig_file in file_map[norm_file]: - yield orig_file + for path in matched_files: + yield file_map[path] def match_tree_entries(self, root, on_error=None, follow_links=None): - # type: (Text, Optional[Callable], Optional[bool]) -> Iterator[TreeEntry] """ Walks the specified root path for all files and matches them to this path-spec. @@ -210,14 +153,13 @@ def match_tree_entries(self, root, on_error=None, follow_links=None): to walk symbolic links that resolve to directories. See :func:`~pathspec.util.iter_tree_files` for more information. - Returns the matched files (:class:`~collections.abc.Iterator` of - :class:`.TreeEntry`). + Returns the matched files (:class:`~collections.abc.Iterable` of + :class:`str`). """ entries = util.iter_tree_entries(root, on_error=on_error, follow_links=follow_links) return self.match_entries(entries) def match_tree_files(self, root, on_error=None, follow_links=None): - # type: (Text, Optional[Callable], Optional[bool]) -> Iterator[Text] """ Walks the specified root path for all files and matches them to this path-spec. diff --git a/third_party/python/pathspec/pathspec/pattern.py b/third_party/python/pathspec/pathspec/pattern.py index c354c2632a55e..4ba4edf790c84 100644 --- a/third_party/python/pathspec/pathspec/pattern.py +++ b/third_party/python/pathspec/pathspec/pattern.py @@ -4,18 +4,6 @@ """ import re -try: - from typing import ( - AnyStr, - Iterable, - Iterator, - Optional, - Pattern as RegexHint, - Text, - Tuple, - Union) -except ImportError: - pass from .compat import unicode @@ -29,7 +17,6 @@ class Pattern(object): __slots__ = ('include',) def __init__(self, include): - # type: (Optional[bool]) -> None """ Initializes the :class:`Pattern` instance. @@ -46,7 +33,6 @@ def __init__(self, include): """ def match(self, files): - # type: (Iterable[Text]) -> Iterator[Text] """ Matches this pattern against the specified files. @@ -69,7 +55,6 @@ class RegexPattern(Pattern): __slots__ = ('regex',) def __init__(self, pattern, include=None): - # type: (Union[AnyStr, RegexHint], Optional[bool]) -> None """ Initializes the :class:`RegexPattern` instance. @@ -118,7 +103,6 @@ def __init__(self, pattern, include=None): self.regex = regex def __eq__(self, other): - # type: (RegexPattern) -> bool """ Tests the equality of this regex pattern with *other* (:class:`RegexPattern`) by comparing their :attr:`~Pattern.include` and :attr:`~RegexPattern.regex` @@ -130,7 +114,6 @@ def __eq__(self, other): return NotImplemented def match(self, files): - # type: (Iterable[Text]) -> Iterable[Text] """ Matches this pattern against the specified files. @@ -147,7 +130,6 @@ def match(self, files): @classmethod def pattern_to_regex(cls, pattern): - # type: (Text) -> Tuple[Text, bool] """ Convert the pattern into an uncompiled regular expression. diff --git a/third_party/python/pathspec/pathspec/patterns/gitwildmatch.py b/third_party/python/pathspec/pathspec/patterns/gitwildmatch.py index afd8f6b4cfea5..07fd03880a9ad 100644 --- a/third_party/python/pathspec/pathspec/patterns/gitwildmatch.py +++ b/third_party/python/pathspec/pathspec/patterns/gitwildmatch.py @@ -8,14 +8,6 @@ import re import warnings -try: - from typing import ( - AnyStr, - Optional, - Text, - Tuple) -except ImportError: - pass from .. import util from ..compat import unicode @@ -25,14 +17,6 @@ _BYTES_ENCODING = 'latin1' -class GitWildMatchPatternError(ValueError): - """ - The :class:`GitWildMatchPatternError` indicates an invalid git wild match - pattern. - """ - pass - - class GitWildMatchPattern(RegexPattern): """ The :class:`GitWildMatchPattern` class represents a compiled Git @@ -44,7 +28,6 @@ class GitWildMatchPattern(RegexPattern): @classmethod def pattern_to_regex(cls, pattern): - # type: (AnyStr) -> Tuple[Optional[AnyStr], Optional[bool]] """ Convert the pattern into a regular expression. @@ -64,7 +47,6 @@ def pattern_to_regex(cls, pattern): else: raise TypeError("pattern:{!r} is not a unicode or byte string.".format(pattern)) - original_pattern = pattern pattern = pattern.strip() if pattern.startswith('#'): @@ -81,6 +63,7 @@ def pattern_to_regex(cls, pattern): include = None elif pattern: + if pattern.startswith('!'): # A pattern starting with an exclamation mark ('!') negates the # pattern (exclude instead of include). Escape the exclamation @@ -97,31 +80,11 @@ def pattern_to_regex(cls, pattern): # exclamation mark ('!'). pattern = pattern[1:] - # Allow a regex override for edge cases that cannot be handled - # through normalization. - override_regex = None - # Split pattern into segments. pattern_segs = pattern.split('/') # Normalize pattern to make processing easier. - # EDGE CASE: Deal with duplicate double-asterisk sequences. - # Collapse each sequence down to one double-asterisk. Iterate over - # the segments in reverse and remove the duplicate double - # asterisks as we go. - for i in range(len(pattern_segs) - 1, 0, -1): - prev = pattern_segs[i-1] - seg = pattern_segs[i] - if prev == '**' and seg == '**': - del pattern_segs[i] - - if len(pattern_segs) == 2 and pattern_segs[0] == '**' and not pattern_segs[1]: - # EDGE CASE: The '**/' pattern should match everything except - # individual files in the root directory. This case cannot be - # adequately handled through normalization. Use the override. - override_regex = '^.+/.*$' - if not pattern_segs[0]: # A pattern beginning with a slash ('/') will only match paths # directly on the root directory instead of any descendant @@ -146,75 +109,58 @@ def pattern_to_regex(cls, pattern): # according to `git check-ignore` (v2.4.1). pass - if not pattern_segs: - # After resolving the edge cases, we end up with no - # pattern at all. This must be because the pattern is - # invalid. - raise GitWildMatchPatternError("Invalid git pattern: %r" % (original_pattern,)) - if not pattern_segs[-1] and len(pattern_segs) > 1: - # A pattern ending with a slash ('/') will match all - # descendant paths if it is a directory but not if it is a - # regular file. This is equivalent to "{pattern}/**". So, set - # last segment to a double-asterisk to include all - # descendants. + # A pattern ending with a slash ('/') will match all descendant + # paths if it is a directory but not if it is a regular file. + # This is equivilent to "{pattern}/**". So, set last segment to + # double asterisks to include all descendants. pattern_segs[-1] = '**' - if override_regex is None: - # Build regular expression from pattern. - output = ['^'] - need_slash = False - end = len(pattern_segs) - 1 - for i, seg in enumerate(pattern_segs): - if seg == '**': - if i == 0 and i == end: - # A pattern consisting solely of double-asterisks ('**') - # will match every path. - output.append('.+') - elif i == 0: - # A normalized pattern beginning with double-asterisks - # ('**') will match any leading path segments. - output.append('(?:.+/)?') - need_slash = False - elif i == end: - # A normalized pattern ending with double-asterisks ('**') - # will match any trailing path segments. - output.append('/.*') - else: - # A pattern with inner double-asterisks ('**') will match - # multiple (or zero) inner path segments. - output.append('(?:/.+)?') - need_slash = True - - elif seg == '*': - # Match single path segment. - if need_slash: - output.append('/') - output.append('[^/]+') - need_slash = True - + # Build regular expression from pattern. + output = ['^'] + need_slash = False + end = len(pattern_segs) - 1 + for i, seg in enumerate(pattern_segs): + if seg == '**': + if i == 0 and i == end: + # A pattern consisting solely of double-asterisks ('**') + # will match every path. + output.append('.+') + elif i == 0: + # A normalized pattern beginning with double-asterisks + # ('**') will match any leading path segments. + output.append('(?:.+/)?') + need_slash = False + elif i == end: + # A normalized pattern ending with double-asterisks ('**') + # will match any trailing path segments. + output.append('/.*') else: - # Match segment glob pattern. - if need_slash: - output.append('/') - - output.append(cls._translate_segment_glob(seg)) - if i == end and include is True: - # A pattern ending without a slash ('/') will match a file - # or a directory (with paths underneath it). E.g., "foo" - # matches "foo", "foo/bar", "foo/bar/baz", etc. - # EDGE CASE: However, this does not hold for exclusion cases - # according to `git check-ignore` (v2.4.1). - output.append('(?:/.*)?') - + # A pattern with inner double-asterisks ('**') will match + # multiple (or zero) inner path segments. + output.append('(?:/.+)?') need_slash = True - - output.append('$') - regex = ''.join(output) - - else: - # Use regex override. - regex = override_regex + elif seg == '*': + # Match single path segment. + if need_slash: + output.append('/') + output.append('[^/]+') + need_slash = True + else: + # Match segment glob pattern. + if need_slash: + output.append('/') + output.append(cls._translate_segment_glob(seg)) + if i == end and include is True: + # A pattern ending without a slash ('/') will match a file + # or a directory (with paths underneath it). E.g., "foo" + # matches "foo", "foo/bar", "foo/bar/baz", etc. + # EDGE CASE: However, this does not hold for exclusion cases + # according to `git check-ignore` (v2.4.1). + output.append('(?:/.*)?') + need_slash = True + output.append('$') + regex = ''.join(output) else: # A blank pattern is a null-operation (neither includes nor @@ -229,7 +175,6 @@ def pattern_to_regex(cls, pattern): @staticmethod def _translate_segment_glob(pattern): - # type: (Text) -> Text """ Translates the glob pattern to a regular expression. This is used in the constructor to translate a path segment glob pattern to its @@ -270,28 +215,28 @@ def _translate_segment_glob(pattern): regex += '[^/]' elif char == '[': - # Bracket expression wildcard. Except for the beginning - # exclamation mark, the whole bracket expression can be used + # Braket expression wildcard. Except for the beginning + # exclamation mark, the whole braket expression can be used # directly as regex but we have to find where the expression # ends. - # - "[][!]" matches ']', '[' and '!'. - # - "[]-]" matches ']' and '-'. - # - "[!]a-]" matches any character except ']', 'a' and '-'. + # - "[][!]" matchs ']', '[' and '!'. + # - "[]-]" matchs ']' and '-'. + # - "[!]a-]" matchs any character except ']', 'a' and '-'. j = i # Pass brack expression negation. if j < end and pattern[j] == '!': j += 1 - # Pass first closing bracket if it is at the beginning of the + # Pass first closing braket if it is at the beginning of the # expression. if j < end and pattern[j] == ']': j += 1 - # Find closing bracket. Stop once we reach the end or find it. + # Find closing braket. Stop once we reach the end or find it. while j < end and pattern[j] != ']': j += 1 if j < end: - # Found end of bracket expression. Increment j to be one past - # the closing bracket: + # Found end of braket expression. Increment j to be one past + # the closing braket: # # [...] # ^ ^ @@ -305,7 +250,7 @@ def _translate_segment_glob(pattern): expr += '^' i += 1 elif pattern[i] == '^': - # POSIX declares that the regex bracket expression negation + # POSIX declares that the regex braket expression negation # "[^...]" is undefined in a glob pattern. Python's # `fnmatch.translate()` escapes the caret ('^') as a # literal. To maintain consistency with undefined behavior, @@ -313,19 +258,19 @@ def _translate_segment_glob(pattern): expr += '\\^' i += 1 - # Build regex bracket expression. Escape slashes so they are + # Build regex braket expression. Escape slashes so they are # treated as literal slashes by regex as defined by POSIX. expr += pattern[i:j].replace('\\', '\\\\') - # Add regex bracket expression to regex result. + # Add regex braket expression to regex result. regex += expr - # Set i to one past the closing bracket. + # Set i to one past the closing braket. i = j else: - # Failed to find closing bracket, treat opening bracket as a - # bracket literal instead of as an expression. + # Failed to find closing braket, treat opening braket as a + # braket literal instead of as an expression. regex += '\\[' else: @@ -336,33 +281,18 @@ def _translate_segment_glob(pattern): @staticmethod def escape(s): - # type: (AnyStr) -> AnyStr """ Escape special characters in the given string. *s* (:class:`unicode` or :class:`bytes`) a filename or a string that you want to escape, usually before adding it to a `.gitignore` - Returns the escaped string (:class:`unicode` or :class:`bytes`) + Returns the escaped string (:class:`unicode`, :class:`bytes`) """ - if isinstance(s, unicode): - return_type = unicode - string = s - elif isinstance(s, bytes): - return_type = bytes - string = s.decode(_BYTES_ENCODING) - else: - raise TypeError("s:{!r} is not a unicode or byte string.".format(s)) - # Reference: https://git-scm.com/docs/gitignore#_pattern_format meta_characters = r"[]!*#?" - out_string = "".join("\\" + x if x in meta_characters else x for x in string) - - if return_type is bytes: - return out_string.encode(_BYTES_ENCODING) - else: - return out_string + return "".join("\\" + x if x in meta_characters else x for x in s) util.register_pattern('gitwildmatch', GitWildMatchPattern) @@ -378,7 +308,7 @@ def __init__(self, *args, **kw): Warn about deprecation. """ self._deprecated() - super(GitIgnorePattern, self).__init__(*args, **kw) + return super(GitIgnorePattern, self).__init__(*args, **kw) @staticmethod def _deprecated(): diff --git a/third_party/python/pathspec/pathspec/util.py b/third_party/python/pathspec/pathspec/util.py index 64a5dea9db26a..bcba8783b61c9 100644 --- a/third_party/python/pathspec/pathspec/util.py +++ b/third_party/python/pathspec/pathspec/util.py @@ -7,35 +7,8 @@ import os.path import posixpath import stat -try: - from typing import ( - Any, - AnyStr, - Callable, - Dict, - Iterable, - Iterator, - List, - Optional, - Sequence, - Set, - Text, - Union) -except ImportError: - pass -try: - # Python 3.6+ type hints. - from os import PathLike - from typing import Collection -except ImportError: - pass - -from .compat import ( - CollectionType, - IterableType, - string_types, - unicode) -from .pattern import Pattern + +from .compat import Collection, Iterable, string_types, unicode NORMALIZE_PATH_SEPS = [sep for sep in [os.sep, os.altsep] if sep and sep != posixpath.sep] """ @@ -53,7 +26,6 @@ def detailed_match_files(patterns, files, all_matches=None): - # type: (Iterable[Pattern], Iterable[Text], Optional[bool]) -> Dict[Text, 'MatchDetail'] """ Matches the files to the patterns, and returns which patterns matched the files. @@ -71,7 +43,7 @@ def detailed_match_files(patterns, files, all_matches=None): Returns the matched files (:class:`dict`) which maps each matched file (:class:`str`) to the patterns that matched in order (:class:`.MatchDetail`). """ - all_files = files if isinstance(files, CollectionType) else list(files) + all_files = files if isinstance(files, Collection) else list(files) return_files = {} for pattern in patterns: if pattern.include is not None: @@ -96,7 +68,6 @@ def detailed_match_files(patterns, files, all_matches=None): def _is_iterable(value): - # type: (Any) -> bool """ Check whether the value is an iterable (excludes strings). @@ -104,11 +75,10 @@ def _is_iterable(value): Returns whether *value* is a iterable (:class:`bool`). """ - return isinstance(value, IterableType) and not isinstance(value, (unicode, bytes)) + return isinstance(value, Iterable) and not isinstance(value, (unicode, bytes)) def iter_tree_entries(root, on_error=None, follow_links=None): - # type: (Text, Optional[Callable], Optional[bool]) -> Iterator['TreeEntry'] """ Walks the specified directory for all files and directories. @@ -126,7 +96,7 @@ def iter_tree_entries(root, on_error=None, follow_links=None): Raises :exc:`RecursionError` if recursion is detected. - Returns an :class:`~collections.abc.Iterator` yielding each file or + Returns an :class:`~collections.abc.Iterable` yielding each file or directory entry (:class:`.TreeEntry`) relative to *root*. """ if on_error is not None and not callable(on_error): @@ -140,7 +110,6 @@ def iter_tree_entries(root, on_error=None, follow_links=None): def iter_tree_files(root, on_error=None, follow_links=None): - # type: (Text, Optional[Callable], Optional[bool]) -> Iterator[Text] """ Walks the specified directory for all files. @@ -158,7 +127,7 @@ def iter_tree_files(root, on_error=None, follow_links=None): Raises :exc:`RecursionError` if recursion is detected. - Returns an :class:`~collections.abc.Iterator` yielding the path to + Returns an :class:`~collections.abc.Iterable` yielding the path to each file (:class:`str`) relative to *root*. """ if on_error is not None and not callable(on_error): @@ -177,7 +146,6 @@ def iter_tree_files(root, on_error=None, follow_links=None): def _iter_tree_entries_next(root_full, dir_rel, memo, on_error, follow_links): - # type: (Text, Text, Dict[Text, Text], Callable, bool) -> Iterator['TreeEntry'] """ Scan the directory for all descendant files. @@ -255,7 +223,6 @@ def _iter_tree_entries_next(root_full, dir_rel, memo, on_error, follow_links): def lookup_pattern(name): - # type: (Text) -> Callable[[AnyStr], Pattern] """ Lookups a registered pattern factory by name. @@ -268,7 +235,6 @@ def lookup_pattern(name): def match_file(patterns, file): - # type: (Iterable[Pattern], Text) -> bool """ Matches the file to the patterns. @@ -289,7 +255,6 @@ def match_file(patterns, file): def match_files(patterns, files): - # type: (Iterable[Pattern], Iterable[Text]) -> Set[Text] """ Matches the files to the patterns. @@ -301,7 +266,7 @@ def match_files(patterns, files): Returns the matched files (:class:`set` of :class:`str`). """ - all_files = files if isinstance(files, CollectionType) else list(files) + all_files = files if isinstance(files, Collection) else list(files) return_files = set() for pattern in patterns: if pattern.include is not None: @@ -314,7 +279,6 @@ def match_files(patterns, files): def _normalize_entries(entries, separators=None): - # type: (Iterable['TreeEntry'], Optional[Collection[Text]]) -> Dict[Text, 'TreeEntry'] """ Normalizes the entry paths to use the POSIX path separator. @@ -335,10 +299,8 @@ def _normalize_entries(entries, separators=None): def normalize_file(file, separators=None): - # type: (Union[Text, PathLike], Optional[Collection[Text]]) -> Text """ - Normalizes the file path to use the POSIX path separator (i.e., - ``'/'``), and make the paths relative (remove leading ``'/'``). + Normalizes the file path to use the POSIX path separator (i.e., ``'/'``). *file* (:class:`str` or :class:`pathlib.PurePath`) is the file path. @@ -361,19 +323,14 @@ def normalize_file(file, separators=None): for sep in separators: norm_file = norm_file.replace(sep, posixpath.sep) - if norm_file.startswith('/'): - # Make path relative. - norm_file = norm_file[1:] - - elif norm_file.startswith('./'): - # Remove current directory prefix. + # Remove current directory prefix. + if norm_file.startswith('./'): norm_file = norm_file[2:] return norm_file def normalize_files(files, separators=None): - # type: (Iterable[Union[str, PathLike]], Optional[Collection[Text]]) -> Dict[Text, List[Union[str, PathLike]]] """ Normalizes the file paths to use the POSIX path separator. @@ -384,23 +341,16 @@ def normalize_files(files, separators=None): :data:`None`) optionally contains the path separators to normalize. See :func:`normalize_file` for more information. - Returns a :class:`dict` mapping the each normalized file path - (:class:`str`) to the original file paths (:class:`list` of - :class:`str` or :class:`pathlib.PurePath`). + Returns a :class:`dict` mapping the each normalized file path (:class:`str`) + to the original file path (:class:`str`) """ norm_files = {} for path in files: - norm_file = normalize_file(path, separators=separators) - if norm_file in norm_files: - norm_files[norm_file].append(path) - else: - norm_files[norm_file] = [path] - + norm_files[normalize_file(path, separators=separators)] = path return norm_files def register_pattern(name, pattern_factory, override=None): - # type: (Text, Callable[[AnyStr], Pattern], Optional[bool]) -> None """ Registers the specified pattern factory. @@ -432,7 +382,6 @@ class AlreadyRegisteredError(Exception): """ def __init__(self, name, pattern_factory): - # type: (Text, Callable[[AnyStr], Pattern]) -> None """ Initializes the :exc:`AlreadyRegisteredError` instance. @@ -445,7 +394,6 @@ def __init__(self, name, pattern_factory): @property def message(self): - # type: () -> Text """ *message* (:class:`str`) is the error message. """ @@ -456,7 +404,6 @@ def message(self): @property def name(self): - # type: () -> Text """ *name* (:class:`str`) is the name of the registered pattern. """ @@ -464,7 +411,6 @@ def name(self): @property def pattern_factory(self): - # type: () -> Callable[[AnyStr], Pattern] """ *pattern_factory* (:class:`~collections.abc.Callable`) is the registered pattern factory. @@ -479,7 +425,6 @@ class RecursionError(Exception): """ def __init__(self, real_path, first_path, second_path): - # type: (Text, Text, Text) -> None """ Initializes the :exc:`RecursionError` instance. @@ -496,7 +441,6 @@ def __init__(self, real_path, first_path, second_path): @property def first_path(self): - # type: () -> Text """ *first_path* (:class:`str`) is the first path encountered for :attr:`self.real_path `. @@ -505,7 +449,6 @@ def first_path(self): @property def message(self): - # type: () -> Text """ *message* (:class:`str`) is the error message. """ @@ -517,7 +460,6 @@ def message(self): @property def real_path(self): - # type: () -> Text """ *real_path* (:class:`str`) is the real path that recursion was encountered on. @@ -526,7 +468,6 @@ def real_path(self): @property def second_path(self): - # type: () -> Text """ *second_path* (:class:`str`) is the second path encountered for :attr:`self.real_path `. @@ -543,7 +484,6 @@ class MatchDetail(object): __slots__ = ('patterns',) def __init__(self, patterns): - # type: (Sequence[Pattern]) -> None """ Initialize the :class:`.MatchDetail` instance. @@ -570,7 +510,6 @@ class TreeEntry(object): __slots__ = ('_lstat', 'name', 'path', '_stat') def __init__(self, name, path, lstat, stat): - # type: (Text, Text, os.stat_result, os.stat_result) -> None """ Initialize the :class:`.TreeEntry` instance. @@ -608,7 +547,6 @@ def __init__(self, name, path, lstat, stat): """ def is_dir(self, follow_links=None): - # type: (Optional[bool]) -> bool """ Get whether the entry is a directory. @@ -625,7 +563,6 @@ def is_dir(self, follow_links=None): return stat.S_ISDIR(node_stat.st_mode) def is_file(self, follow_links=None): - # type: (Optional[bool]) -> bool """ Get whether the entry is a regular file. @@ -642,14 +579,12 @@ def is_file(self, follow_links=None): return stat.S_ISREG(node_stat.st_mode) def is_symlink(self): - # type: () -> bool """ Returns whether the entry is a symbolic link (:class:`bool`). """ return stat.S_ISLNK(self._lstat.st_mode) def stat(self, follow_links=None): - # type: (Optional[bool]) -> os.stat_result """ Get the cached stat result for the entry. diff --git a/third_party/python/requirements.in b/third_party/python/requirements.in index 5ef584fafc27e..0ed3005cf2ce4 100644 --- a/third_party/python/requirements.in +++ b/third_party/python/requirements.in @@ -20,7 +20,7 @@ jsmin==2.1.0 json-e==2.7.0 mozilla-version==0.3.4 packaging==21.0 -pathspec==0.9.0 +pathspec==0.8 pip==21.2.4 pip-tools==5.5.0 ply==3.10 diff --git a/third_party/python/requirements.txt b/third_party/python/requirements.txt index aefbb5f64d70c..468dfb711552c 100644 --- a/third_party/python/requirements.txt +++ b/third_party/python/requirements.txt @@ -80,9 +80,9 @@ chardet==4.0.0 \ # via # aiohttp # requests -click==7.1.2 \ - --hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a \ - --hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc +click==7.0 \ + --hash=sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13 \ + --hash=sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7 # via # glean-parser # pip-tools @@ -288,9 +288,9 @@ packaging==21.0 \ --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \ --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 # via -r requirements-mach-vendor-python.in -pathspec==0.9.0 \ - --hash=sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a \ - --hash=sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1 +pathspec==0.8 \ + --hash=sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0 \ + --hash=sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061 # via # -r requirements-mach-vendor-python.in # yamllint @@ -439,10 +439,10 @@ tqdm==4.62.3 \ --hash=sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c \ --hash=sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d # via -r requirements-mach-vendor-python.in -typing-extensions==3.10.0.0 \ - --hash=sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497 \ - --hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \ - --hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84 +typing-extensions==3.7.4.3 \ + --hash=sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918 \ + --hash=sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c \ + --hash=sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f # via # aiohttp # importlib-metadata diff --git a/third_party/python/tqdm/tqdm/completion.sh b/third_party/python/tqdm/tqdm/completion.sh old mode 100755 new mode 100644 diff --git a/third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/RECORD b/third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/RECORD deleted file mode 100644 index 217df4acdde90..0000000000000 --- a/third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/RECORD +++ /dev/null @@ -1,6 +0,0 @@ -typing_extensions.py,sha256=upcRc-ygmoZSgbJ4WZa34ZE_PVJsYrOlGM7WWbBrJuo,108429 -typing_extensions-3.10.0.0.dist-info/LICENSE,sha256=_xfOlOECAk3raHc-scx0ynbaTmWPNzUx8Kwi1oprsa0,12755 -typing_extensions-3.10.0.0.dist-info/METADATA,sha256=zjlcNCeUQUETPe37jftee4IwkGKxm8YPKQxFFOMgyqQ,2099 -typing_extensions-3.10.0.0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 -typing_extensions-3.10.0.0.dist-info/top_level.txt,sha256=hkDmk3VmrfXPOD--jS4aKTCu6kFZo-kVT1cIFfq1eU8,18 -typing_extensions-3.10.0.0.dist-info/RECORD,, diff --git a/third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/LICENSE b/third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/LICENSE similarity index 100% rename from third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/LICENSE rename to third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/LICENSE diff --git a/third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/METADATA b/third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/METADATA similarity index 66% rename from third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/METADATA rename to third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/METADATA index fa314015ef789..ee6d0e727ca03 100644 --- a/third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/METADATA +++ b/third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/METADATA @@ -1,9 +1,9 @@ Metadata-Version: 2.1 Name: typing-extensions -Version: 3.10.0.0 +Version: 3.7.4.3 Summary: Backported and Experimental Type Hints for Python 3.5+ Home-page: https://github.com/python/typing/blob/master/typing_extensions/README.rst -Author: Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee +Author: Guido van Rossum, Jukka Lehtosalo, Lukasz Langa, Michael Lee Author-email: levkivskyi@gmail.com License: PSF Keywords: typing function annotations type hints hinting checking checker typehints typehinting typechecking backport @@ -20,25 +20,23 @@ Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 Classifier: Topic :: Software Development Requires-Dist: typing (>=3.7.4) ; python_version < "3.5" Typing Extensions -- Backported and Experimental Type Hints for Python -The ``typing`` module was added to the standard library in Python 3.5, but -many new features have been added to the module since then. -This means users of Python 3.5 - 3.6 who are unable to upgrade will not be +The ``typing`` module was added to the standard library in Python 3.5 on +a provisional basis and will no longer be provisional in Python 3.7. However, +this means users of Python 3.5 - 3.6 who are unable to upgrade will not be able to take advantage of new types added to the ``typing`` module, such as -``typing.Protocol`` or ``typing.TypedDict``. +``typing.Text`` or ``typing.Coroutine``. -The ``typing_extensions`` module contains backports of these changes. -Experimental types that will eventually be added to the ``typing`` -module are also included in ``typing_extensions``, such as -``typing.ParamSpec`` and ``typing.TypeGuard``. +The ``typing_extensions`` module contains both backports of these changes +as well as experimental types that will eventually be added to the ``typing`` +module, such as ``Protocol`` or ``TypedDict``. -Users of Python versions before 3.5 should install and use -the ``typing`` module from PyPI instead of using this one, unless specifically +Users of other Python versions should continue to install and use +the ``typing`` module from PyPi instead of using this one unless specifically writing code that must be compatible with multiple Python versions or requires experimental types. diff --git a/third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/RECORD b/third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/RECORD new file mode 100644 index 0000000000000..0ec829548e9a0 --- /dev/null +++ b/third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/RECORD @@ -0,0 +1,6 @@ +typing_extensions.py,sha256=MX7Db0Dze7Y5_V7HHFajAbf8IPuvw2ZS07o_sfpBOE8,83727 +typing_extensions-3.7.4.3.dist-info/LICENSE,sha256=_xfOlOECAk3raHc-scx0ynbaTmWPNzUx8Kwi1oprsa0,12755 +typing_extensions-3.7.4.3.dist-info/METADATA,sha256=ZHHy0ceuecxgjj7eY65f-I_dN1m3u2BgGVbnc9lgg78,2020 +typing_extensions-3.7.4.3.dist-info/WHEEL,sha256=p46_5Uhzqz6AzeSosiOnxK-zmFja1i22CrQCjmYe8ec,92 +typing_extensions-3.7.4.3.dist-info/top_level.txt,sha256=hkDmk3VmrfXPOD--jS4aKTCu6kFZo-kVT1cIFfq1eU8,18 +typing_extensions-3.7.4.3.dist-info/RECORD,, diff --git a/third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/WHEEL b/third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/WHEEL similarity index 65% rename from third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/WHEEL rename to third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/WHEEL index 385faab0525cc..3b5c4038dd7bd 100644 --- a/third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/WHEEL +++ b/third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/WHEEL @@ -1,5 +1,5 @@ Wheel-Version: 1.0 -Generator: bdist_wheel (0.36.2) +Generator: bdist_wheel (0.33.6) Root-Is-Purelib: true Tag: py3-none-any diff --git a/third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/top_level.txt b/third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/top_level.txt similarity index 100% rename from third_party/python/typing_extensions/typing_extensions-3.10.0.0.dist-info/top_level.txt rename to third_party/python/typing_extensions/typing_extensions-3.7.4.3.dist-info/top_level.txt diff --git a/third_party/python/typing_extensions/typing_extensions.py b/third_party/python/typing_extensions/typing_extensions.py index 82d1c2dc2c406..a6f4281b258ea 100644 --- a/third_party/python/typing_extensions/typing_extensions.py +++ b/third_party/python/typing_extensions/typing_extensions.py @@ -115,9 +115,7 @@ def _check_methods_in_mro(C, *methods): __all__ = [ # Super-special typing primitives. 'ClassVar', - 'Concatenate', 'Final', - 'ParamSpec', 'Type', # ABCs (from collections.abc). @@ -136,7 +134,6 @@ def _check_methods_in_mro(C, *methods): 'Counter', 'Deque', 'DefaultDict', - 'OrderedDict' 'TypedDict', # Structural checks, a.k.a. protocols. @@ -149,8 +146,6 @@ def _check_methods_in_mro(C, *methods): 'NewType', 'overload', 'Text', - 'TypeAlias', - 'TypeGuard', 'TYPE_CHECKING', ] @@ -943,34 +938,6 @@ def __new__(cls, *args, **kwds): return _generic_new(collections.defaultdict, cls, *args, **kwds) -if hasattr(typing, 'OrderedDict'): - OrderedDict = typing.OrderedDict -elif (3, 7, 0) <= sys.version_info[:3] < (3, 7, 2): - OrderedDict = typing._alias(collections.OrderedDict, (KT, VT)) -elif _geqv_defined: - class OrderedDict(collections.OrderedDict, typing.MutableMapping[KT, VT], - metaclass=_ExtensionsGenericMeta, - extra=collections.OrderedDict): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, OrderedDict): - return collections.OrderedDict(*args, **kwds) - return _generic_new(collections.OrderedDict, cls, *args, **kwds) -else: - class OrderedDict(collections.OrderedDict, typing.MutableMapping[KT, VT], - metaclass=_ExtensionsGenericMeta, - extra=collections.OrderedDict): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is OrderedDict: - return collections.OrderedDict(*args, **kwds) - return _generic_new(collections.OrderedDict, cls, *args, **kwds) - - if hasattr(typing, 'Counter'): Counter = typing.Counter elif (3, 5, 0) <= sys.version_info[:3] <= (3, 5, 1): @@ -1152,11 +1119,6 @@ def _is_callable_members_only(cls): if hasattr(typing, 'Protocol'): Protocol = typing.Protocol elif HAVE_PROTOCOLS and not PEP_560: - - def _no_init(self, *args, **kwargs): - if type(self)._is_protocol: - raise TypeError('Protocols cannot be instantiated') - class _ProtocolMeta(GenericMeta): """Internal metaclass for Protocol. @@ -1247,6 +1209,9 @@ def __init__(cls, *args, **kwargs): raise TypeError('Protocols can only inherit from other' ' protocols, got %r' % base) + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') cls.__init__ = _no_init def _proto_hook(other): @@ -1401,10 +1366,6 @@ def __new__(cls, *args, **kwds): elif PEP_560: from typing import _type_check, _GenericAlias, _collect_type_vars # noqa - def _no_init(self, *args, **kwargs): - if type(self)._is_protocol: - raise TypeError('Protocols cannot be instantiated') - class _ProtocolMeta(abc.ABCMeta): # This metaclass is a bit unfortunate and exists only because of the lack # of __instancehook__. @@ -1581,6 +1542,10 @@ def _proto_hook(other): isinstance(base, _ProtocolMeta) and base._is_protocol): raise TypeError('Protocols can only inherit from other' ' protocols, got %r' % base) + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') cls.__init__ = _no_init @@ -1619,11 +1584,9 @@ def __index__(self) -> int: pass -if sys.version_info >= (3, 9, 2): +if sys.version_info[:2] >= (3, 9): # The standard library TypedDict in Python 3.8 does not store runtime information # about which (if any) keys are optional. See https://bugs.python.org/issue38834 - # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" - # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 TypedDict = typing.TypedDict else: def _check_fails(cls, other): @@ -1680,24 +1643,19 @@ def _typeddict_new(*args, total=True, **kwargs): raise TypeError("TypedDict takes either a dict or keyword arguments," " but not both") - ns = {'__annotations__': dict(fields)} + ns = {'__annotations__': dict(fields), '__total__': total} try: # Setting correct module is necessary to make typed dict classes pickleable. ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass - return _TypedDictMeta(typename, (), ns, total=total) + return _TypedDictMeta(typename, (), ns) _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,' ' /, *, total=True, **kwargs)') class _TypedDictMeta(type): - def __init__(cls, name, bases, ns, total=True): - # In Python 3.4 and 3.5 the __init__ method also needs to support the keyword arguments. - # See https://www.python.org/dev/peps/pep-0487/#implementation-details - super(_TypedDictMeta, cls).__init__(name, bases, ns) - def __new__(cls, name, bases, ns, total=True): # Create new typed dict class object. # This method is called directly when TypedDict is subclassed, @@ -2066,23 +2024,11 @@ class Annotated(metaclass=AnnotatedMeta): # Python 3.8 has get_origin() and get_args() but those implementations aren't # Annotated-aware, so we can't use those, only Python 3.9 versions will do. -# Similarly, Python 3.9's implementation doesn't support ParamSpecArgs and -# ParamSpecKwargs. -if sys.version_info[:2] >= (3, 10): +if sys.version_info[:2] >= (3, 9): get_origin = typing.get_origin get_args = typing.get_args elif PEP_560: - from typing import _GenericAlias - try: - # 3.9+ - from typing import _BaseGenericAlias - except ImportError: - _BaseGenericAlias = _GenericAlias - try: - # 3.9+ - from typing import GenericAlias - except ImportError: - GenericAlias = _GenericAlias + from typing import _GenericAlias # noqa def get_origin(tp): """Get the unsubscripted version of a type. @@ -2097,12 +2043,10 @@ def get_origin(tp): get_origin(Generic[T]) is Generic get_origin(Union[T, int]) is Union get_origin(List[Tuple[T, T]][int]) == list - get_origin(P.args) is P """ if isinstance(tp, _AnnotatedAlias): return Annotated - if isinstance(tp, (_GenericAlias, GenericAlias, _BaseGenericAlias, - ParamSpecArgs, ParamSpecKwargs)): + if isinstance(tp, _GenericAlias): return tp.__origin__ if tp is Generic: return Generic @@ -2121,9 +2065,7 @@ def get_args(tp): """ if isinstance(tp, _AnnotatedAlias): return (tp.__origin__,) + tp.__metadata__ - if isinstance(tp, (_GenericAlias, GenericAlias)): - if getattr(tp, "_special", False): - return () + if isinstance(tp, _GenericAlias): res = tp.__args__ if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis: res = (list(res[:-1]), res[-1]) @@ -2224,582 +2166,3 @@ class TypeAlias(metaclass=_TypeAliasMeta, _root=True): It's invalid when used anywhere except as in the example above. """ __slots__ = () - - -# Python 3.10+ has PEP 612 -if hasattr(typing, 'ParamSpecArgs'): - ParamSpecArgs = typing.ParamSpecArgs - ParamSpecKwargs = typing.ParamSpecKwargs -else: - class _Immutable: - """Mixin to indicate that object should not be copied.""" - __slots__ = () - - def __copy__(self): - return self - - def __deepcopy__(self, memo): - return self - - class ParamSpecArgs(_Immutable): - """The args for a ParamSpec object. - - Given a ParamSpec object P, P.args is an instance of ParamSpecArgs. - - ParamSpecArgs objects have a reference back to their ParamSpec: - - P.args.__origin__ is P - - This type is meant for runtime introspection and has no special meaning to - static type checkers. - """ - def __init__(self, origin): - self.__origin__ = origin - - def __repr__(self): - return "{}.args".format(self.__origin__.__name__) - - class ParamSpecKwargs(_Immutable): - """The kwargs for a ParamSpec object. - - Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs. - - ParamSpecKwargs objects have a reference back to their ParamSpec: - - P.kwargs.__origin__ is P - - This type is meant for runtime introspection and has no special meaning to - static type checkers. - """ - def __init__(self, origin): - self.__origin__ = origin - - def __repr__(self): - return "{}.kwargs".format(self.__origin__.__name__) - -if hasattr(typing, 'ParamSpec'): - ParamSpec = typing.ParamSpec -else: - - # Inherits from list as a workaround for Callable checks in Python < 3.9.2. - class ParamSpec(list): - """Parameter specification variable. - - Usage:: - - P = ParamSpec('P') - - Parameter specification variables exist primarily for the benefit of static - type checkers. They are used to forward the parameter types of one - callable to another callable, a pattern commonly found in higher order - functions and decorators. They are only valid when used in ``Concatenate``, - or s the first argument to ``Callable``. In Python 3.10 and higher, - they are also supported in user-defined Generics at runtime. - See class Generic for more information on generic types. An - example for annotating a decorator:: - - T = TypeVar('T') - P = ParamSpec('P') - - def add_logging(f: Callable[P, T]) -> Callable[P, T]: - '''A type-safe decorator to add logging to a function.''' - def inner(*args: P.args, **kwargs: P.kwargs) -> T: - logging.info(f'{f.__name__} was called') - return f(*args, **kwargs) - return inner - - @add_logging - def add_two(x: float, y: float) -> float: - '''Add two numbers together.''' - return x + y - - Parameter specification variables defined with covariant=True or - contravariant=True can be used to declare covariant or contravariant - generic types. These keyword arguments are valid, but their actual semantics - are yet to be decided. See PEP 612 for details. - - Parameter specification variables can be introspected. e.g.: - - P.__name__ == 'T' - P.__bound__ == None - P.__covariant__ == False - P.__contravariant__ == False - - Note that only parameter specification variables defined in global scope can - be pickled. - """ - - @property - def args(self): - return ParamSpecArgs(self) - - @property - def kwargs(self): - return ParamSpecKwargs(self) - - def __init__(self, name, *, bound=None, covariant=False, contravariant=False): - super().__init__([self]) - self.__name__ = name - self.__covariant__ = bool(covariant) - self.__contravariant__ = bool(contravariant) - if bound: - self.__bound__ = typing._type_check(bound, 'Bound must be a type.') - else: - self.__bound__ = None - - # for pickling: - try: - def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - def_mod = None - if def_mod != 'typing_extensions': - self.__module__ = def_mod - - def __repr__(self): - if self.__covariant__: - prefix = '+' - elif self.__contravariant__: - prefix = '-' - else: - prefix = '~' - return prefix + self.__name__ - - def __hash__(self): - return object.__hash__(self) - - def __eq__(self, other): - return self is other - - def __reduce__(self): - return self.__name__ - - # Hack to get typing._type_check to pass. - def __call__(self, *args, **kwargs): - pass - - # Note: Can't fake ParamSpec as a TypeVar to get it to work - # with Generics. ParamSpec isn't an instance of TypeVar in 3.10. - # So encouraging code like isinstance(ParamSpec('P'), TypeVar)) - # will lead to breakage in 3.10. - # This also means no accurate __parameters__ for GenericAliases. - -# Inherits from list as a workaround for Callable checks in Python < 3.9.2. -class _ConcatenateGenericAlias(list): - def __init__(self, origin, args): - super().__init__(args) - self.__origin__ = origin - self.__args__ = args - - def __repr__(self): - _type_repr = typing._type_repr - return '{origin}[{args}]' \ - .format(origin=_type_repr(self.__origin__), - args=', '.join(_type_repr(arg) for arg in self.__args__)) - - def __hash__(self): - return hash((self.__origin__, self.__args__)) - -@_tp_cache -def _concatenate_getitem(self, parameters): - if parameters == (): - raise TypeError("Cannot take a Concatenate of no types.") - if not isinstance(parameters, tuple): - parameters = (parameters,) - if not isinstance(parameters[-1], ParamSpec): - raise TypeError("The last parameter to Concatenate should be a " - "ParamSpec variable.") - msg = "Concatenate[arg, ...]: each arg must be a type." - parameters = tuple(typing._type_check(p, msg) for p in parameters) - return _ConcatenateGenericAlias(self, parameters) - - -if hasattr(typing, 'Concatenate'): - Concatenate = typing.Concatenate - _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa -elif sys.version_info[:2] >= (3, 9): - @_TypeAliasForm - def Concatenate(self, parameters): - """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a - higher order function which adds, removes or transforms parameters of a - callable. - - For example:: - - Callable[Concatenate[int, P], int] - - See PEP 612 for detailed information. - """ - return _concatenate_getitem(self, parameters) - -elif sys.version_info[:2] >= (3, 7): - class _ConcatenateForm(typing._SpecialForm, _root=True): - def __repr__(self): - return 'typing_extensions.' + self._name - - def __getitem__(self, parameters): - return _concatenate_getitem(self, parameters) - - Concatenate = _ConcatenateForm('Concatenate', - doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a - higher order function which adds, removes or transforms parameters of a - callable. - - For example:: - - Callable[Concatenate[int, P], int] - - See PEP 612 for detailed information. - """) - -elif hasattr(typing, '_FinalTypingBase'): - class _ConcatenateAliasMeta(typing.TypingMeta): - """Metaclass for Concatenate.""" - - def __repr__(self): - return 'typing_extensions.Concatenate' - - class _ConcatenateAliasBase(typing._FinalTypingBase, - metaclass=_ConcatenateAliasMeta, - _root=True): - """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a - higher order function which adds, removes or transforms parameters of a - callable. - - For example:: - - Callable[Concatenate[int, P], int] - - See PEP 612 for detailed information. - """ - __slots__ = () - - def __instancecheck__(self, obj): - raise TypeError("Concatenate cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Concatenate cannot be used with issubclass().") - - def __repr__(self): - return 'typing_extensions.Concatenate' - - def __getitem__(self, parameters): - return _concatenate_getitem(self, parameters) - - Concatenate = _ConcatenateAliasBase(_root=True) -# For 3.5.0 - 3.5.2 -else: - class _ConcatenateAliasMeta(typing.TypingMeta): - """Metaclass for Concatenate.""" - - def __instancecheck__(self, obj): - raise TypeError("TypeAlias cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("TypeAlias cannot be used with issubclass().") - - def __call__(self, *args, **kwargs): - raise TypeError("Cannot instantiate TypeAlias") - - def __getitem__(self, parameters): - return _concatenate_getitem(self, parameters) - - class Concatenate(metaclass=_ConcatenateAliasMeta, _root=True): - """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a - higher order function which adds, removes or transforms parameters of a - callable. - - For example:: - - Callable[Concatenate[int, P], int] - - See PEP 612 for detailed information. - """ - __slots__ = () - -if hasattr(typing, 'TypeGuard'): - TypeGuard = typing.TypeGuard -elif sys.version_info[:2] >= (3, 9): - class _TypeGuardForm(typing._SpecialForm, _root=True): - def __repr__(self): - return 'typing_extensions.' + self._name - - @_TypeGuardForm - def TypeGuard(self, parameters): - """Special typing form used to annotate the return type of a user-defined - type guard function. ``TypeGuard`` only accepts a single type argument. - At runtime, functions marked this way should return a boolean. - - ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static - type checkers to determine a more precise type of an expression within a - program's code flow. Usually type narrowing is done by analyzing - conditional code flow and applying the narrowing to a block of code. The - conditional expression here is sometimes referred to as a "type guard". - - Sometimes it would be convenient to use a user-defined boolean function - as a type guard. Such a function should use ``TypeGuard[...]`` as its - return type to alert static type checkers to this intention. - - Using ``-> TypeGuard`` tells the static type checker that for a given - function: - - 1. The return value is a boolean. - 2. If the return value is ``True``, the type of its argument - is the type inside ``TypeGuard``. - - For example:: - - def is_str(val: Union[str, float]): - # "isinstance" type guard - if isinstance(val, str): - # Type of ``val`` is narrowed to ``str`` - ... - else: - # Else, type of ``val`` is narrowed to ``float``. - ... - - Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower - form of ``TypeA`` (it can even be a wider form) and this may lead to - type-unsafe results. The main reason is to allow for things like - narrowing ``List[object]`` to ``List[str]`` even though the latter is not - a subtype of the former, since ``List`` is invariant. The responsibility of - writing type-safe type guards is left to the user. - - ``TypeGuard`` also works with type variables. For more information, see - PEP 647 (User-Defined Type Guards). - """ - item = typing._type_check(parameters, '{} accepts only single type.'.format(self)) - return _GenericAlias(self, (item,)) - -elif sys.version_info[:2] >= (3, 7): - class _TypeGuardForm(typing._SpecialForm, _root=True): - - def __repr__(self): - return 'typing_extensions.' + self._name - - def __getitem__(self, parameters): - item = typing._type_check(parameters, - '{} accepts only a single type'.format(self._name)) - return _GenericAlias(self, (item,)) - - TypeGuard = _TypeGuardForm( - 'TypeGuard', - doc="""Special typing form used to annotate the return type of a user-defined - type guard function. ``TypeGuard`` only accepts a single type argument. - At runtime, functions marked this way should return a boolean. - - ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static - type checkers to determine a more precise type of an expression within a - program's code flow. Usually type narrowing is done by analyzing - conditional code flow and applying the narrowing to a block of code. The - conditional expression here is sometimes referred to as a "type guard". - - Sometimes it would be convenient to use a user-defined boolean function - as a type guard. Such a function should use ``TypeGuard[...]`` as its - return type to alert static type checkers to this intention. - - Using ``-> TypeGuard`` tells the static type checker that for a given - function: - - 1. The return value is a boolean. - 2. If the return value is ``True``, the type of its argument - is the type inside ``TypeGuard``. - - For example:: - - def is_str(val: Union[str, float]): - # "isinstance" type guard - if isinstance(val, str): - # Type of ``val`` is narrowed to ``str`` - ... - else: - # Else, type of ``val`` is narrowed to ``float``. - ... - - Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower - form of ``TypeA`` (it can even be a wider form) and this may lead to - type-unsafe results. The main reason is to allow for things like - narrowing ``List[object]`` to ``List[str]`` even though the latter is not - a subtype of the former, since ``List`` is invariant. The responsibility of - writing type-safe type guards is left to the user. - - ``TypeGuard`` also works with type variables. For more information, see - PEP 647 (User-Defined Type Guards). - """) -elif hasattr(typing, '_FinalTypingBase'): - class _TypeGuard(typing._FinalTypingBase, _root=True): - """Special typing form used to annotate the return type of a user-defined - type guard function. ``TypeGuard`` only accepts a single type argument. - At runtime, functions marked this way should return a boolean. - - ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static - type checkers to determine a more precise type of an expression within a - program's code flow. Usually type narrowing is done by analyzing - conditional code flow and applying the narrowing to a block of code. The - conditional expression here is sometimes referred to as a "type guard". - - Sometimes it would be convenient to use a user-defined boolean function - as a type guard. Such a function should use ``TypeGuard[...]`` as its - return type to alert static type checkers to this intention. - - Using ``-> TypeGuard`` tells the static type checker that for a given - function: - - 1. The return value is a boolean. - 2. If the return value is ``True``, the type of its argument - is the type inside ``TypeGuard``. - - For example:: - - def is_str(val: Union[str, float]): - # "isinstance" type guard - if isinstance(val, str): - # Type of ``val`` is narrowed to ``str`` - ... - else: - # Else, type of ``val`` is narrowed to ``float``. - ... - - Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower - form of ``TypeA`` (it can even be a wider form) and this may lead to - type-unsafe results. The main reason is to allow for things like - narrowing ``List[object]`` to ``List[str]`` even though the latter is not - a subtype of the former, since ``List`` is invariant. The responsibility of - writing type-safe type guards is left to the user. - - ``TypeGuard`` also works with type variables. For more information, see - PEP 647 (User-Defined Type Guards). - """ - - __slots__ = ('__type__',) - - def __init__(self, tp=None, **kwds): - self.__type__ = tp - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is None: - return cls(typing._type_check(item, - '{} accepts only a single type.'.format(cls.__name__[1:])), - _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - def _eval_type(self, globalns, localns): - new_tp = typing._eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(new_tp, _root=True) - - def __repr__(self): - r = super().__repr__() - if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, _TypeGuard): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - TypeGuard = _TypeGuard(_root=True) -else: - class _TypeGuardMeta(typing.TypingMeta): - """Metaclass for TypeGuard""" - - def __new__(cls, name, bases, namespace, tp=None, _root=False): - self = super().__new__(cls, name, bases, namespace, _root=_root) - if tp is not None: - self.__type__ = tp - return self - - def __instancecheck__(self, obj): - raise TypeError("TypeGuard cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("TypeGuard cannot be used with issubclass().") - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is not None: - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - param = typing._type_check( - item, - '{} accepts only single type.'.format(cls.__name__[1:])) - return cls(self.__name__, self.__bases__, - dict(self.__dict__), tp=param, _root=True) - - def _eval_type(self, globalns, localns): - new_tp = typing._eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(self.__name__, self.__bases__, - dict(self.__dict__), tp=self.__type__, - _root=True) - - def __repr__(self): - r = super().__repr__() - if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not hasattr(other, "__type__"): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - class TypeGuard(typing.Final, metaclass=_TypeGuardMeta, _root=True): - """Special typing form used to annotate the return type of a user-defined - type guard function. ``TypeGuard`` only accepts a single type argument. - At runtime, functions marked this way should return a boolean. - - ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static - type checkers to determine a more precise type of an expression within a - program's code flow. Usually type narrowing is done by analyzing - conditional code flow and applying the narrowing to a block of code. The - conditional expression here is sometimes referred to as a "type guard". - - Sometimes it would be convenient to use a user-defined boolean function - as a type guard. Such a function should use ``TypeGuard[...]`` as its - return type to alert static type checkers to this intention. - - Using ``-> TypeGuard`` tells the static type checker that for a given - function: - - 1. The return value is a boolean. - 2. If the return value is ``True``, the type of its argument - is the type inside ``TypeGuard``. - - For example:: - - def is_str(val: Union[str, float]): - # "isinstance" type guard - if isinstance(val, str): - # Type of ``val`` is narrowed to ``str`` - ... - else: - # Else, type of ``val`` is narrowed to ``float``. - ... - - Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower - form of ``TypeA`` (it can even be a wider form) and this may lead to - type-unsafe results. The main reason is to allow for things like - narrowing ``List[object]`` to ``List[str]`` even though the latter is not - a subtype of the former, since ``List`` is invariant. The responsibility of - writing type-safe type guards is left to the user. - - ``TypeGuard`` also works with type variables. For more information, see - PEP 647 (User-Defined Type Guards). - """ - __type__ = None