feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@@ -0,0 +1,648 @@
# Copyright 2012-2017, Andrey Kislyuk and argcomplete contributors.
# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.
from __future__ import absolute_import, division, print_function, unicode_literals
import os, sys, argparse, contextlib
from . import completers, my_shlex as shlex
from .compat import USING_PYTHON2, str, sys_encoding, ensure_str, ensure_bytes
from .completers import FilesCompleter, SuppressCompleter
from .my_argparse import IntrospectiveArgumentParser, action_is_satisfied, action_is_open, action_is_greedy
from .shellintegration import shellcode # noqa
_DEBUG = "_ARC_DEBUG" in os.environ
debug_stream = sys.stderr
def debug(*args):
if _DEBUG:
if USING_PYTHON2:
# debug_stream has to be binary mode in Python 2.
# Attempting to write unicode directly uses the default ascii conversion.
# Convert any unicode to bytes, leaving non-string input alone.
args = [ensure_bytes(x) if isinstance(x, str) else x for x in args]
print(file=debug_stream, *args)
BASH_FILE_COMPLETION_FALLBACK = 79
BASH_DIR_COMPLETION_FALLBACK = 80
safe_actions = (argparse._StoreAction,
argparse._StoreConstAction,
argparse._StoreTrueAction,
argparse._StoreFalseAction,
argparse._AppendAction,
argparse._AppendConstAction,
argparse._CountAction)
@contextlib.contextmanager
def mute_stdout():
stdout = sys.stdout
sys.stdout = open(os.devnull, "w")
try:
yield
finally:
sys.stdout = stdout
@contextlib.contextmanager
def mute_stderr():
stderr = sys.stderr
sys.stderr = open(os.devnull, "w")
try:
yield
finally:
sys.stderr.close()
sys.stderr = stderr
class ArgcompleteException(Exception):
pass
def split_line(line, point=None):
if point is None:
point = len(line)
lexer = shlex.shlex(line, posix=True)
lexer.whitespace_split = True
lexer.wordbreaks = os.environ.get("_ARGCOMPLETE_COMP_WORDBREAKS", "")
words = []
def split_word(word):
# TODO: make this less ugly
point_in_word = len(word) + point - lexer.instream.tell()
if isinstance(lexer.state, (str, bytes)) and lexer.state in lexer.whitespace:
point_in_word += 1
if point_in_word > len(word):
debug("In trailing whitespace")
words.append(word)
word = ""
prefix, suffix = word[:point_in_word], word[point_in_word:]
prequote = ""
# posix
if lexer.state is not None and lexer.state in lexer.quotes:
prequote = lexer.state
# non-posix
# if len(prefix) > 0 and prefix[0] in lexer.quotes:
# prequote, prefix = prefix[0], prefix[1:]
return prequote, prefix, suffix, words, lexer.last_wordbreak_pos
while True:
try:
word = lexer.get_token()
if word == lexer.eof:
# TODO: check if this is ever unsafe
# raise ArgcompleteException("Unexpected end of input")
return "", "", "", words, None
if lexer.instream.tell() >= point:
debug("word", word, "split, lexer state: '{s}'".format(s=lexer.state))
return split_word(word)
words.append(word)
except ValueError:
debug("word", lexer.token, "split (lexer stopped, state: '{s}')".format(s=lexer.state))
if lexer.instream.tell() >= point:
return split_word(lexer.token)
else:
raise ArgcompleteException("Unexpected internal state. Please report this bug at https://github.com/kislyuk/argcomplete/issues.") # noqa
def default_validator(completion, prefix):
return completion.startswith(prefix)
class CompletionFinder(object):
"""
Inherit from this class if you wish to override any of the stages below. Otherwise, use
``argcomplete.autocomplete()`` directly (it's a convenience instance of this class). It has the same signature as
:meth:`CompletionFinder.__call__()`.
"""
def __init__(self, argument_parser=None, always_complete_options=True, exclude=None, validator=None,
print_suppressed=False, default_completer=FilesCompleter(), append_space=None):
self._parser = argument_parser
self.always_complete_options = always_complete_options
self.exclude = exclude
if validator is None:
validator = default_validator
self.validator = validator
self.print_suppressed = print_suppressed
self.completing = False
self._display_completions = {}
self.default_completer = default_completer
if append_space is None:
append_space = os.environ.get("_ARGCOMPLETE_SUPPRESS_SPACE") != "1"
self.append_space = append_space
def __call__(self, argument_parser, always_complete_options=True, exit_method=os._exit, output_stream=None,
exclude=None, validator=None, print_suppressed=False, append_space=None,
default_completer=FilesCompleter()):
"""
:param argument_parser: The argument parser to autocomplete on
:type argument_parser: :class:`argparse.ArgumentParser`
:param always_complete_options:
Controls the autocompletion of option strings if an option string opening character (normally ``-``) has not
been entered. If ``True`` (default), both short (``-x``) and long (``--x``) option strings will be
suggested. If ``False``, no option strings will be suggested. If ``long``, long options and short options
with no long variant will be suggested. If ``short``, short options and long options with no short variant
will be suggested.
:type always_complete_options: boolean or string
:param exit_method:
Method used to stop the program after printing completions. Defaults to :meth:`os._exit`. If you want to
perform a normal exit that calls exit handlers, use :meth:`sys.exit`.
:type exit_method: callable
:param exclude: List of strings representing options to be omitted from autocompletion
:type exclude: iterable
:param validator:
Function to filter all completions through before returning (called with two string arguments, completion
and prefix; return value is evaluated as a boolean)
:type validator: callable
:param print_suppressed:
Whether or not to autocomplete options that have the ``help=argparse.SUPPRESS`` keyword argument set.
:type print_suppressed: boolean
:param append_space:
Whether to append a space to unique matches. The default is ``True``.
:type append_space: boolean
.. note::
If you are not subclassing CompletionFinder to override its behaviors,
use ``argcomplete.autocomplete()`` directly. It has the same signature as this method.
Produces tab completions for ``argument_parser``. See module docs for more info.
Argcomplete only executes actions if their class is known not to have side effects. Custom action classes can be
added to argcomplete.safe_actions, if their values are wanted in the ``parsed_args`` completer argument, or
their execution is otherwise desirable.
"""
self.__init__(argument_parser, always_complete_options=always_complete_options, exclude=exclude,
validator=validator, print_suppressed=print_suppressed, append_space=append_space,
default_completer=default_completer)
if "_ARGCOMPLETE" not in os.environ:
# not an argument completion invocation
return
if _DEBUG:
global debug_stream
try:
debug_stream = os.fdopen(9, "w")
except:
debug_stream = sys.stderr
if output_stream is None:
try:
output_stream = os.fdopen(8, "wb")
except:
debug("Unable to open fd 8 for writing, quitting")
exit_method(1)
# print("", stream=debug_stream)
# for v in "COMP_CWORD COMP_LINE COMP_POINT COMP_TYPE COMP_KEY _ARGCOMPLETE_COMP_WORDBREAKS COMP_WORDS".split():
# print(v, os.environ[v], stream=debug_stream)
ifs = os.environ.get("_ARGCOMPLETE_IFS", "\013")
if len(ifs) != 1:
debug("Invalid value for IFS, quitting [{v}]".format(v=ifs))
exit_method(1)
comp_line = os.environ["COMP_LINE"]
comp_point = int(os.environ["COMP_POINT"])
comp_line = ensure_str(comp_line)
cword_prequote, cword_prefix, cword_suffix, comp_words, last_wordbreak_pos = split_line(comp_line, comp_point)
# _ARGCOMPLETE is set by the shell script to tell us where comp_words
# should start, based on what we're completing.
# 1: <script> [args]
# 2: python <script> [args]
# 3: python -m <module> [args]
start = int(os.environ["_ARGCOMPLETE"]) - 1
comp_words = comp_words[start:]
debug("\nLINE: {!r}".format(comp_line),
"\nPOINT: {!r}".format(comp_point),
"\nPREQUOTE: {!r}".format(cword_prequote),
"\nPREFIX: {!r}".format(cword_prefix),
"\nSUFFIX: {!r}".format(cword_suffix),
"\nWORDS:", comp_words)
completions = self._get_completions(comp_words, cword_prefix, cword_prequote, last_wordbreak_pos)
debug("\nReturning completions:", completions)
output_stream.write(ifs.join(completions).encode(sys_encoding))
output_stream.flush()
if _DEBUG:
debug_stream.flush()
exit_method(0)
def _get_completions(self, comp_words, cword_prefix, cword_prequote, last_wordbreak_pos):
active_parsers = self._patch_argument_parser()
parsed_args = argparse.Namespace()
self.completing = True
if USING_PYTHON2:
# Python 2 argparse only properly works with byte strings.
comp_words = [ensure_bytes(word) for word in comp_words]
try:
debug("invoking parser with", comp_words[1:])
with mute_stderr():
a = self._parser.parse_known_args(comp_words[1:], namespace=parsed_args)
debug("parsed args:", a)
except BaseException as e:
debug("\nexception", type(e), str(e), "while parsing args")
self.completing = False
# key: complete word, value: description.
completions = self.collect_completions(active_parsers, parsed_args, cword_prefix, debug)
completions = self.filter_completions(completions)
completions = self.quote_completions(completions, cword_prequote, last_wordbreak_pos)
return completions
def _patch_argument_parser(self):
"""
Since argparse doesn't support much introspection, we monkey-patch it to replace the parse_known_args method and
all actions with hooks that tell us which action was last taken or about to be taken, and let us have the parser
figure out which subparsers need to be activated (then recursively monkey-patch those).
We save all active ArgumentParsers to extract all their possible option names later.
"""
self.active_parsers = []
self.visited_positionals = []
completer = self
def patch(parser):
completer.visited_positionals.append(parser)
completer.active_parsers.append(parser)
if isinstance(parser, IntrospectiveArgumentParser):
return
classname = "MonkeyPatchedIntrospectiveArgumentParser"
if USING_PYTHON2:
classname = bytes(classname)
parser.__class__ = type(classname, (IntrospectiveArgumentParser, parser.__class__), {})
for action in parser._actions:
if hasattr(action, "_orig_class"):
continue
# TODO: accomplish this with super
class IntrospectAction(action.__class__):
def __call__(self, parser, namespace, values, option_string=None):
debug("Action stub called on", self)
debug("\targs:", parser, namespace, values, option_string)
debug("\torig class:", self._orig_class)
debug("\torig callable:", self._orig_callable)
if not completer.completing:
self._orig_callable(parser, namespace, values, option_string=option_string)
elif issubclass(self._orig_class, argparse._SubParsersAction):
debug("orig class is a subparsers action: patching and running it")
patch(self._name_parser_map[values[0]])
self._orig_callable(parser, namespace, values, option_string=option_string)
elif self._orig_class in safe_actions:
if not self.option_strings:
completer.visited_positionals.append(self)
self._orig_callable(parser, namespace, values, option_string=option_string)
action._orig_class = action.__class__
action._orig_callable = action.__call__
action.__class__ = IntrospectAction
patch(self._parser)
debug("Active parsers:", self.active_parsers)
debug("Visited positionals:", self.visited_positionals)
return self.active_parsers
def _get_subparser_completions(self, parser, cword_prefix):
def filter_aliases(metavar, dest, prefix):
if not metavar:
return dest if dest and dest.startswith(prefix) else ""
# metavar combines dest and aliases with ",".
a = metavar.replace(",", "").split()
return " ".join(x for x in a if x.startswith(prefix))
for action in parser._get_subactions():
subcmd_with_aliases = filter_aliases(action.metavar, action.dest, cword_prefix)
if subcmd_with_aliases:
self._display_completions[subcmd_with_aliases] = action.help
completions = [subcmd for subcmd in parser.choices.keys() if subcmd.startswith(cword_prefix)]
return completions
def _include_options(self, action, cword_prefix):
if len(cword_prefix) > 0 or self.always_complete_options is True:
return [ensure_str(opt) for opt in action.option_strings if ensure_str(opt).startswith(cword_prefix)]
long_opts = [ensure_str(opt) for opt in action.option_strings if len(opt) > 2]
short_opts = [ensure_str(opt) for opt in action.option_strings if len(opt) <= 2]
if self.always_complete_options == "long":
return long_opts if long_opts else short_opts
elif self.always_complete_options == "short":
return short_opts if short_opts else long_opts
return []
def _get_option_completions(self, parser, cword_prefix):
self._display_completions.update(
[[" ".join(ensure_str(x) for x in action.option_strings if ensure_str(x).startswith(cword_prefix)), action.help] # noqa
for action in parser._actions
if action.option_strings])
option_completions = []
for action in parser._actions:
if not self.print_suppressed:
completer = getattr(action, "completer", None)
if isinstance(completer, SuppressCompleter) and completer.suppress():
continue
if action.help == argparse.SUPPRESS:
continue
if not self._action_allowed(action, parser):
continue
if not isinstance(action, argparse._SubParsersAction):
option_completions += self._include_options(action, cword_prefix)
return option_completions
@staticmethod
def _action_allowed(action, parser):
# Logic adapted from take_action in ArgumentParser._parse_known_args
# (members are saved by my_argparse.IntrospectiveArgumentParser)
for conflict_action in parser._action_conflicts.get(action, []):
if conflict_action in parser._seen_non_default_actions:
return False
return True
def _complete_active_option(self, parser, next_positional, cword_prefix, parsed_args, completions):
debug("Active actions (L={l}): {a}".format(l=len(parser.active_actions), a=parser.active_actions))
isoptional = cword_prefix and cword_prefix[0] in parser.prefix_chars
greedy_actions = [x for x in parser.active_actions if action_is_greedy(x, isoptional)]
if greedy_actions:
assert len(greedy_actions) == 1, "expect at most 1 greedy action"
# This means the action will fail to parse if the word under the cursor is not given
# to it, so give it exclusive control over completions (flush previous completions)
debug("Resetting completions because", greedy_actions[0], "must consume the next argument")
self._display_completions = {}
completions = []
elif isoptional:
# Only run completers if current word does not start with - (is not an optional)
return completions
complete_remaining_positionals = False
# Use the single greedy action (if there is one) or all active actions.
for active_action in greedy_actions or parser.active_actions:
if not active_action.option_strings: # action is a positional
if action_is_open(active_action):
# Any positional arguments after this may slide down into this action
# if more arguments are added (since the user may not be done yet),
# so it is extremely difficult to tell which completers to run.
# Running all remaining completers will probably show more than the user wants
# but it also guarantees we won't miss anything.
complete_remaining_positionals = True
if not complete_remaining_positionals:
if action_is_satisfied(active_action) and not action_is_open(active_action):
debug("Skipping", active_action)
continue
debug("Activating completion for", active_action, active_action._orig_class)
# completer = getattr(active_action, "completer", DefaultCompleter())
completer = getattr(active_action, "completer", None)
if completer is None:
if active_action.choices is not None and not isinstance(active_action, argparse._SubParsersAction):
completer = completers.ChoicesCompleter(active_action.choices)
elif not isinstance(active_action, argparse._SubParsersAction):
completer = self.default_completer
if completer:
if callable(completer):
completions_from_callable = [c for c in completer(
prefix=cword_prefix, action=active_action, parser=parser, parsed_args=parsed_args)
if self.validator(c, cword_prefix)]
if completions_from_callable:
completions += completions_from_callable
if isinstance(completer, completers.ChoicesCompleter):
self._display_completions.update(
[[x, active_action.help] for x in completions_from_callable])
else:
self._display_completions.update(
[[x, ""] for x in completions_from_callable])
else:
debug("Completer is not callable, trying the readline completer protocol instead")
for i in range(9999):
next_completion = completer.complete(cword_prefix, i)
if next_completion is None:
break
if self.validator(next_completion, cword_prefix):
self._display_completions.update({next_completion: ""})
completions.append(next_completion)
debug("Completions:", completions)
return completions
def collect_completions(self, active_parsers, parsed_args, cword_prefix, debug):
"""
Visits the active parsers and their actions, executes their completers or introspects them to collect their
option strings. Returns the resulting completions as a list of strings.
This method is exposed for overriding in subclasses; there is no need to use it directly.
"""
completions = []
debug("all active parsers:", active_parsers)
active_parser = active_parsers[-1]
debug("active_parser:", active_parser)
if self.always_complete_options or (len(cword_prefix) > 0 and cword_prefix[0] in active_parser.prefix_chars):
completions += self._get_option_completions(active_parser, cword_prefix)
debug("optional options:", completions)
next_positional = self._get_next_positional()
debug("next_positional:", next_positional)
if isinstance(next_positional, argparse._SubParsersAction):
completions += self._get_subparser_completions(next_positional, cword_prefix)
completions = self._complete_active_option(active_parser, next_positional, cword_prefix, parsed_args,
completions)
debug("active options:", completions)
debug("display completions:", self._display_completions)
return completions
def _get_next_positional(self):
"""
Get the next positional action if it exists.
"""
active_parser = self.active_parsers[-1]
last_positional = self.visited_positionals[-1]
all_positionals = active_parser._get_positional_actions()
if not all_positionals:
return None
if active_parser == last_positional:
return all_positionals[0]
i = 0
for i in range(len(all_positionals)):
if all_positionals[i] == last_positional:
break
if i + 1 < len(all_positionals):
return all_positionals[i + 1]
return None
def filter_completions(self, completions):
"""
Ensures collected completions are Unicode text, de-duplicates them, and excludes those specified by ``exclude``.
Returns the filtered completions as an iterable.
This method is exposed for overriding in subclasses; there is no need to use it directly.
"""
# On Python 2, we have to make sure all completions are unicode objects before we continue and output them.
# Otherwise, because python disobeys the system locale encoding and uses ascii as the default encoding, it will
# try to implicitly decode string objects using ascii, and fail.
completions = [ensure_str(c) for c in completions]
# De-duplicate completions and remove excluded ones
if self.exclude is None:
self.exclude = set()
seen = set(self.exclude)
return [c for c in completions if c not in seen and not seen.add(c)]
def quote_completions(self, completions, cword_prequote, last_wordbreak_pos):
"""
If the word under the cursor started with a quote (as indicated by a nonempty ``cword_prequote``), escapes
occurrences of that quote character in the completions, and adds the quote to the beginning of each completion.
Otherwise, escapes all characters that bash splits words on (``COMP_WORDBREAKS``), and removes portions of
completions before the first colon if (``COMP_WORDBREAKS``) contains a colon.
If there is only one completion, and it doesn't end with a **continuation character** (``/``, ``:``, or ``=``),
adds a space after the completion.
This method is exposed for overriding in subclasses; there is no need to use it directly.
"""
special_chars = "\\"
# If the word under the cursor was quoted, escape the quote char.
# Otherwise, escape all special characters and specially handle all COMP_WORDBREAKS chars.
if cword_prequote == "":
# Bash mangles completions which contain characters in COMP_WORDBREAKS.
# This workaround has the same effect as __ltrim_colon_completions in bash_completion
# (extended to characters other than the colon).
if last_wordbreak_pos:
completions = [c[last_wordbreak_pos + 1:] for c in completions]
special_chars += "();<>|&!`$* \t\n\"'"
elif cword_prequote == '"':
special_chars += '"`$!'
if os.environ.get("_ARGCOMPLETE_SHELL") == "tcsh":
# tcsh escapes special characters itself.
special_chars = ""
elif cword_prequote == "'":
# Nothing can be escaped in single quotes, so we need to close
# the string, escape the single quote, then open a new string.
special_chars = ""
completions = [c.replace("'", r"'\''") for c in completions]
for char in special_chars:
completions = [c.replace(char, "\\" + char) for c in completions]
if self.append_space:
# Similar functionality in bash was previously turned off by supplying the "-o nospace" option to complete.
# Now it is conditionally disabled using "compopt -o nospace" if the match ends in a continuation character.
# This code is retained for environments where this isn't done natively.
continuation_chars = "=/:"
if len(completions) == 1 and completions[0][-1] not in continuation_chars:
if cword_prequote == "":
completions[0] += " "
return completions
def rl_complete(self, text, state):
"""
Alternate entry point for using the argcomplete completer in a readline-based REPL. See also
`rlcompleter <https://docs.python.org/2/library/rlcompleter.html#completer-objects>`_.
Usage:
.. code-block:: python
import argcomplete, argparse, readline
parser = argparse.ArgumentParser()
...
completer = argcomplete.CompletionFinder(parser)
readline.set_completer_delims("")
readline.set_completer(completer.rl_complete)
readline.parse_and_bind("tab: complete")
result = input("prompt> ")
(Use ``raw_input`` instead of ``input`` on Python 2, or use `eight <https://github.com/kislyuk/eight>`_).
"""
if state == 0:
cword_prequote, cword_prefix, cword_suffix, comp_words, first_colon_pos = split_line(text)
comp_words.insert(0, sys.argv[0])
matches = self._get_completions(comp_words, cword_prefix, cword_prequote, first_colon_pos)
self._rl_matches = [text + match[len(cword_prefix):] for match in matches]
if state < len(self._rl_matches):
return self._rl_matches[state]
else:
return None
def get_display_completions(self):
"""
This function returns a mapping of option names to their help strings for displaying to the user
Usage:
.. code-block:: python
def display_completions(substitution, matches, longest_match_length):
_display_completions = argcomplete.autocomplete.get_display_completions()
print("")
if _display_completions:
help_len = [len(x) for x in _display_completions.values() if x]
if help_len:
maxlen = max([len(x) for x in _display_completions])
print("\\n".join("{0:{2}} -- {1}".format(k, v, maxlen)
for k, v in sorted(_display_completions.items())))
else:
print(" ".join(k for k in sorted(_display_completions)))
else:
print(" ".join(x for x in sorted(matches)))
import readline
print("cli /> {0}".format(readline.get_line_buffer()), end="")
readline.redisplay()
...
readline.set_completion_display_matches_hook(display_completions)
"""
return self._display_completions
class ExclusiveCompletionFinder(CompletionFinder):
@staticmethod
def _action_allowed(action, parser):
if not CompletionFinder._action_allowed(action, parser):
return False
append_classes = (argparse._AppendAction, argparse._AppendConstAction)
if action._orig_class in append_classes:
return True
if action not in parser._seen_non_default_actions:
return True
return False
autocomplete = CompletionFinder()
autocomplete.__doc__ = """ Use this to access argcomplete. See :meth:`argcomplete.CompletionFinder.__call__()`. """
def warn(*args):
"""
Prints **args** to standard error when running completions. This will interrupt the user's command line interaction;
use it to indicate an error condition that is preventing your completer from working.
"""
print("\n", file=debug_stream, *args)

