argcomplete 3.6.3__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.3 → argcomplete-3.7.0}/.github/workflows/ci.yml +10 -10
  2. {argcomplete-3.6.3 → argcomplete-3.7.0}/Changes.rst +8 -0
  3. {argcomplete-3.6.3 → argcomplete-3.7.0}/PKG-INFO +2 -2
  4. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/bash_completion.d/_python-argcomplete +1 -4
  5. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/completers.py +7 -4
  6. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/finders.py +1 -1
  7. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/packages/_argparse.py +16 -8
  8. {argcomplete-3.6.3 → argcomplete-3.7.0}/pyproject.toml +1 -2
  9. {argcomplete-3.6.3 → argcomplete-3.7.0}/test/test.py +14 -0
  10. argcomplete-3.6.3/argcomplete/scripts/python_argcomplete_check_easy_install_script.py +0 -84
  11. {argcomplete-3.6.3 → argcomplete-3.7.0}/.github/FUNDING.yml +0 -0
  12. {argcomplete-3.6.3 → argcomplete-3.7.0}/.github/workflows/release.yml +0 -0
  13. {argcomplete-3.6.3 → argcomplete-3.7.0}/.gitignore +0 -0
  14. {argcomplete-3.6.3 → argcomplete-3.7.0}/Authors.rst +0 -0
  15. {argcomplete-3.6.3 → argcomplete-3.7.0}/LICENSE.rst +0 -0
  16. {argcomplete-3.6.3 → argcomplete-3.7.0}/MANIFEST.in +0 -0
  17. {argcomplete-3.6.3 → argcomplete-3.7.0}/Makefile +0 -0
  18. {argcomplete-3.6.3 → argcomplete-3.7.0}/NOTICE +0 -0
  19. {argcomplete-3.6.3 → argcomplete-3.7.0}/README.rst +0 -0
  20. {argcomplete-3.6.3 → argcomplete-3.7.0}/SECURITY.md +0 -0
  21. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/__init__.py +0 -0
  22. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/_check_console_script.py +0 -0
  23. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/_check_module.py +0 -0
  24. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/exceptions.py +0 -0
  25. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/io.py +0 -0
  26. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/lexers.py +0 -0
  27. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/packages/__init__.py +0 -0
  28. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/packages/_shlex.py +0 -0
  29. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/py.typed +0 -0
  30. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/scripts/__init__.py +0 -0
  31. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/scripts/activate_global_python_argcomplete.py +0 -0
  32. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/scripts/register_python_argcomplete.py +0 -0
  33. {argcomplete-3.6.3 → argcomplete-3.7.0}/argcomplete/shell_integration.py +0 -0
  34. {argcomplete-3.6.3 → argcomplete-3.7.0}/common.mk +0 -0
  35. {argcomplete-3.6.3 → argcomplete-3.7.0}/contrib/README.rst +0 -0
  36. {argcomplete-3.6.3 → argcomplete-3.7.0}/docs/changelog.rst +0 -0
  37. {argcomplete-3.6.3 → argcomplete-3.7.0}/docs/conf.py +0 -0
  38. {argcomplete-3.6.3 → argcomplete-3.7.0}/docs/examples/describe_github_user.py +0 -0
  39. {argcomplete-3.6.3 → argcomplete-3.7.0}/docs/fish_help_string.png +0 -0
  40. {argcomplete-3.6.3 → argcomplete-3.7.0}/docs/index.rst +0 -0
  41. {argcomplete-3.6.3 → argcomplete-3.7.0}/docs/toc.html +0 -0
  42. {argcomplete-3.6.3 → argcomplete-3.7.0}/setup.cfg +0 -0
  43. {argcomplete-3.6.3 → argcomplete-3.7.0}/test/__init__.py +0 -0
  44. {argcomplete-3.6.3 → argcomplete-3.7.0}/test/inputrc +0 -0
  45. {argcomplete-3.6.3 → argcomplete-3.7.0}/test/prog +0 -0
  46. {argcomplete-3.6.3 → argcomplete-3.7.0}/test/stuck +0 -0
  47. {argcomplete-3.6.3 → argcomplete-3.7.0}/test/test_contrib_shells.py +0 -0
  48. {argcomplete-3.6.3 → argcomplete-3.7.0}/test/test_package/__init__.py +0 -0
  49. {argcomplete-3.6.3 → argcomplete-3.7.0}/test/test_package/setup.py +0 -0
  50. {argcomplete-3.6.3 → argcomplete-3.7.0}/test/test_package/test_module.py +0 -0
  51. {argcomplete-3.6.3 → 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,11 @@
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
+
1
9
  Changes for v3.6.3 (2025-10-19)
2
10
  ===============================
3
11
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: argcomplete
3
- Version: 3.6.3
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
@@ -190,10 +190,7 @@ _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 ([[ "${interpreter[@]}" == *python* ]] || [[ "${interpreter[@]}" == *pypy* ]])\
193
+ if ([[ "${interpreter[@]}" == *python* ]] || [[ "${interpreter[@]}" == *pypy* ]])\
197
194
  && __python_argcomplete_run "${interpreter[@]}" -m argcomplete._check_console_script "$SCRIPT_NAME"; then
198
195
  ARGCOMPLETE=1
199
196
  fi
@@ -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
 
@@ -527,7 +527,7 @@ class CompletionFinder(object):
527
527
  # (extended to characters other than the colon).
528
528
  if last_wordbreak_pos is not None:
529
529
  completions = [c[last_wordbreak_pos + 1 :] for c in completions]
530
- special_chars += "();<>|&!`$* \t\n\"'"
530
+ special_chars += "();<>|&!`$*?[]{} \t\n\"'"
531
531
  elif cword_prequote == '"':
532
532
  special_chars += '"`$!'
533
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
@@ -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
File without changes