argcomplete 3.6.2__tar.gz → 3.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. {argcomplete-3.6.2 → argcomplete-3.7.0}/.github/workflows/ci.yml +10 -10
  2. {argcomplete-3.6.2 → argcomplete-3.7.0}/Changes.rst +20 -0
  3. {argcomplete-3.6.2 → argcomplete-3.7.0}/PKG-INFO +3 -3
  4. {argcomplete-3.6.2 → argcomplete-3.7.0}/README.rst +1 -1
  5. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/bash_completion.d/_python-argcomplete +3 -5
  6. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/completers.py +7 -4
  7. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/finders.py +14 -4
  8. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/packages/_argparse.py +16 -8
  9. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/scripts/activate_global_python_argcomplete.py +3 -1
  10. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/shell_integration.py +9 -9
  11. {argcomplete-3.6.2 → argcomplete-3.7.0}/contrib/README.rst +8 -0
  12. {argcomplete-3.6.2 → argcomplete-3.7.0}/pyproject.toml +1 -2
  13. {argcomplete-3.6.2 → argcomplete-3.7.0}/test/test.py +14 -0
  14. argcomplete-3.6.2/argcomplete/scripts/python_argcomplete_check_easy_install_script.py +0 -84
  15. {argcomplete-3.6.2 → argcomplete-3.7.0}/.github/FUNDING.yml +0 -0
  16. {argcomplete-3.6.2 → argcomplete-3.7.0}/.github/workflows/release.yml +0 -0
  17. {argcomplete-3.6.2 → argcomplete-3.7.0}/.gitignore +0 -0
  18. {argcomplete-3.6.2 → argcomplete-3.7.0}/Authors.rst +0 -0
  19. {argcomplete-3.6.2 → argcomplete-3.7.0}/LICENSE.rst +0 -0
  20. {argcomplete-3.6.2 → argcomplete-3.7.0}/MANIFEST.in +0 -0
  21. {argcomplete-3.6.2 → argcomplete-3.7.0}/Makefile +0 -0
  22. {argcomplete-3.6.2 → argcomplete-3.7.0}/NOTICE +0 -0
  23. {argcomplete-3.6.2 → argcomplete-3.7.0}/SECURITY.md +0 -0
  24. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/__init__.py +0 -0
  25. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/_check_console_script.py +0 -0
  26. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/_check_module.py +0 -0
  27. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/exceptions.py +0 -0
  28. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/io.py +0 -0
  29. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/lexers.py +0 -0
  30. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/packages/__init__.py +0 -0
  31. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/packages/_shlex.py +0 -0
  32. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/py.typed +0 -0
  33. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/scripts/__init__.py +0 -0
  34. {argcomplete-3.6.2 → argcomplete-3.7.0}/argcomplete/scripts/register_python_argcomplete.py +0 -0
  35. {argcomplete-3.6.2 → argcomplete-3.7.0}/common.mk +0 -0
  36. {argcomplete-3.6.2 → argcomplete-3.7.0}/docs/changelog.rst +0 -0
  37. {argcomplete-3.6.2 → argcomplete-3.7.0}/docs/conf.py +0 -0
  38. {argcomplete-3.6.2 → argcomplete-3.7.0}/docs/examples/describe_github_user.py +0 -0
  39. {argcomplete-3.6.2 → argcomplete-3.7.0}/docs/fish_help_string.png +0 -0
  40. {argcomplete-3.6.2 → argcomplete-3.7.0}/docs/index.rst +0 -0
  41. {argcomplete-3.6.2 → argcomplete-3.7.0}/docs/toc.html +0 -0
  42. {argcomplete-3.6.2 → argcomplete-3.7.0}/setup.cfg +0 -0
  43. {argcomplete-3.6.2 → argcomplete-3.7.0}/test/__init__.py +0 -0
  44. {argcomplete-3.6.2 → argcomplete-3.7.0}/test/inputrc +0 -0
  45. {argcomplete-3.6.2 → argcomplete-3.7.0}/test/prog +0 -0
  46. {argcomplete-3.6.2 → argcomplete-3.7.0}/test/stuck +0 -0
  47. {argcomplete-3.6.2 → argcomplete-3.7.0}/test/test_contrib_shells.py +0 -0
  48. {argcomplete-3.6.2 → argcomplete-3.7.0}/test/test_package/__init__.py +0 -0
  49. {argcomplete-3.6.2 → argcomplete-3.7.0}/test/test_package/setup.py +0 -0
  50. {argcomplete-3.6.2 → argcomplete-3.7.0}/test/test_package/test_module.py +0 -0
  51. {argcomplete-3.6.2 → argcomplete-3.7.0}/test/test_package/test_package/__init__.py +0 -0
