diff --git a/NEWS.rst b/NEWS.rst index 2ae1fc508f420574e8a89dabcef9953f50539fb3..f6b24c4734e1520e2c215e537dd7c14e15eeb852 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -13,7 +13,7 @@ - Fix an error where the vendored requests was not correctly containing itself to only the internal vendored prefix. -- Restore compatability with 2.6. +- Restore compatibility with 2.6. 9.0.2 (2018-03-16) diff --git a/news/36f6f515-fa9e-4d45-84fd-740a5c09752e.trivial b/news/36f6f515-fa9e-4d45-84fd-740a5c09752e.trivial new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/news/4954.feature b/news/4954.feature new file mode 100644 index 0000000000000000000000000000000000000000..4d8104473b87bde5f44827c8d32481646839a2a5 --- /dev/null +++ b/news/4954.feature @@ -0,0 +1 @@ +Add auto completion of short options. diff --git a/news/4966.bugfix b/news/4966.bugfix new file mode 100644 index 0000000000000000000000000000000000000000..93df64c670064bfbf31d2ad03aeaac777946948a --- /dev/null +++ b/news/4966.bugfix @@ -0,0 +1 @@ +Terminal size is now correctly inferred when using Python 3 on Windows. diff --git a/src/pip/_internal/__init__.py b/src/pip/_internal/__init__.py index fede520fad712a1e64d2b40258d1620376284ca4..865d9ec3cc3b9f879b6caf401c8571f00e07b1c0 100755 --- a/src/pip/_internal/__init__.py +++ b/src/pip/_internal/__init__.py @@ -105,9 +105,11 @@ def autocomplete(): sys.exit(1) subcommand = commands_dict[subcommand_name]() - options += [(opt.get_opt_string(), opt.nargs) - for opt in subcommand.parser.option_list_all - if opt.help != optparse.SUPPRESS_HELP] + + for opt in subcommand.parser.option_list_all: + if opt.help != optparse.SUPPRESS_HELP: + for opt_str in opt._long_opts + opt._short_opts: + options.append((opt_str, opt.nargs)) # filter out previously specified options from available options prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]] @@ -117,7 +119,7 @@ def autocomplete(): for option in options: opt_label = option[0] # append '=' to options which require args - if option[1]: + if option[1] and option[0][:2] == "--": opt_label += '=' print(opt_label) else: @@ -127,8 +129,9 @@ def autocomplete(): opts.append(parser.option_list) opts = (o for it in opts for o in it) - subcommands += [i.get_opt_string() for i in opts - if i.help != optparse.SUPPRESS_HELP] + for opt in opts: + if opt.help != optparse.SUPPRESS_HELP: + subcommands += opt._long_opts + opt._short_opts print(' '.join([x for x in subcommands if x.startswith(current)])) sys.exit(1) diff --git a/src/pip/_internal/baseparser.py b/src/pip/_internal/baseparser.py index 2fda656f76bd25c863d9cb3fc1bb803fea7b4bc4..9a8d129747e5308f32ba713570e887deb2ba7e22 100644 --- a/src/pip/_internal/baseparser.py +++ b/src/pip/_internal/baseparser.py @@ -9,8 +9,8 @@ from distutils.util import strtobool from pip._vendor.six import string_types +from pip._internal.compat import get_terminal_size from pip._internal.configuration import Configuration, ConfigurationError -from pip._internal.utils.misc import get_terminal_size logger = logging.getLogger(__name__) diff --git a/src/pip/_internal/cmdoptions.py b/src/pip/_internal/cmdoptions.py index 17d9e0ac34d5e03cca2420e424e78742bdd10c61..6447285a10b29022995ade8dd3e0e9ba514131ae 100644 --- a/src/pip/_internal/cmdoptions.py +++ b/src/pip/_internal/cmdoptions.py @@ -429,7 +429,7 @@ no_deps = partial( dest='ignore_dependencies', action='store_true', default=False, - help="Don't install package dependencies)." + help="Don't install package dependencies.", ) # type: Any build_dir = partial( diff --git a/src/pip/_internal/commands/configuration.py b/src/pip/_internal/commands/configuration.py index 24a45e847170dd69cf51909557b52e7de998eab8..57448cb90d5b4b282a70e445058ed3a627baa938 100644 --- a/src/pip/_internal/commands/configuration.py +++ b/src/pip/_internal/commands/configuration.py @@ -13,19 +13,7 @@ logger = logging.getLogger(__name__) class ConfigurationCommand(Command): - """Manage local and global configuration.""" - name = 'config' - usage = """ - %prog [] list - %prog [] [--editor ] edit - - %prog [] get name - %prog [] set name value - %prog [] unset name - """ - - summary = """ - Manage local and global configuration. + """Manage local and global configuration. Subcommands: @@ -41,6 +29,18 @@ class ConfigurationCommand(Command): default. """ + name = 'config' + usage = """ + %prog [] list + %prog [] [--editor ] edit + + %prog [] get name + %prog [] set name value + %prog [] unset name + """ + + summary = "Manage local and global configuration." + def __init__(self, *args, **kwargs): super(ConfigurationCommand, self).__init__(*args, **kwargs) diff --git a/src/pip/_internal/commands/search.py b/src/pip/_internal/commands/search.py index 5a577b50ed03a0c0214f8760cc86482733b8e4c7..3abdf59735ad302aa3357d8bfcd620fde50fdf49 100644 --- a/src/pip/_internal/commands/search.py +++ b/src/pip/_internal/commands/search.py @@ -12,12 +12,12 @@ from pip._vendor.packaging.version import parse as parse_version from pip._vendor.six.moves import xmlrpc_client # type: ignore from pip._internal.basecommand import SUCCESS, Command +from pip._internal.compat import get_terminal_size from pip._internal.download import PipXmlrpcTransport from pip._internal.exceptions import CommandError from pip._internal.models import PyPI from pip._internal.status_codes import NO_MATCHES_FOUND from pip._internal.utils.logging import indent_log -from pip._internal.utils.misc import get_terminal_size logger = logging.getLogger(__name__) diff --git a/src/pip/_internal/compat.py b/src/pip/_internal/compat.py index 70d7730f666dc6f4fa2d06db59a7cb4172dbedf1..c71f5e66fcbff18c704a62d8f7332a340142ffe4 100644 --- a/src/pip/_internal/compat.py +++ b/src/pip/_internal/compat.py @@ -6,6 +6,7 @@ import codecs import locale import logging import os +import shutil import sys from pip._vendor.six import text_type @@ -23,7 +24,7 @@ except ImportError: __all__ = [ "ipaddress", "uses_pycache", "console_to_str", "native_str", - "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", + "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", "get_terminal_size", ] @@ -192,3 +193,43 @@ def samefile(file1, file2): path1 = os.path.normcase(os.path.abspath(file1)) path2 = os.path.normcase(os.path.abspath(file2)) return path1 == path2 + + +if hasattr(shutil, 'get_terminal_size'): + def get_terminal_size(): + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + return tuple(shutil.get_terminal_size()) +else: + def get_terminal_size(): + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + def ioctl_GWINSZ(fd): + try: + import fcntl + import termios + import struct + cr = struct.unpack( + 'hh', + fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234') + ) + except: + return None + if cr == (0, 0): + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + return int(cr[1]), int(cr[0]) diff --git a/src/pip/_internal/utils/misc.py b/src/pip/_internal/utils/misc.py index d0280f36405e051b088c71b7392bda52a1e399bd..9d4c9b16fc80d13cd15df642c45a0bfb0fc436a9 100644 --- a/src/pip/_internal/utils/misc.py +++ b/src/pip/_internal/utils/misc.py @@ -43,7 +43,7 @@ __all__ = ['rmtree', 'display_path', 'backup_dir', 'is_svn_page', 'file_contents', 'split_leading_dir', 'has_leading_dir', 'normalize_path', - 'renames', 'get_terminal_size', 'get_prog', + 'renames', 'get_prog', 'unzip_file', 'untar_file', 'unpack_file', 'call_subprocess', 'captured_stdout', 'ensure_dir', 'ARCHIVE_EXTENSIONS', 'SUPPORTED_EXTENSIONS', @@ -438,36 +438,6 @@ def dist_location(dist): return dist.location -def get_terminal_size(): - """Returns a tuple (x, y) representing the width(x) and the height(x) - in characters of the terminal window.""" - def ioctl_GWINSZ(fd): - try: - import fcntl - import termios - import struct - cr = struct.unpack( - 'hh', - fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234') - ) - except: - return None - if cr == (0, 0): - return None - return cr - cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) - if not cr: - try: - fd = os.open(os.ctermid(), os.O_RDONLY) - cr = ioctl_GWINSZ(fd) - os.close(fd) - except: - pass - if not cr: - cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) - return int(cr[1]), int(cr[0]) - - def current_umask(): """Get the current umask which involves having to set it temporarily.""" mask = os.umask(0) diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py index c5483bf0301bd2a696f37450cbbb06e489f34e16..996dcc63d0f1bbacf4a4d01a201fa15cdd1c65d4 100644 --- a/tests/functional/test_completion.py +++ b/tests/functional/test_completion.py @@ -121,6 +121,29 @@ def test_completion_option_for_command(script): "autocomplete function could not complete ``--``" +def test_completion_short_option(script): + """ + Test getting completion for short options after ``-`` (eg. pip -) + """ + + res, env = setup_completion(script, 'pip -', '1') + + assert '-h' in res.stdout.split(),\ + "autocomplete function could not complete short options after ``-``" + + +def test_completion_short_option_for_command(script): + """ + Test getting completion for short options after ``-`` in command + (eg. pip search -) + """ + + res, env = setup_completion(script, 'pip search -', '2') + + assert '-h' in res.stdout.split(),\ + "autocomplete function could not complete short options after ``-``" + + @pytest.mark.parametrize('flag', ['--bash', '--zsh', '--fish']) def test_completion_uses_same_executable_name(script, flag): expect_stderr = sys.version_info[:2] == (3, 3) diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index d03a8c78d4f8224392a841bb96c5d086a895816a..ffa69640d1d62efb86b0487e8e1acea9ced02362 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -709,7 +709,7 @@ def create_basic_wheel_for_package(script, name, version, depends, extras): "{dist_info}/top_level.txt": """ {name} """, - # Have an empty RECORD becuase we don't want to be checking hashes. + # Have an empty RECORD because we don't want to be checking hashes. "{dist_info}/RECORD": "" } diff --git a/tests/unit/test_finder.py b/tests/unit/test_finder.py index 3139a0fbfa679c7b94df0e5e712bbaba3f34b0a4..ed7f0efdec04ef18a9fdddcb7fcc62ea30e19f76 100644 --- a/tests/unit/test_finder.py +++ b/tests/unit/test_finder.py @@ -402,7 +402,7 @@ def test_finder_only_installs_data_require(data): distribution are compatible with which version of Python by adding a data-python-require to the anchor links. - See pep 503 for more informations. + See pep 503 for more information. """ # using a local index (that has pre & dev releases) diff --git a/tests/yaml/README.md b/tests/yaml/README.md index 6a2506d81036917a2aac8f8b10d27a347b556599..559c7d8e24ce4b82f805cef8401703817f54c27a 100644 --- a/tests/yaml/README.md +++ b/tests/yaml/README.md @@ -1,5 +1,5 @@ # Fixtures -This directory contains fixtures for testing pip's resolver. The fixtures are written as yml files, with a convinient format that allows for specifying a custom index for temporary use. +This directory contains fixtures for testing pip's resolver. The fixtures are written as yml files, with a convenient format that allows for specifying a custom index for temporary use.