View File

@@ -0,0 +1,25 @@
from __future__ import absolute_import, division, print_function, unicode_literals
import locale
import sys
sys_encoding = locale.getpreferredencoding()
USING_PYTHON2 = True if sys.version_info < (3, 0) else False
if USING_PYTHON2:
str = unicode # noqa
else:
str = str
def ensure_bytes(x, encoding=sys_encoding):
if not isinstance(x, bytes):
x = x.encode(encoding)
return x
def ensure_str(x, encoding=sys_encoding):
if not isinstance(x, str):
x = x.decode(encoding)
return x

View File

@@ -0,0 +1,108 @@
# Copyright 2012-2013, Andrey Kislyuk and argcomplete contributors.
# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.
from __future__ import absolute_import, division, print_function, unicode_literals
import os
import subprocess
from .compat import str, sys_encoding
def _call(*args, **kwargs):
try:
return subprocess.check_output(*args, **kwargs).decode(sys_encoding).splitlines()
except subprocess.CalledProcessError:
return []
class ChoicesCompleter(object):
def __init__(self, choices):
self.choices = choices
def _convert(self, choice):
if isinstance(choice, bytes):
choice = choice.decode(sys_encoding)
if not isinstance(choice, str):
choice = str(choice)
return choice
def __call__(self, **kwargs):
return (self._convert(c) for c in self.choices)
EnvironCompleter = ChoicesCompleter(os.environ)
class FilesCompleter(object):
"""
File completer class, optionally takes a list of allowed extensions
"""
def __init__(self, allowednames=(), directories=True):
# Fix if someone passes in a string instead of a list
if isinstance(allowednames, (str, bytes)):
allowednames = [allowednames]
self.allowednames = [x.lstrip("*").lstrip(".") for x in allowednames]
self.directories = directories
def __call__(self, prefix, **kwargs):
completion = []
if self.allowednames:
if self.directories:
files = _call(["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)])
completion += [f + "/" for f in files]
for x in self.allowednames:
completion += _call(["bash", "-c", "compgen -A file -X '!*.{0}' -- '{p}'".format(x, p=prefix)])
else:
completion += _call(["bash", "-c", "compgen -A file -- '{p}'".format(p=prefix)])
anticomp = _call(["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)])
completion = list(set(completion) - set(anticomp))
if self.directories:
completion += [f + "/" for f in anticomp]
return completion
class _FilteredFilesCompleter(object):
def __init__(self, predicate):
"""
Create the completer
A predicate accepts as its only argument a candidate path and either
accepts it or rejects it.
"""
assert predicate, "Expected a callable predicate"
self.predicate = predicate
def __call__(self, prefix, **kwargs):
"""
Provide completions on prefix
"""
target_dir = os.path.dirname(prefix)
try:
names = os.listdir(target_dir or ".")
except:
return # empty iterator
incomplete_part = os.path.basename(prefix)
# Iterate on target_dir entries and filter on given predicate
for name in names:
if not name.startswith(incomplete_part):
continue
candidate = os.path.join(target_dir, name)
if not self.predicate(candidate):
continue
yield candidate + "/" if os.path.isdir(candidate) else candidate
class DirectoriesCompleter(_FilteredFilesCompleter):
def __init__(self):
_FilteredFilesCompleter.__init__(self, predicate=os.path.isdir)
class SuppressCompleter(object):
"""
A completer used to suppress the completion of specific arguments
"""
def __init__(self):
pass
def suppress(self):
"""
Decide if the completion should be suppressed
"""
return True

View File

@@ -0,0 +1,331 @@
# Copyright 2012-2013, Andrey Kislyuk and argcomplete contributors.
# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.
from argparse import ArgumentParser, ArgumentError, SUPPRESS, _SubParsersAction
from argparse import OPTIONAL, ZERO_OR_MORE, ONE_OR_MORE, REMAINDER, PARSER
from argparse import _get_action_name, _
_num_consumed_args = {}
def action_is_satisfied(action):
''' Returns False if the parse would raise an error if no more arguments are given to this action, True otherwise.
'''
num_consumed_args = _num_consumed_args.get(action, 0)
if action.nargs in [OPTIONAL, ZERO_OR_MORE, REMAINDER]:
return True
if action.nargs == ONE_OR_MORE:
return num_consumed_args >= 1
if action.nargs == PARSER:
# Not sure what this should be, but this previously always returned False
# so at least this won't break anything that wasn't already broken.
return False
if action.nargs is None:
return num_consumed_args == 1
assert isinstance(action.nargs, int), 'failed to handle a possible nargs value: %r' % action.nargs
return num_consumed_args == action.nargs
def action_is_open(action):
''' Returns True if action could consume more arguments (i.e., its pattern is open).
'''
num_consumed_args = _num_consumed_args.get(action, 0)
if action.nargs in [ZERO_OR_MORE, ONE_OR_MORE, PARSER, REMAINDER]:
return True
if action.nargs == OPTIONAL or action.nargs is None:
return num_consumed_args == 0
assert isinstance(action.nargs, int), 'failed to handle a possible nargs value: %r' % action.nargs
return num_consumed_args < action.nargs
def action_is_greedy(action, isoptional=False):
''' Returns True if action will necessarily consume the next argument.
isoptional indicates whether the argument is an optional (starts with -).
'''
num_consumed_args = _num_consumed_args.get(action, 0)
if action.option_strings:
if not isoptional and not action_is_satisfied(action):
return True
return action.nargs == REMAINDER
else:
return action.nargs == REMAINDER and num_consumed_args >= 1
class IntrospectiveArgumentParser(ArgumentParser):
''' The following is a verbatim copy of ArgumentParser._parse_known_args (Python 2.7.3),
except for the lines that contain the string "Added by argcomplete".
'''
def _parse_known_args(self, arg_strings, namespace):
_num_consumed_args.clear() # Added by argcomplete
self._argcomplete_namespace = namespace
self.active_actions = [] # Added by argcomplete
# replace arg strings that are file references
if self.fromfile_prefix_chars is not None:
arg_strings = self._read_args_from_files(arg_strings)
# map all mutually exclusive arguments to the other arguments
# they can't occur with
action_conflicts = {}
self._action_conflicts = action_conflicts # Added by argcomplete
for mutex_group in self._mutually_exclusive_groups:
group_actions = mutex_group._group_actions
for i, mutex_action in enumerate(mutex_group._group_actions):
conflicts = action_conflicts.setdefault(mutex_action, [])
conflicts.extend(group_actions[:i])
conflicts.extend(group_actions[i + 1:])
# find all option indices, and determine the arg_string_pattern
# which has an 'O' if there is an option at an index,
# an 'A' if there is an argument, or a '-' if there is a '--'
option_string_indices = {}
arg_string_pattern_parts = []
arg_strings_iter = iter(arg_strings)
for i, arg_string in enumerate(arg_strings_iter):
# all args after -- are non-options
if arg_string == '--':
arg_string_pattern_parts.append('-')
for arg_string in arg_strings_iter:
arg_string_pattern_parts.append('A')
# otherwise, add the arg to the arg strings
# and note the index if it was an option
else:
option_tuple = self._parse_optional(arg_string)
if option_tuple is None:
pattern = 'A'
else:
option_string_indices[i] = option_tuple
pattern = 'O'
arg_string_pattern_parts.append(pattern)
# join the pieces together to form the pattern
arg_strings_pattern = ''.join(arg_string_pattern_parts)
# converts arg strings to the appropriate and then takes the action
seen_actions = set()
seen_non_default_actions = set()
self._seen_non_default_actions = seen_non_default_actions # Added by argcomplete
def take_action(action, argument_strings, option_string=None):
seen_actions.add(action)
argument_values = self._get_values(action, argument_strings)
# error if this argument is not allowed with other previously
# seen arguments, assuming that actions that use the default
# value don't really count as "present"
if argument_values is not action.default:
seen_non_default_actions.add(action)
for conflict_action in action_conflicts.get(action, []):
if conflict_action in seen_non_default_actions:
msg = _('not allowed with argument %s')
action_name = _get_action_name(conflict_action)
raise ArgumentError(action, msg % action_name)
# take the action if we didn't receive a SUPPRESS value
# (e.g. from a default)
if argument_values is not SUPPRESS \
or isinstance(action, _SubParsersAction):
try:
action(self, namespace, argument_values, option_string)
except:
# Begin added by argcomplete
# When a subparser action is taken and fails due to incomplete arguments, it does not merge the
# contents of its parsed namespace into the parent namespace. Do that here to allow completers to
# access the partially parsed arguments for the subparser.
if isinstance(action, _SubParsersAction):
subnamespace = action._name_parser_map[argument_values[0]]._argcomplete_namespace
for key, value in vars(subnamespace).items():
setattr(namespace, key, value)
# End added by argcomplete
raise
# function to convert arg_strings into an optional action
def consume_optional(start_index):
# get the optional identified at this index
option_tuple = option_string_indices[start_index]
action, option_string, explicit_arg = option_tuple
# identify additional optionals in the same arg string
# (e.g. -xyz is the same as -x -y -z if no args are required)
match_argument = self._match_argument
action_tuples = []
while True:
# if we found no optional action, skip it
if action is None:
extras.append(arg_strings[start_index])
return start_index + 1
# if there is an explicit argument, try to match the
# optional's string arguments to only this
if explicit_arg is not None:
arg_count = match_argument(action, 'A')
# if the action is a single-dash option and takes no
# arguments, try to parse more single-dash options out
# of the tail of the option string
chars = self.prefix_chars
if arg_count == 0 and option_string[1] not in chars:
action_tuples.append((action, [], option_string))
char = option_string[0]
option_string = char + explicit_arg[0]
new_explicit_arg = explicit_arg[1:] or None
optionals_map = self._option_string_actions
if option_string in optionals_map:
action = optionals_map[option_string]
explicit_arg = new_explicit_arg
else:
msg = _('ignored explicit argument %r')
raise ArgumentError(action, msg % explicit_arg)
# if the action expect exactly one argument, we've
# successfully matched the option; exit the loop
elif arg_count == 1:
stop = start_index + 1
args = [explicit_arg]
action_tuples.append((action, args, option_string))
break
# error if a double-dash option did not use the
# explicit argument
else:
msg = _('ignored explicit argument %r')
raise ArgumentError(action, msg % explicit_arg)
# if there is no explicit argument, try to match the
# optional's string arguments with the following strings
# if successful, exit the loop
else:
start = start_index + 1
selected_patterns = arg_strings_pattern[start:]
self.active_actions = [action] # Added by argcomplete
_num_consumed_args[action] = 0 # Added by argcomplete
arg_count = match_argument(action, selected_patterns)
stop = start + arg_count
args = arg_strings[start:stop]
# Begin added by argcomplete
# If the pattern is not open (e.g. no + at the end), remove the action from active actions (since
# it wouldn't be able to consume any more args)
_num_consumed_args[action] = len(args)
if not action_is_open(action):
self.active_actions.remove(action)
# End added by argcomplete
action_tuples.append((action, args, option_string))
break
# add the Optional to the list and return the index at which
# the Optional's string args stopped
assert action_tuples
for action, args, option_string in action_tuples:
take_action(action, args, option_string)
return stop
# the list of Positionals left to be parsed; this is modified
# by consume_positionals()
positionals = self._get_positional_actions()
# function to convert arg_strings into positional actions
def consume_positionals(start_index):
# match as many Positionals as possible
match_partial = self._match_arguments_partial
selected_pattern = arg_strings_pattern[start_index:]
arg_counts = match_partial(positionals, selected_pattern)
# slice off the appropriate arg strings for each Positional
# and add the Positional and its args to the list
for action, arg_count in zip(positionals, arg_counts): # Added by argcomplete
self.active_actions.append(action) # Added by argcomplete
for action, arg_count in zip(positionals, arg_counts):
args = arg_strings[start_index: start_index + arg_count]
start_index += arg_count
_num_consumed_args[action] = len(args) # Added by argcomplete
take_action(action, args)
# slice off the Positionals that we just parsed and return the
# index at which the Positionals' string args stopped
positionals[:] = positionals[len(arg_counts):]
return start_index
# consume Positionals and Optionals alternately, until we have
# passed the last option string
extras = []
start_index = 0
if option_string_indices:
max_option_string_index = max(option_string_indices)
else:
max_option_string_index = -1
while start_index <= max_option_string_index:
# consume any Positionals preceding the next option
next_option_string_index = min([
index
for index in option_string_indices
if index >= start_index])
if start_index != next_option_string_index:
positionals_end_index = consume_positionals(start_index)
# only try to parse the next optional if we didn't consume
# the option string during the positionals parsing
if positionals_end_index > start_index:
start_index = positionals_end_index
continue
else:
start_index = positionals_end_index
# if we consumed all the positionals we could and we're not
# at the index of an option string, there were extra arguments
if start_index not in option_string_indices:
strings = arg_strings[start_index:next_option_string_index]
extras.extend(strings)
start_index = next_option_string_index
# consume the next optional and any arguments for it
start_index = consume_optional(start_index)
# consume any positionals following the last Optional
stop_index = consume_positionals(start_index)
# if we didn't consume all the argument strings, there were extras
extras.extend(arg_strings[stop_index:])
# if we didn't use all the Positional objects, there were too few
# arg strings supplied.
if positionals:
self.active_actions.append(positionals[0]) # Added by argcomplete
self.error(_('too few arguments'))
# make sure all required actions were present
for action in self._actions:
if action.required:
if action not in seen_actions:
name = _get_action_name(action)
self.error(_('argument %s is required') % name)
# make sure all required groups had one option present
for group in self._mutually_exclusive_groups:
if group.required:
for action in group._group_actions:
if action in seen_non_default_actions:
break
# if no actions were used, report the error
else:
names = [_get_action_name(action)
for action in group._group_actions
if action.help is not SUPPRESS]
msg = _('one of the arguments %s is required')
self.error(msg % ' '.join(names))
# return the updated namespace and the extra arguments
return namespace, extras

View File

@@ -0,0 +1,328 @@
# -*- coding: utf-8 -*-
# This copy of shlex.py from Python 3.6 is distributed with argcomplete.
# It contains only the shlex class, with modifications as noted.
"""A lexical analyzer class for simple shell-like syntaxes."""
# Module and documentation by Eric S. Raymond, 21 Dec 1998
# Input stacking and error message cleanup added by ESR, March 2000
# push_source() and pop_source() made explicit by ESR, January 2001.
# Posix compliance, split(), string arguments, and
# iterator interface by Gustavo Niemeyer, April 2003.
# changes to tokenize more like Posix shells by Vinay Sajip, July 2016.
import os
import sys
from collections import deque
# Modified by argcomplete: 2/3 compatibility
# Note: cStringIO is not compatible with Unicode
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
# Modified by argcomplete: 2/3 compatibility
try:
basestring
except NameError:
basestring = str
class shlex:
"A lexical analyzer class for simple shell-like syntaxes."
def __init__(self, instream=None, infile=None, posix=False,
punctuation_chars=False):
# Modified by argcomplete: 2/3 compatibility
if isinstance(instream, basestring):
instream = StringIO(instream)
if instream is not None:
self.instream = instream
self.infile = infile
else:
self.instream = sys.stdin
self.infile = None
self.posix = posix
if posix:
self.eof = None
else:
self.eof = ''
self.commenters = '#'
self.wordchars = ('abcdfeghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_')
# Modified by argcomplete: 2/3 compatibility
# if self.posix:
# self.wordchars += ('ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ'
# 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ')
self.whitespace = ' \t\r\n'
self.whitespace_split = False
self.quotes = '\'"'
self.escape = '\\'
self.escapedquotes = '"'
self.state = ' '
self.pushback = deque()
self.lineno = 1
self.debug = 0
self.token = ''
self.filestack = deque()
self.source = None
if not punctuation_chars:
punctuation_chars = ''
elif punctuation_chars is True:
punctuation_chars = '();<>|&'
self.punctuation_chars = punctuation_chars
if punctuation_chars:
# _pushback_chars is a push back queue used by lookahead logic
self._pushback_chars = deque()
# these chars added because allowed in file names, args, wildcards
self.wordchars += '~-./*?='
#remove any punctuation chars from wordchars
t = self.wordchars.maketrans(dict.fromkeys(punctuation_chars))
self.wordchars = self.wordchars.translate(t)
# Modified by argcomplete: Record last wordbreak position
self.last_wordbreak_pos = None
self.wordbreaks = ''
def push_token(self, tok):
"Push a token onto the stack popped by the get_token method"
if self.debug >= 1:
print("shlex: pushing token " + repr(tok))
self.pushback.appendleft(tok)
def push_source(self, newstream, newfile=None):
"Push an input source onto the lexer's input source stack."
# Modified by argcomplete: 2/3 compatibility
if isinstance(newstream, basestring):
newstream = StringIO(newstream)
self.filestack.appendleft((self.infile, self.instream, self.lineno))
self.infile = newfile
self.instream = newstream
self.lineno = 1
if self.debug:
if newfile is not None:
print('shlex: pushing to file %s' % (self.infile,))
else:
print('shlex: pushing to stream %s' % (self.instream,))
def pop_source(self):
"Pop the input source stack."
self.instream.close()
(self.infile, self.instream, self.lineno) = self.filestack.popleft()
if self.debug:
print('shlex: popping to %s, line %d' \
% (self.instream, self.lineno))
self.state = ' '
def get_token(self):
"Get a token from the input stream (or from stack if it's nonempty)"
if self.pushback:
tok = self.pushback.popleft()
if self.debug >= 1:
print("shlex: popping token " + repr(tok))
return tok
# No pushback. Get a token.
raw = self.read_token()
# Handle inclusions
if self.source is not None:
while raw == self.source:
spec = self.sourcehook(self.read_token())
if spec:
(newfile, newstream) = spec
self.push_source(newstream, newfile)
raw = self.get_token()
# Maybe we got EOF instead?
while raw == self.eof:
if not self.filestack:
return self.eof
else:
self.pop_source()
raw = self.get_token()
# Neither inclusion nor EOF
if self.debug >= 1:
if raw != self.eof:
print("shlex: token=" + repr(raw))
else:
print("shlex: token=EOF")
return raw
def read_token(self):
quoted = False
escapedstate = ' '
while True:
if self.punctuation_chars and self._pushback_chars:
nextchar = self._pushback_chars.pop()
else:
nextchar = self.instream.read(1)
if nextchar == '\n':
self.lineno += 1
if self.debug >= 3:
print("shlex: in state %r I see character: %r" % (self.state,
nextchar))
if self.state is None:
self.token = '' # past end of file
break
elif self.state == ' ':
if not nextchar:
self.state = None # end of file
break
elif nextchar in self.whitespace:
if self.debug >= 2:
print("shlex: I see whitespace in whitespace state")
if self.token or (self.posix and quoted):
break # emit current token
else:
continue
elif nextchar in self.commenters:
self.instream.readline()
self.lineno += 1
elif self.posix and nextchar in self.escape:
escapedstate = 'a'
self.state = nextchar
elif nextchar in self.wordchars:
self.token = nextchar
self.state = 'a'
elif nextchar in self.punctuation_chars:
self.token = nextchar
self.state = 'c'
elif nextchar in self.quotes:
if not self.posix:
self.token = nextchar
self.state = nextchar
elif self.whitespace_split:
self.token = nextchar
self.state = 'a'
else:
self.token = nextchar
if self.token or (self.posix and quoted):
break # emit current token
else:
continue
elif self.state in self.quotes:
quoted = True
if not nextchar: # end of file
if self.debug >= 2:
print("shlex: I see EOF in quotes state")
# XXX what error should be raised here?
raise ValueError("No closing quotation")
if nextchar == self.state:
if not self.posix:
self.token += nextchar
self.state = ' '
break
else:
self.state = 'a'
elif (self.posix and nextchar in self.escape and self.state
in self.escapedquotes):
escapedstate = self.state
self.state = nextchar
else:
self.token += nextchar
elif self.state in self.escape:
if not nextchar: # end of file
if self.debug >= 2:
print("shlex: I see EOF in escape state")
# XXX what error should be raised here?
raise ValueError("No escaped character")
# In posix shells, only the quote itself or the escape
# character may be escaped within quotes.
if (escapedstate in self.quotes and
nextchar != self.state and nextchar != escapedstate):
self.token += self.state
self.token += nextchar
self.state = escapedstate
elif self.state in ('a', 'c'):
if not nextchar:
self.state = None # end of file
break
elif nextchar in self.whitespace:
if self.debug >= 2:
print("shlex: I see whitespace in word state")
self.state = ' '
if self.token or (self.posix and quoted):
break # emit current token
else:
continue
elif nextchar in self.commenters:
self.instream.readline()
self.lineno += 1
if self.posix:
self.state = ' '
if self.token or (self.posix and quoted):
break # emit current token
else:
continue
elif self.posix and nextchar in self.quotes:
self.state = nextchar
elif self.posix and nextchar in self.escape:
escapedstate = 'a'
self.state = nextchar
elif self.state == 'c':
if nextchar in self.punctuation_chars:
self.token += nextchar
else:
if nextchar not in self.whitespace:
self._pushback_chars.append(nextchar)
self.state = ' '
break
elif (nextchar in self.wordchars or nextchar in self.quotes
or self.whitespace_split):
self.token += nextchar
# Modified by argcomplete: Record last wordbreak position
if nextchar in self.wordbreaks:
self.last_wordbreak_pos = len(self.token) - 1
else:
if self.punctuation_chars:
self._pushback_chars.append(nextchar)
else:
self.pushback.appendleft(nextchar)
if self.debug >= 2:
print("shlex: I see punctuation in word state")
self.state = ' '
if self.token or (self.posix and quoted):
break # emit current token
else:
continue
result = self.token
self.token = ''
if self.posix and not quoted and result == '':
result = None
if self.debug > 1:
if result:
print("shlex: raw token=" + repr(result))
else:
print("shlex: raw token=EOF")
# Modified by argcomplete: Record last wordbreak position
if self.state == ' ':
self.last_wordbreak_pos = None
return result
def sourcehook(self, newfile):
"Hook called on a filename to be sourced."
if newfile[0] == '"':
newfile = newfile[1:-1]
# This implements cpp-like semantics for relative-path inclusion.
# Modified by argcomplete: 2/3 compatibility
if isinstance(self.infile, basestring) and not os.path.isabs(newfile):
newfile = os.path.join(os.path.dirname(self.infile), newfile)
return (newfile, open(newfile, "r"))
def error_leader(self, infile=None, lineno=None):
"Emit a C-compiler-like, Emacs-friendly error-message leader."
if infile is None:
infile = self.infile
if lineno is None:
lineno = self.lineno
return "\"%s\", line %d: " % (infile, lineno)
def __iter__(self):
return self
def __next__(self):
token = self.get_token()
if token == self.eof:
raise StopIteration
return token
# Modified by argcomplete: 2/3 compatibility
next = __next__

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env python
# PYTHON_ARGCOMPLETE_OK
# Copyright 2012-2013, Andrey Kislyuk and argcomplete contributors.
# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.
'''
Activate the generic bash-completion script for the argcomplete module.
'''
import os, sys, argparse, argcomplete, shutil, fileinput
parser = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
dest_opt = parser.add_argument("--dest", default="/etc/bash_completion.d",
help="Specify the bash completion modules directory to install into")
parser.add_argument("--user", help="Install into user directory (~/.bash_completion.d/)", action='store_true')
parser.add_argument("--no-defaults", dest="use_defaults", action="store_false", default=True,
help="When no matches are generated, do not fallback to readline\'s default completion")
parser.add_argument("--complete-arguments", nargs=argparse.REMAINDER,
help="arguments to call complete with; use of this option discards default options")
argcomplete.autocomplete(parser)
args = parser.parse_args()
if args.user:
args.dest = os.path.expanduser("~/.bash_completion.d/")
if not os.path.exists(args.dest):
try:
os.mkdir(args.dest)
except Exception as e:
parser.error("Path {d} does not exist and could not be created: {e}".format(d=args.dest, e=e))
elif not os.path.exists(args.dest) and args.dest != '-':
if sys.platform == 'darwin' and args.dest == dest_opt.default and os.path.exists("/usr/local" + dest_opt.default):
args.dest = "/usr/local" + dest_opt.default
else:
parser.error("Path {d} does not exist".format(d=args.dest))
activator = os.path.join(os.path.dirname(argcomplete.__file__), 'bash_completion.d', 'python-argcomplete.sh')
if args.complete_arguments is None:
complete_options = '-o default -o bashdefault' if args.use_defaults else '-o bashdefault'
else:
complete_options = " ".join(args.complete_arguments)
complete_call = "complete{} -D -F _python_argcomplete_global".format(" " + complete_options if complete_options else "")
def replaceCompleteCall(line):
if line.startswith("complete") and "_python_argcomplete_global" in line:
return complete_call + ('\n' if line.endswith('\n') else '')
else:
return line
if args.dest == '-':
for l in open(activator):
sys.stdout.write(replaceCompleteCall(l))
else:
dest = os.path.join(args.dest, "python-argcomplete.sh")
sys.stdout.write("Installing bash completion script " + dest)
if not args.use_defaults:
sys.stdout.write(" without -o default")
elif args.complete_arguments:
sys.stdout.write(" with options: " + complete_options)
sys.stdout.write("\n")
try:
shutil.copy(activator, dest)
if not args.complete_arguments is None or not args.use_defaults:
for l in fileinput.input(dest, inplace=True):
# fileinput with inplace=True redirects stdout to the edited file
sys.stdout.write(replaceCompleteCall(l))
except Exception as e:
err = str(e)
if args.dest == dest_opt.default:
err += "\nPlease try --user to install into a user directory, or --dest to specify the bash completion modules directory"
parser.error(err)

View File

@@ -0,0 +1,55 @@
#!/usr/bin/env python
# Copyright 2012-2013, Andrey Kislyuk and argcomplete contributors.
# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.
'''
This script is part of the Python argcomplete package (https://github.com/kislyuk/argcomplete).
It is used to check if an EASY-INSTALL-SCRIPT wrapper redirects to a script that contains the string
"PYTHON_ARGCOMPLETE_OK". If you have enabled global completion in argcomplete, the completion hook will run it every
time you press <TAB> in your shell.
Usage:
python-argcomplete-check-easy-install-script <input executable file>
'''
import sys
if len(sys.argv) != 2:
sys.exit(__doc__)
sys.tracebacklimit = 0
with open(sys.argv[1]) as fh:
line1, head = fh.read(1024).split("\n", 1)[:2]
if line1.startswith('#') and ('py' in line1 or 'Py' in line1):
import re
lines = head.split("\n", 12)
for line in lines:
if line.startswith("# EASY-INSTALL-SCRIPT"):
import pkg_resources
dist, script = re.match("# EASY-INSTALL-SCRIPT: '(.+)','(.+)'", line).groups()
if "PYTHON_ARGCOMPLETE_OK" in pkg_resources.get_distribution(dist).get_metadata('scripts/' + script):
exit(0)
elif line.startswith("# EASY-INSTALL-ENTRY-SCRIPT"):
dist, group, name = re.match("# EASY-INSTALL-ENTRY-SCRIPT: '(.+)','(.+)','(.+)'", line).groups()
import pkg_resources, pkgutil
module_name = pkg_resources.get_distribution(dist).get_entry_info(group, name).module_name
with open(pkgutil.get_loader(module_name).get_filename()) as mod_fh:
if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024):
exit(0)
elif line.startswith("# EASY-INSTALL-DEV-SCRIPT"):
for line2 in lines:
if line2.startswith('__file__'):
filename = re.match("__file__ = '(.+)'", line2).group(1)
with open(filename) as mod_fh:
if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024):
exit(0)
elif line.startswith("# PBR Generated"):
module = re.search("from (.*) import", head).groups()[0]
import pkg_resources, pkgutil
with open(pkgutil.get_loader(module).get_filename()) as mod_fh:
if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024):
exit(0)
exit(1)

View File

@@ -0,0 +1,23 @@
#!/bin/sh
IFS=
export IFS
COMP_WORDBREAKS=
export COMP_WORDBREAKS
COMP_TYPE=
export COMP_TYPE
COMP_LINE=${COMMAND_LINE}
export COMP_LINE
COMP_POINT=${#COMMAND_LINE}
export COMP_POINT
_ARGCOMPLETE=1
export _ARGCOMPLETE
_ARGCOMPLETE_SHELL=tcsh
export _ARGCOMPLETE_SHELL
"$1" 8>&1 9>&2 1>/dev/null 2>/dev/null

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python
# PYTHON_ARGCOMPLETE_OK
# Copyright 2012-2013, Andrey Kislyuk and argcomplete contributors.
# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.
'''
Register a Python executable for use with the argcomplete module.
To perform the registration, source the output of this script in your bash shell (quote the output to avoid interpolation).
Example:
$ eval "$(register-python-argcomplete my-favorite-script.py)"
For Tcsh
$ eval `register-python-argcomplete --shell tcsh my-favorite-script.py`
'''
import sys
import argparse
import argcomplete
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
'--no-defaults',
dest='use_defaults', action='store_false', default=True,
help='When no matches are generated, do not fallback to readline\'s default completion')
parser.add_argument(
'--complete-arguments',
nargs=argparse.REMAINDER,
help='arguments to call complete with; use of this option discards default options')
parser.add_argument(
'-s', '--shell',
choices=('bash', 'tcsh'), default='bash',
help='output code for the specified shell')
parser.add_argument(
'executable',
help='executable to completed (when invoked by exactly this name)')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
sys.stdout.write(argcomplete.shellcode(
args.executable, args.use_defaults, args.shell, args.complete_arguments))

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env python
bashcode = r'''
# Run something, muting output or redirecting it to the debug stream
# depending on the value of _ARC_DEBUG.
__python_argcomplete_run() {
if [[ -z "$_ARC_DEBUG" ]]; then
"$@" 8>&1 9>&2 1>/dev/null 2>&1
else
"$@" 8>&1 9>&2 1>&9 2>&1
fi
}
_python_argcomplete() {
local IFS=$'\013'
local SUPPRESS_SPACE=0
if compopt +o nospace 2> /dev/null; then
SUPPRESS_SPACE=1
fi
COMPREPLY=( $(IFS="$IFS" \
COMP_LINE="$COMP_LINE" \
COMP_POINT="$COMP_POINT" \
COMP_TYPE="$COMP_TYPE" \
_ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS" \
_ARGCOMPLETE=1 \
_ARGCOMPLETE_SUPPRESS_SPACE=$SUPPRESS_SPACE \
__python_argcomplete_run "$1") )
if [[ $? != 0 ]]; then
unset COMPREPLY
elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "$COMPREPLY" =~ [=/:]$ ]]; then
compopt -o nospace
fi
}
complete %(complete_opts)s -F _python_argcomplete "%(executable)s"
'''
tcshcode = '''\
complete "%(executable)s" 'p@*@`python-argcomplete-tcsh "%(executable)s"`@'
'''
def shellcode(executable, use_defaults=True, shell='bash', complete_arguments=None):
'''
Provide the shell code required to register a python executable for use with the argcomplete module.
:param str executable: Executable to be completed (when invoked exactly with this name
:param bool use_defaults: Whether to fallback to readline's default completion when no matches are generated.
:param str shell: Name of the shell to output code for (bash or tcsh)
:param complete_arguments: Arguments to call complete with
:type complete_arguments: list(str) or None
'''
if complete_arguments is None:
complete_options = '-o nospace -o default' if use_defaults else '-o nospace'
else:
complete_options = " ".join(complete_arguments)
if shell == 'bash':
code = bashcode
else:
code = tcshcode
return code % dict(complete_opts=complete_options, executable=executable)