@@ -3,17 +3,17 @@ name: Python package
3
3
  on: [push, pull_request]
4
4
 
5
5
  jobs:
6
- build:
6
+ ci:
7
7
  runs-on: ${{matrix.os}}
8
8
  strategy:
9
9
  fail-fast: false
10
10
  matrix:
11
- os: [ubuntu-20.04, ubuntu-latest, macos-13, macos-latest]
12
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
11
+ os: [ubuntu-22.04, ubuntu-latest, macos-14, macos-latest]
12
+ python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
13
13
 
14
14
  steps:
15
- - uses: actions/checkout@v4
16
- - uses: actions/setup-python@v4
15
+ - uses: actions/checkout@v7
16
+ - uses: actions/setup-python@v6
17
17
  with:
18
18
  python-version: ${{matrix.python-version}}
19
19
  allow-prereleases: true
@@ -31,15 +31,15 @@ jobs:
31
31
  - uses: codecov/codecov-action@v5
32
32
  if: ${{matrix.python-version == '3.12' && matrix.os == 'ubuntu-22.04'}}
33
33
  isort:
34
- runs-on: ubuntu-22.04
34
+ runs-on: ubuntu-24.04
35
35
  steps:
36
- - uses: actions/checkout@v4
36
+ - uses: actions/checkout@v7
37
37
  - uses: isort/isort-action@v1.1.0
38
38
  ruff:
39
39
  runs-on: ubuntu-latest
40
40
  steps:
41
- - uses: actions/checkout@v4
42
- - uses: astral-sh/ruff-action@v1
43
- - uses: astral-sh/ruff-action@v1
41
+ - uses: actions/checkout@v7
42
+ - uses: astral-sh/ruff-action@v4.0.0
43
+ - uses: astral-sh/ruff-action@v4.0.0
44
44
  with:
45
45
  args: "format --check"
@@ -1,3 +1,23 @@
1
+ Changes for v3.7.0 (2026-06-30)
2
+ ===============================
3
+
4
+ - Escape glob and brace metacharacters in quote_completions (#552)
5
+ - Quote prefix passed to compgen in FilesCompleter (#551)
6
+ - Remove deprecated easy_install script detection
7
+ - Type hinting improvements
8
+
9
+ Changes for v3.6.3 (2025-10-19)
10
+ ===============================
11
+
12
+ - Make RE PCRE compatible. Fixes #539
13
+
14
+ - Only execute Python interpreters (#536)
15
+
16
+ - fish: set variable scope to local to avoid clobbering global or
17
+ universal variables (#534)
18
+
19
+ - Documentation and help improvements
20
+
1
21
  Changes for v3.6.2 (2025-04-02)
2
22
  ===============================
3
23
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: argcomplete
3
- Version: 3.6.2
3
+ Version: 3.7.0
4
4
  Summary: Bash tab completion for argparse
5
5
  Project-URL: Homepage, https://github.com/kislyuk/argcomplete
6
6
  Project-URL: Documentation, https://kislyuk.github.io/argcomplete
@@ -22,12 +22,12 @@ Classifier: Operating System :: MacOS :: MacOS X
22
22
  Classifier: Operating System :: POSIX
23
23
  Classifier: Programming Language :: Python
24
24
  Classifier: Programming Language :: Python :: 3
25
- Classifier: Programming Language :: Python :: 3.8
26
25
  Classifier: Programming Language :: Python :: 3.9
27
26
  Classifier: Programming Language :: Python :: 3.10
28
27
  Classifier: Programming Language :: Python :: 3.11
29
28
  Classifier: Programming Language :: Python :: 3.12
30
29
  Classifier: Programming Language :: Python :: 3.13
30
+ Classifier: Programming Language :: Python :: 3.14
31
31
  Classifier: Programming Language :: Python :: Implementation :: CPython
32
32
  Classifier: Programming Language :: Python :: Implementation :: PyPy
33
33
  Classifier: Topic :: Software Development
@@ -51,7 +51,7 @@ Argcomplete provides easy, extensible command line tab completion of arguments f
51
51
 
52
52
  It makes two assumptions:
53
53
 
54
- * You're using bash or zsh as your shell
54
+ * You're using bash or zsh as your shell (limited support exists for other shells - see below)
55
55
  * You're using `argparse <http://docs.python.org/3/library/argparse.html>`_ to manage your command line arguments/options
56
56
 
57
57
  Argcomplete is particularly useful if your program has lots of options or subparsers, and if your program can
@@ -6,7 +6,7 @@ Argcomplete provides easy, extensible command line tab completion of arguments f
6
6
 
7
7
  It makes two assumptions:
8
8
 
9
- * You're using bash or zsh as your shell
9
+ * You're using bash or zsh as your shell (limited support exists for other shells - see below)
10
10
  * You're using `argparse <http://docs.python.org/3/library/argparse.html>`_ to manage your command line arguments/options
11
11
 
12
12
  Argcomplete is particularly useful if your program has lots of options or subparsers, and if your program can
@@ -190,10 +190,8 @@ _python_argcomplete_global() {
190
190
  interpreter=($interpreter)
191
191
  fi
192
192
 
193
- if (__python_argcomplete_scan_head_noerr "$SCRIPT_NAME" easy_install \
194
- && "${interpreter[@]}" "$(__python_argcomplete_which python-argcomplete-check-easy-install-script)" "$SCRIPT_NAME") >/dev/null 2>&1; then
195
- ARGCOMPLETE=1
196
- elif __python_argcomplete_run "${interpreter[@]}" -m argcomplete._check_console_script "$SCRIPT_NAME"; then
193
+ if ([[ "${interpreter[@]}" == *python* ]] || [[ "${interpreter[@]}" == *pypy* ]])\
194
+ && __python_argcomplete_run "${interpreter[@]}" -m argcomplete._check_console_script "$SCRIPT_NAME"; then
197
195
  ARGCOMPLETE=1
198
196
  fi
199
197
  fi
@@ -215,7 +213,7 @@ _python_argcomplete_global() {
215
213
  if is-at-least 5.8; then
216
214
  nosort=(-o nosort)
217
215
  fi
218
- if [[ "${completions-}" =~ ([^\\]): && "${BASH_REMATCH[2]}" =~ [=/:] ]]; then
216
+ if [[ "${completions-}" =~ ([^\\\\]): && "${BASH_REMATCH[2]}" =~ [=/:] ]]; then
219
217
  nospace=(-S '')
220
218
  fi
221
219
  _describe "$executable" completions "${nosort[@]}" "${nospace[@]}"
@@ -4,6 +4,7 @@
4
4
  import argparse
5
5
  import os
6
6
  import subprocess
7
+ from shlex import quote
7
8
 
8
9
 
9
10
  def _call(*args, **kwargs):
@@ -64,20 +65,22 @@ class FilesCompleter(BaseCompleter):
64
65
  # correctly in older versions and calling bind makes them available. For details, see
65
66
  # https://savannah.gnu.org/support/index.php?111125
66
67
  files = _call(
67
- ["bash", "-c", "bind; compgen -A directory -- '{p}'".format(p=prefix)], stderr=subprocess.DEVNULL
68
+ ["bash", "-c", "bind; compgen -A directory -- {p}".format(p=quote(prefix))],
69
+ stderr=subprocess.DEVNULL,
68
70
  )
69
71
  completion += [f + "/" for f in files]
70
72
  for x in self.allowednames:
71
73
  completion += _call(
72
- ["bash", "-c", "bind; compgen -A file -X '!*.{0}' -- '{p}'".format(x, p=prefix)],
74
+ ["bash", "-c", "bind; compgen -A file -X '!*.{0}' -- {p}".format(x, p=quote(prefix))],
73
75
  stderr=subprocess.DEVNULL,
74
76
  )
75
77
  else:
76
78
  completion += _call(
77
- ["bash", "-c", "bind; compgen -A file -- '{p}'".format(p=prefix)], stderr=subprocess.DEVNULL
79
+ ["bash", "-c", "bind; compgen -A file -- {p}".format(p=quote(prefix))], stderr=subprocess.DEVNULL
78
80
  )
79
81
  anticomp = _call(
80
- ["bash", "-c", "bind; compgen -A directory -- '{p}'".format(p=prefix)], stderr=subprocess.DEVNULL
82
+ ["bash", "-c", "bind; compgen -A directory -- {p}".format(p=quote(prefix))],
83
+ stderr=subprocess.DEVNULL,
81
84
  )
82
85
  completion = list(set(completion) - set(anticomp))
83
86
 
@@ -48,6 +48,7 @@ class CompletionFinder(object):
48
48
  append_space=None,
49
49
  ):
50
50
  self._parser = argument_parser
51
+ self._formatter = None
51
52
  self.always_complete_options = always_complete_options
52
53
  self.exclude = exclude
53
54
  if validator is None:
@@ -283,6 +284,15 @@ class CompletionFinder(object):
283
284
 
284
285
  return self.active_parsers
285
286
 
287
+ def _get_action_help(self, action):
288
+ if action.help is None:
289
+ return ""
290
+ if "%" not in action.help:
291
+ return action.help
292
+ if self._formatter is None:
293
+ self._formatter = self._parser.formatter_class(prog=self._parser.prog)
294
+ return self._formatter._expand_help(action)
295
+
286
296
  def _get_subparser_completions(self, parser, cword_prefix):
287
297
  aliases_by_parser: Dict[argparse.ArgumentParser, List[str]] = {}
288
298
  for key in parser.choices.keys():
@@ -292,7 +302,7 @@ class CompletionFinder(object):
292
302
  for action in parser._get_subactions():
293
303
  for alias in aliases_by_parser[parser.choices[action.dest]]:
294
304
  if alias.startswith(cword_prefix):
295
- self._display_completions[alias] = action.help or ""
305
+ self._display_completions[alias] = self._get_action_help(action)
296
306
 
297
307
  completions = [subcmd for subcmd in parser.choices.keys() if subcmd.startswith(cword_prefix)]
298
308
  return completions
@@ -313,7 +323,7 @@ class CompletionFinder(object):
313
323
  if action.option_strings:
314
324
  for option_string in action.option_strings:
315
325
  if option_string.startswith(cword_prefix):
316
- self._display_completions[option_string] = action.help or ""
326
+ self._display_completions[option_string] = self._get_action_help(action)
317
327
 
318
328
  option_completions = []
319
329
  for action in parser._actions:
@@ -405,7 +415,7 @@ class CompletionFinder(object):
405
415
  if self.validator(completion, cword_prefix):
406
416
  completions.append(completion)
407
417
  if isinstance(completer, ChoicesCompleter):
408
- self._display_completions[completion] = active_action.help or ""
418
+ self._display_completions[completion] = self._get_action_help(active_action)
409
419
  else:
410
420
  self._display_completions[completion] = ""
411
421
  else:
@@ -517,7 +527,7 @@ class CompletionFinder(object):
517
527
  # (extended to characters other than the colon).
518
528
  if last_wordbreak_pos is not None:
519
529
  completions = [c[last_wordbreak_pos + 1 :] for c in completions]
520
- special_chars += "();<>|&!`$* \t\n\"'"
530
+ special_chars += "();<>|&!`$*?[]{} \t\n\"'"
521
531
  elif cword_prequote == '"':
522
532
  special_chars += '"`$!'
523
533
 
@@ -19,7 +19,13 @@ from argparse import (
19
19
  _SubParsersAction,
20
20
  )
21
21
  from gettext import gettext
22
- from typing import Dict, List, Set, Tuple
22
+ from typing import Dict, List, Optional, Set, Tuple, Union, cast
23
+
24
+ _OptionTuple = Union[
25
+ Tuple[Optional[Action], str, Optional[str]],
26
+ Tuple[Optional[Action], str, Optional[str], Optional[str]],
27
+ ]
28
+ _OptionTupleEntry = Union[_OptionTuple, List[_OptionTuple]]
23
29
 
24
30
  _num_consumed_args: Dict[Action, int] = {}
25
31
 
@@ -97,7 +103,7 @@ class IntrospectiveArgumentParser(ArgumentParser):
97
103
  # find all option indices, and determine the arg_string_pattern
98
104
  # which has an 'O' if there is an option at an index,
99
105
  # an 'A' if there is an argument, or a '-' if there is a '--'
100
- option_string_indices = {}
106
+ option_string_indices: Dict[int, _OptionTupleEntry] = {}
101
107
  arg_string_pattern_parts = []
102
108
  arg_strings_iter = iter(arg_strings)
103
109
  for i, arg_string in enumerate(arg_strings_iter):
@@ -114,7 +120,7 @@ class IntrospectiveArgumentParser(ArgumentParser):
114
120
  if option_tuple is None:
115
121
  pattern = 'A'
116
122
  else:
117
- option_string_indices[i] = option_tuple
123
+ option_string_indices[i] = cast(_OptionTupleEntry, option_tuple)
118
124
  pattern = 'O'
119
125
  arg_string_pattern_parts.append(pattern)
120
126
 
@@ -161,9 +167,11 @@ class IntrospectiveArgumentParser(ArgumentParser):
161
167
  # function to convert arg_strings into an optional action
162
168
  def consume_optional(start_index):
163
169
  # get the optional identified at this index
164
- option_tuple = option_string_indices[start_index]
165
- if isinstance(option_tuple, list): # Python 3.12.7+
166
- option_tuple = option_tuple[0]
170
+ raw_option_tuple = option_string_indices[start_index]
171
+ if isinstance(raw_option_tuple, list): # Python 3.12.7+
172
+ option_tuple = raw_option_tuple[0]
173
+ else:
174
+ option_tuple = raw_option_tuple
167
175
  if len(option_tuple) == 3:
168
176
  action, option_string, explicit_arg = option_tuple
169
177
  else: # Python 3.11.9+, 3.12.3+, 3.13+
@@ -241,8 +249,8 @@ class IntrospectiveArgumentParser(ArgumentParser):
241
249
  # add the Optional to the list and return the index at which
242
250
  # the Optional's string args stopped
243
251
  assert action_tuples
244
- for action, args, option_string in action_tuples:
245
- take_action(action, args, option_string)
252
+ for optional_action, args, option_string in action_tuples:
253
+ take_action(optional_action, args, option_string)
246
254
  return stop
247
255
 
248
256
  # the list of Positionals left to be parsed; this is modified
@@ -79,7 +79,9 @@ def install_to_destination(dest):
79
79
  try:
80
80
  os.makedirs(destdir, exist_ok=True)
81
81
  except Exception as e:
82
- parser.error(f"path {destdir} does not exist and could not be created: {e}")
82
+ parser.error(
83
+ f"path {destdir} does not exist and could not be created: {e}. Please run this command using sudo, or see --help for more options."
84
+ )
83
85
  try:
84
86
  print(f"Installing {activator} to {dest}...", file=sys.stderr)
85
87
  shutil.copy(activator, dest)
@@ -47,7 +47,7 @@ _python_argcomplete%(function_suffix)s() {
47
47
  if is-at-least 5.8; then
48
48
  nosort=(-o nosort)
49
49
  fi
50
- if [[ "${completions-}" =~ ([^\\]): && "${match[1]}" =~ [=/:] ]]; then
50
+ if [[ "${completions-}" =~ ([^\\\\]): && "${match[1]}" =~ [=/:] ]]; then
51
51
  nospace=(-S '')
52
52
  fi
53
53
  _describe "${words[1]}" completions "${nosort[@]}" "${nospace[@]}"
@@ -94,14 +94,14 @@ complete "%(executable)s" 'p@*@`python-argcomplete-tcsh "%(argcomplete_script)s"
94
94
 
95
95
  fishcode = r"""
96
96
  function __fish_%(function_name)s_complete
97
- set -x _ARGCOMPLETE 1
98
- set -x _ARGCOMPLETE_DFS \t
99
- set -x _ARGCOMPLETE_IFS \n
100
- set -x _ARGCOMPLETE_SUPPRESS_SPACE 1
101
- set -x _ARGCOMPLETE_SHELL fish
102
- set -x COMP_LINE (commandline -p)
103
- set -x COMP_POINT (string length (commandline -cp))
104
- set -x COMP_TYPE
97
+ set -lx _ARGCOMPLETE 1
98
+ set -lx _ARGCOMPLETE_DFS \t
99
+ set -lx _ARGCOMPLETE_IFS \n
100
+ set -lx _ARGCOMPLETE_SUPPRESS_SPACE 1
101
+ set -lx _ARGCOMPLETE_SHELL fish
102
+ set -lx COMP_LINE (commandline -p)
103
+ set -lx COMP_POINT (string length (commandline -cp))
104
+ set -lx COMP_TYPE
105
105
  if set -q _ARC_DEBUG
106
106
  %(argcomplete_script)s 8>&1 9>&2 1>&9 2>&1
107
107
  else
@@ -16,6 +16,14 @@ or create new completion file, e.g::
16
16
 
17
17
  register-python-argcomplete --shell fish my-awesome-script > ~/.config/fish/completions/my-awesome-script.fish
18
18
 
19
+ If the script is called using a path, such as ``./my-awesome-script``, it must be registered using the absolute path to the script. ::
20
+
21
+ register-python-argcomplete --shell fish $(realpath ./my-awesome-script) > ~/.config/fish/completions/my-awesome-script.fish
22
+
23
+ The completions using a path will not be automatically loaded since the command name does not match the file name. To load the completions, ``source`` them. ::
24
+
25
+ source ~/.config/fish/completions/my-awesome-script.fish
26
+
19
27
  Completion Description For Fish
20
28
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21
29
  By default help string is added as completion description.
@@ -15,12 +15,12 @@ classifiers = [
15
15
  "Operating System :: POSIX",
16
16
  "Programming Language :: Python",
17
17
  "Programming Language :: Python :: 3",
18
- "Programming Language :: Python :: 3.8",
19
18
  "Programming Language :: Python :: 3.9",
20
19
  "Programming Language :: Python :: 3.10",
21
20
  "Programming Language :: Python :: 3.11",
22
21
  "Programming Language :: Python :: 3.12",
23
22
  "Programming Language :: Python :: 3.13",
23
+ "Programming Language :: Python :: 3.14",
24
24
  "Programming Language :: Python :: Implementation :: CPython",
25
25
  "Programming Language :: Python :: Implementation :: PyPy",
26
26
  "Development Status :: 5 - Production/Stable",
@@ -32,7 +32,6 @@ classifiers = [
32
32
 
33
33
  [project.scripts]
34
34
  activate-global-python-argcomplete = "argcomplete.scripts.activate_global_python_argcomplete:main"
35
- python-argcomplete-check-easy-install-script = "argcomplete.scripts.python_argcomplete_check_easy_install_script:main"
36
35
  register-python-argcomplete = "argcomplete.scripts.register_python_argcomplete:main"
37
36
 
38
37
  [project.optional-dependencies]
@@ -356,6 +356,15 @@ class TestArgcomplete(unittest.TestCase):
356
356
  fp.write("test")
357
357
  self.assertEqual(set(fc("a")), set(["abcdefж/", "abcaha/", "abcxyz"]))
358
358
 
359
+ def test_file_completion_quotes_prefix(self):
360
+ with TempDir(prefix="test_dir_fc_quote", dir="."):
361
+ marker = "should_not_exist"
362
+ prefix = "'; touch {0}; echo '".format(marker)
363
+
364
+ self.assertFalse(os.path.exists(marker))
365
+ self.assertEqual(FilesCompleter()(prefix), [])
366
+ self.assertFalse(os.path.exists(marker))
367
+
359
368
  def test_filescompleter_filetype_integration(self):
360
369
  def make_parser():
361
370
  parser = ArgumentParser()
@@ -828,6 +837,7 @@ class TestArgcomplete(unittest.TestCase):
828
837
  parser.add_argument("-1", choices=["bar<$>baz"])
829
838
  parser.add_argument("-2", choices=[r"\* "])
830
839
  parser.add_argument("-3", choices=["\"'"])
840
+ parser.add_argument("-4", choices=["data?", "file[1]", "{x,--flag}"])
831
841
  return parser
832
842
 
833
843
  self.assertEqual(set(self.run_completer(make_parser(), "prog -1 ")), {r"bar\<\$\>baz "})
@@ -835,6 +845,10 @@ class TestArgcomplete(unittest.TestCase):
835
845
  self.assertEqual(set(self.run_completer(make_parser(), "prog -3 ")), {r"\"\' "})
836
846
  self.assertEqual(set(self.run_completer(make_parser(), 'prog -3 "')), {r"\"'"})
837
847
  self.assertEqual(set(self.run_completer(make_parser(), "prog -3 '")), {"\"'\\''"})
848
+ self.assertEqual(
849
+ set(self.run_completer(make_parser(), "prog -4 ")),
850
+ {r"data\?", r"file\[1\]", r"\{x,--flag\}"},
851
+ )
838
852
 
839
853
  self.assertEqual(set(self.run_completer(make_parser(), "prog -1 ", shell="tcsh")), {"bar<$>baz "})
840
854
  # The trailing space won't actually work correctly in tcsh.
@@ -1,84 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors.
4
- # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.
5
-
6
- """
7
- This script is part of the Python argcomplete package (https://github.com/kislyuk/argcomplete).
8
- It is used to check if an EASY-INSTALL-SCRIPT wrapper redirects to a script that contains the string
9
- "PYTHON_ARGCOMPLETE_OK". If you have enabled global completion in argcomplete, the completion hook will run it every
10
- time you press <TAB> in your shell.
11
-
12
- Usage:
13
- python-argcomplete-check-easy-install-script <input executable file>
14
- """
15
-
16
- import sys
17
-
18
- # PEP 366
19
- __package__ = "argcomplete.scripts"
20
-
21
-
22
- def main():
23
- if len(sys.argv) != 2:
24
- sys.exit(__doc__)
25
-
26
- sys.tracebacklimit = 0
27
-
28
- with open(sys.argv[1]) as fh:
29
- line1, head = fh.read(1024).split("\n", 1)[:2]
30
- if line1.startswith("#") and ("py" in line1 or "Py" in line1):
31
- import re
32
-
33
- lines = head.split("\n", 12)
34
- for line in lines:
35
- if line.startswith("# EASY-INSTALL-SCRIPT"):
36
- import pkg_resources # type: ignore
37
-
38
- re_match = re.match("# EASY-INSTALL-SCRIPT: '(.+)','(.+)'", line)
39
- assert re_match is not None
40
- dist, script = re_match.groups()
41
- if "PYTHON_ARGCOMPLETE_OK" in pkg_resources.get_distribution(dist).get_metadata(
42
- "scripts/" + script
43
- ):
44
- return 0
45
- elif line.startswith("# EASY-INSTALL-ENTRY-SCRIPT"):
46
- re_match = re.match("# EASY-INSTALL-ENTRY-SCRIPT: '(.+)','(.+)','(.+)'", line)
47
- assert re_match is not None
48
- dist, group, name = re_match.groups()
49
- import pkgutil
50
-
51
- import pkg_resources # type: ignore
52
-
53
- entry_point_info = pkg_resources.get_distribution(dist).get_entry_info(group, name)
54
- assert entry_point_info is not None
55
- module_name = entry_point_info.module_name
56
- with open(pkgutil.get_loader(module_name).get_filename()) as mod_fh: # type: ignore
57
- if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024):
58
- return 0
59
- elif line.startswith("# EASY-INSTALL-DEV-SCRIPT"):
60
- for line2 in lines:
61
- if line2.startswith("__file__"):
62
- re_match = re.match("__file__ = '(.+)'", line2)
63
- assert re_match is not None
64
- filename = re_match.group(1)
65
- with open(filename) as mod_fh:
66
- if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024):
67
- return 0
68
- elif line.startswith("# PBR Generated"):
69
- re_match = re.search("from (.*) import", head)
70
- assert re_match is not None
71
- module = re_match.groups()[0]
72
- import pkgutil
73
-
74
- import pkg_resources # type: ignore
75
-
76
- with open(pkgutil.get_loader(module).get_filename()) as mod_fh: # type: ignore
77
- if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024):
78
- return 0
79
-
80
- return 1
81
-
82
-
83
- if __name__ == "__main__":
84
- sys.exit(main())
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes