Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-argcomplete for
openSUSE:Factory checked in at 2026-10-23 20:20:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-argcomplete (Old)
and /work/SRC/openSUSE:Factory/.python-argcomplete.new.2252680 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-argcomplete"
Fri Oct 23 20:20:55 2026 rev:46 rq:1386544 version:3.7.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-argcomplete/python-argcomplete.changes
2026-02-11 18:53:31.313171699 +0100
+++
/work/SRC/openSUSE:Factory/.python-argcomplete.new.2252680/python-argcomplete.changes
2026-10-23 20:21:05.442477055 +0200
@@ -1,0 +2,15 @@
+Sat Oct 17 15:42:36 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to 3.7.2:
+ * Require Python 3.10+
+ * Fix zsh global completion with zsh 5.9.0.1; expand ~ in
+ _FilteredFilesCompleter
+ * Escape glob and brace metacharacters in quote_completions;
+ quote compgen prefix in FilesCompleter
+ * Remove deprecated easy_install script detection
+- Drop python-argcomplete-check-easy-install-script and its
+ libalternatives registrations, removed upstream
+- Remove the /usr/bin/env python3 shebang from the non-executable
+ argcomplete/scripts modules
+
+-------------------------------------------------------------------
Old:
----
argcomplete-3.6.3.tar.gz
New:
----
argcomplete-3.7.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-argcomplete.spec ++++++
--- /var/tmp/diff_new_pack.Fskhsq/_old 2026-10-23 20:21:06.096504425 +0200
+++ /var/tmp/diff_new_pack.Fskhsq/_new 2026-10-23 20:21:06.098504508 +0200
@@ -33,13 +33,13 @@
%{?pythons_for_pypi}
%{?sle15_python_module_pythons}
Name: python-argcomplete%{psuffix}
-Version: 3.6.3
+Version: 3.7.2
Release: 0
Summary: Bash tab completion for argparse
License: Apache-2.0
URL: https://github.com/kislyuk/argcomplete
Source:
https://files.pythonhosted.org/packages/source/a/argcomplete/argcomplete-%{version}.tar.gz
-BuildRequires: %{python_module base >= 3.8}
+BuildRequires: %{python_module base >= 3.10}
BuildRequires: %{python_module hatchling}
BuildRequires: %{python_module pip}
BuildRequires: %{python_module setuptools >= 67.2}
@@ -80,6 +80,8 @@
%prep
%autosetup -p1 -n argcomplete-%{version}
+# imported via the entry points and installed non-executable
+sed -i '1{/^#!/d}' argcomplete/scripts/*.py
%build
%if %{without test}
@@ -91,7 +93,6 @@
%pyproject_install
%python_clone -a %{buildroot}%{_bindir}/activate-global-python-argcomplete
%python_clone -a %{buildroot}%{_bindir}/register-python-argcomplete
-%python_clone -a
%{buildroot}%{_bindir}/python-argcomplete-check-easy-install-script
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%python_expand chmod -x %{buildroot}%{$python_sitelib}/argcomplete/scripts/*.py
%endif
@@ -118,17 +119,14 @@
%pre
%python_libalternatives_reset_alternative activate-global-python-argcomplete
%python_libalternatives_reset_alternative register-python-argcomplete
-%python_libalternatives_reset_alternative
python-argcomplete-check-easy-install-script
%post
%python_install_alternative activate-global-python-argcomplete
%python_install_alternative register-python-argcomplete
-%python_install_alternative python-argcomplete-check-easy-install-script
%postun
%python_uninstall_alternative activate-global-python-argcomplete
%python_uninstall_alternative register-python-argcomplete
-%python_uninstall_alternative python-argcomplete-check-easy-install-script
%files %{python_files}
%doc README.rst
@@ -136,7 +134,6 @@
%{python_sitelib}/argcomplete-%{version}.dist-info
%{python_sitelib}/argcomplete
%python_alternative %{_bindir}/activate-global-python-argcomplete
-%python_alternative %{_bindir}/python-argcomplete-check-easy-install-script
%python_alternative %{_bindir}/register-python-argcomplete
%endif
++++++ argcomplete-3.6.3.tar.gz -> argcomplete-3.7.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/.github/workflows/ci.yml
new/argcomplete-3.7.2/.github/workflows/ci.yml
--- old/argcomplete-3.6.3/.github/workflows/ci.yml 2020-02-02
01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/.github/workflows/ci.yml 2020-02-02
01:00:00.000000000 +0100
@@ -3,17 +3,17 @@
on: [push, pull_request]
jobs:
- build:
+ ci:
runs-on: ${{matrix.os}}
strategy:
fail-fast: false
matrix:
- os: [ubuntu-20.04, ubuntu-latest, macos-13, macos-latest]
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
+ os: [ubuntu-22.04, ubuntu-latest, macos-14, macos-latest]
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-python@v4
+ - uses: actions/checkout@v7
+ - uses: actions/setup-python@v6
with:
python-version: ${{matrix.python-version}}
allow-prereleases: true
@@ -31,15 +31,15 @@
- uses: codecov/codecov-action@v5
if: ${{matrix.python-version == '3.12' && matrix.os == 'ubuntu-22.04'}}
isort:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- uses: isort/[email protected]
ruff:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
- - uses: astral-sh/ruff-action@v1
- - uses: astral-sh/ruff-action@v1
+ - uses: actions/checkout@v7
+ - uses: astral-sh/[email protected]
+ - uses: astral-sh/[email protected]
with:
args: "format --check"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/Changes.rst
new/argcomplete-3.7.2/Changes.rst
--- old/argcomplete-3.6.3/Changes.rst 2020-02-02 01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/Changes.rst 2020-02-02 01:00:00.000000000 +0100
@@ -1,3 +1,24 @@
+Changes for v3.7.2 (2026-08-05)
+===============================
+
+Require Python 3.10+ (#558)
+
+Changes for v3.7.1 (2026-08-04)
+===============================
+
+- Fix for kislyuk#544 => TestZshGlobalExplicit tests are failing after
+ upgrade to zsh 5.9.0.1 (#557)
+
+- Expand ~ in \_FilteredFilesCompleter (DirectoriesCompleter) (#553)
+
+Changes for v3.7.0 (2026-06-30)
+===============================
+
+- Escape glob and brace metacharacters in quote_completions (#552)
+- Quote prefix passed to compgen in FilesCompleter (#551)
+- Remove deprecated easy_install script detection
+- Type hinting improvements
+
Changes for v3.6.3 (2025-10-19)
===============================
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/PKG-INFO
new/argcomplete-3.7.2/PKG-INFO
--- old/argcomplete-3.6.3/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: argcomplete
-Version: 3.6.3
+Version: 3.7.2
Summary: Bash tab completion for argparse
Project-URL: Homepage, https://github.com/kislyuk/argcomplete
Project-URL: Documentation, https://kislyuk.github.io/argcomplete
@@ -22,19 +22,18 @@
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Shells
Classifier: Topic :: Terminals
-Requires-Python: >=3.8
+Requires-Python: >=3.10
Provides-Extra: test
Requires-Dist: coverage; extra == 'test'
Requires-Dist: mypy; extra == 'test'
@@ -298,7 +297,7 @@
Python Support
--------------
-Argcomplete requires Python 3.9+.
+Argcomplete requires Python 3.10+.
Support for other shells
------------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/README.rst
new/argcomplete-3.7.2/README.rst
--- old/argcomplete-3.6.3/README.rst 2020-02-02 01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/README.rst 2020-02-02 01:00:00.000000000 +0100
@@ -253,7 +253,7 @@
Python Support
--------------
-Argcomplete requires Python 3.9+.
+Argcomplete requires Python 3.10+.
Support for other shells
------------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/argcomplete/__init__.py
new/argcomplete-3.7.2/argcomplete/__init__.py
--- old/argcomplete-3.6.3/argcomplete/__init__.py 2020-02-02
01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/argcomplete/__init__.py 2020-02-02
01:00:00.000000000 +0100
@@ -9,5 +9,5 @@
from .lexers import split_line
from .shell_integration import shellcode
-autocomplete = CompletionFinder()
+autocomplete: CompletionFinder = CompletionFinder()
autocomplete.__doc__ = """ Use this to access argcomplete. See
:meth:`argcomplete.CompletionFinder.__call__()`. """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/argcomplete-3.6.3/argcomplete/bash_completion.d/_python-argcomplete
new/argcomplete-3.7.2/argcomplete/bash_completion.d/_python-argcomplete
--- old/argcomplete-3.6.3/argcomplete/bash_completion.d/_python-argcomplete
2020-02-02 01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/argcomplete/bash_completion.d/_python-argcomplete
2020-02-02 01:00:00.000000000 +0100
@@ -145,9 +145,13 @@
req_argv=( "" "${COMP_WORDS[@]:1}" )
__python_argcomplete_expand_tilde_by_ref executable
else
- executable="${words[1]}"
+ # Reconstruct the unshifted words array from BUFFER to handle cases
where
+ # a helper completion function (like _python) has shifted the words
array.
+ local -a local_words
+ local_words=( ${(z)BUFFER} )
+ executable="${local_words[1]}"
__python_argcomplete_expand_tilde_by_ref executable
- req_argv=( "${words[@]:1}" )
+ req_argv=( "${local_words[@]:1}" )
fi
local ARGCOMPLETE=0
@@ -190,10 +194,7 @@
interpreter=($interpreter)
fi
- if (__python_argcomplete_scan_head_noerr "$SCRIPT_NAME"
easy_install \
- && "${interpreter[@]}" "$(__python_argcomplete_which
python-argcomplete-check-easy-install-script)" "$SCRIPT_NAME") >/dev/null 2>&1;
then
- ARGCOMPLETE=1
- elif ([[ "${interpreter[@]}" == *python* ]] || [[
"${interpreter[@]}" == *pypy* ]])\
+ if ([[ "${interpreter[@]}" == *python* ]] || [[
"${interpreter[@]}" == *pypy* ]])\
&& __python_argcomplete_run "${interpreter[@]}" -m
argcomplete._check_console_script "$SCRIPT_NAME"; then
ARGCOMPLETE=1
fi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/argcomplete/completers.py
new/argcomplete-3.7.2/argcomplete/completers.py
--- old/argcomplete-3.6.3/argcomplete/completers.py 2020-02-02
01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/argcomplete/completers.py 2020-02-02
01:00:00.000000000 +0100
@@ -4,6 +4,11 @@
import argparse
import os
import subprocess
+from collections.abc import Callable, Generator, Iterable, Mapping
+from shlex import quote
+from typing import Final
+
+_Ignored = object
def _call(*args, **kwargs):
@@ -22,12 +27,14 @@
def __call__(
self, *, prefix: str, action: argparse.Action, parser:
argparse.ArgumentParser, parsed_args: argparse.Namespace
- ) -> None:
+ ) -> Iterable[str]:
raise NotImplementedError("This method should be implemented by a
subclass.")
class ChoicesCompleter(BaseCompleter):
- def __init__(self, choices):
+ choices: Final[Mapping[str, str | bytes]]
+
+ def __init__(self, choices: Mapping[str, str | bytes]) -> None:
self.choices = choices
def _convert(self, choice):
@@ -35,11 +42,11 @@
choice = str(choice)
return choice
- def __call__(self, **kwargs):
+ def __call__(self, **kwargs: _Ignored) -> Iterable[str]:
return (self._convert(c) for c in self.choices)
-EnvironCompleter = ChoicesCompleter(os.environ)
+EnvironCompleter: Final[ChoicesCompleter] = ChoicesCompleter(os.environ)
class FilesCompleter(BaseCompleter):
@@ -47,7 +54,10 @@
File completer class, optionally takes a list of allowed extensions
"""
- def __init__(self, allowednames=(), directories=True):
+ allowednames: Final[list[str]]
+ directories: Final[bool]
+
+ def __init__(self, allowednames: Iterable[str] | str = (), directories:
bool = True) -> None:
# Fix if someone passes in a string instead of a list
if isinstance(allowednames, (str, bytes)):
allowednames = [allowednames]
@@ -55,8 +65,8 @@
self.allowednames = [x.lstrip("*").lstrip(".") for x in allowednames]
self.directories = directories
- def __call__(self, prefix, **kwargs):
- completion = []
+ def __call__(self, prefix: str, **kwargs: _Ignored) -> list[str]:
+ completion: list[str] = []
if self.allowednames:
if self.directories:
# Using 'bind' in this and the following commands is a
workaround to a bug in bash
@@ -64,20 +74,22 @@
# correctly in older versions and calling bind makes them
available. For details, see
# https://savannah.gnu.org/support/index.php?111125
files = _call(
- ["bash", "-c", "bind; compgen -A directory --
'{p}'".format(p=prefix)], stderr=subprocess.DEVNULL
+ ["bash", "-c", "bind; compgen -A directory --
{p}".format(p=quote(prefix))],
+ stderr=subprocess.DEVNULL,
)
completion += [f + "/" for f in files]
for x in self.allowednames:
completion += _call(
- ["bash", "-c", "bind; compgen -A file -X '!*.{0}' --
'{p}'".format(x, p=prefix)],
+ ["bash", "-c", "bind; compgen -A file -X '!*.{0}' --
{p}".format(x, p=quote(prefix))],
stderr=subprocess.DEVNULL,
)
else:
completion += _call(
- ["bash", "-c", "bind; compgen -A file --
'{p}'".format(p=prefix)], stderr=subprocess.DEVNULL
+ ["bash", "-c", "bind; compgen -A file --
{p}".format(p=quote(prefix))], stderr=subprocess.DEVNULL
)
anticomp = _call(
- ["bash", "-c", "bind; compgen -A directory --
'{p}'".format(p=prefix)], stderr=subprocess.DEVNULL
+ ["bash", "-c", "bind; compgen -A directory --
{p}".format(p=quote(prefix))],
+ stderr=subprocess.DEVNULL,
)
completion = list(set(completion) - set(anticomp))
@@ -87,26 +99,31 @@
class _FilteredFilesCompleter(BaseCompleter):
- def __init__(self, predicate):
+ predicate: Final[Callable[[str], bool]]
+
+ def __init__(self, predicate: Callable[[str], bool]) -> None:
"""
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"
+ assert predicate, "Expected a callable predicate" # type:
ignore[truthy-function]
self.predicate = predicate
- def __call__(self, prefix, **kwargs):
+ def __call__(self, prefix: str, **kwargs: _Ignored) -> Generator[str]:
"""
Provide completions on prefix
"""
- target_dir = os.path.dirname(prefix)
+ # Expand a leading "~" so that filesystem operations below work on a
+ # real path (os.listdir("~") would otherwise raise FileNotFoundError).
+ expanded_prefix = os.path.expanduser(prefix)
+ target_dir = os.path.dirname(expanded_prefix)
try:
names = os.listdir(target_dir or ".")
except Exception:
return # empty iterator
- incomplete_part = os.path.basename(prefix)
+ incomplete_part = os.path.basename(expanded_prefix)
# Iterate on target_dir entries and filter on given predicate
for name in names:
if not name.startswith(incomplete_part):
@@ -118,7 +135,7 @@
class DirectoriesCompleter(_FilteredFilesCompleter):
- def __init__(self):
+ def __init__(self) -> None:
_FilteredFilesCompleter.__init__(self, predicate=os.path.isdir)
@@ -127,10 +144,10 @@
A completer used to suppress the completion of specific arguments
"""
- def __init__(self):
+ def __init__(self) -> None:
pass
- def suppress(self):
+ def suppress(self) -> bool:
"""
Decide if the completion should be suppressed
"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/argcomplete/finders.py
new/argcomplete-3.7.2/argcomplete/finders.py
--- old/argcomplete-3.6.3/argcomplete/finders.py 2020-02-02
01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/argcomplete/finders.py 2020-02-02
01:00:00.000000000 +0100
@@ -6,8 +6,8 @@
import argparse
import os
import sys
-from collections.abc import Mapping
-from typing import Callable, Dict, List, Optional, Sequence, TextIO, Union
+from collections.abc import Container, Mapping
+from typing import Callable, Literal, TextIO
from . import io as _io
from .completers import BaseCompleter, ChoicesCompleter, FilesCompleter,
SuppressCompleter
@@ -26,28 +26,42 @@
}
-def default_validator(completion, prefix):
+def default_validator(completion: str, prefix: str) -> bool:
return completion.startswith(prefix)
-class CompletionFinder(object):
+class CompletionFinder:
"""
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__()`.
"""
+ _parser: argparse.ArgumentParser | None
+ _formatter: argparse.HelpFormatter | None
+ always_complete_options: bool | Literal["long", "short"]
+ exclude: Container[str] | None
+ validator: Callable[[str, str], bool]
+ print_suppressed: bool
+ completing: bool
+ _display_completions: dict[str, str]
+ default_completer: BaseCompleter
+ append_space: bool
+
+ active_parsers: list[argparse.ArgumentParser]
+ visited_positionals: list[argparse.Action]
+
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
+ argument_parser: argparse.ArgumentParser | None = None,
+ always_complete_options: bool | Literal["long", "short"] = True,
+ exclude: Container[str] | None = None,
+ validator: Callable[[str, str], bool] | None = None,
+ print_suppressed: bool = False,
+ default_completer: BaseCompleter = FilesCompleter(),
+ append_space: bool | None = None,
+ ) -> None:
+ self._parser = argument_parser # type: ignore[assignment]
self._formatter = None
self.always_complete_options = always_complete_options
self.exclude = exclude
@@ -56,7 +70,7 @@
self.validator = validator
self.print_suppressed = print_suppressed
self.completing = False
- self._display_completions: Dict[str, str] = {}
+ self._display_completions = {}
self.default_completer = default_completer
if append_space is None:
append_space = os.environ.get("_ARGCOMPLETE_SUPPRESS_SPACE") != "1"
@@ -65,13 +79,13 @@
def __call__(
self,
argument_parser: argparse.ArgumentParser,
- always_complete_options: Union[bool, str] = True,
+ always_complete_options: bool | str = True,
exit_method: Callable = os._exit,
- output_stream: Optional[TextIO] = None,
- exclude: Optional[Sequence[str]] = None,
- validator: Optional[Callable] = None,
+ output_stream: TextIO | None = None,
+ exclude: Container[str] | None = None,
+ validator: Callable[[str, str], bool] | None = None,
print_suppressed: bool = False,
- append_space: Optional[bool] = None,
+ append_space: bool | None = None,
default_completer: BaseCompleter = FilesCompleter(),
) -> None:
"""
@@ -158,6 +172,7 @@
start = int(os.environ["_ARGCOMPLETE"]) - 1
comp_words = comp_words[start:]
+ assert self._parser is not None
if cword_prefix and cword_prefix[0] in self._parser.prefix_chars and
"=" in cword_prefix:
# Special case for when the current word is
"--optional=PARTIAL_VALUE". Give the optional to the parser.
comp_words.append(cword_prefix.split("=", 1)[0])
@@ -211,6 +226,7 @@
try:
debug("invoking parser with", comp_words[1:])
with mute_stderr():
+ assert self._parser is not None
a = self._parser.parse_known_args(comp_words[1:],
namespace=parsed_args)
debug("parsed args:", a)
except BaseException as e:
@@ -233,8 +249,8 @@
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: List[argparse.ArgumentParser] = []
- self.visited_positionals: List[argparse.Action] = []
+ self.active_parsers = []
+ self.visited_positionals = []
completer = self
@@ -289,12 +305,14 @@
return ""
if "%" not in action.help:
return action.help
- if self._formatter is None:
- self._formatter =
self._parser.formatter_class(prog=self._parser.prog)
- return self._formatter._expand_help(action)
+ formatters = self._formatter
+ if formatters is None:
+ assert self._parser is not None
+ self._formatter = formatters =
self._parser.formatter_class(prog=self._parser.prog)
+ return formatters._expand_help(action)
def _get_subparser_completions(self, parser, cword_prefix):
- aliases_by_parser: Dict[argparse.ArgumentParser, List[str]] = {}
+ aliases_by_parser: dict[argparse.ArgumentParser, list[str]] = {}
for key in parser.choices.keys():
p = parser.choices[key]
aliases_by_parser.setdefault(p, []).append(key)
@@ -433,15 +451,15 @@
return completions
def collect_completions(
- self, active_parsers: List[argparse.ArgumentParser], parsed_args:
argparse.Namespace, cword_prefix: str
- ) -> List[str]:
+ self, active_parsers: list[argparse.ArgumentParser], parsed_args:
argparse.Namespace, cword_prefix: str
+ ) -> list[str]:
"""
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: List[str] = []
+ completions: list[str] = []
debug("all active parsers:", active_parsers)
active_parser = active_parsers[-1]
@@ -488,7 +506,7 @@
return None
- def filter_completions(self, completions: List[str]) -> List[str]:
+ def filter_completions(self, completions: list[str]) -> list[str]:
"""
De-duplicates completions and excludes those specified by ``exclude``.
Returns the filtered completions as a list.
@@ -505,8 +523,8 @@
return filtered_completions
def quote_completions(
- self, completions: List[str], cword_prequote: str, last_wordbreak_pos:
Optional[int]
- ) -> List[str]:
+ self, completions: list[str], cword_prequote: str, last_wordbreak_pos:
int | None
+ ) -> list[str]:
"""
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.
@@ -527,7 +545,7 @@
# (extended to characters other than the colon).
if last_wordbreak_pos is not None:
completions = [c[last_wordbreak_pos + 1 :] for c in
completions]
- special_chars += "();<>|&!`$* \t\n\"'"
+ special_chars += "();<>|&!`$*?[]{} \t\n\"'"
elif cword_prequote == '"':
special_chars += '"`$!'
@@ -570,7 +588,7 @@
return escaped_completions
- def rl_complete(self, text, state):
+ def rl_complete(self, text: str, state: int) -> str | None:
"""
Alternate entry point for using the argcomplete completer in a
readline-based REPL. See also
`rlcompleter
<https://docs.python.org/3/library/rlcompleter.html#completer-objects>`_.
@@ -598,7 +616,7 @@
else:
return None
- def get_display_completions(self):
+ def get_display_completions(self) -> dict[str, str]:
"""
This function returns a mapping of completions to their help strings
for displaying to the user.
"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/argcomplete/io.py
new/argcomplete-3.7.2/argcomplete/io.py
--- old/argcomplete-3.6.3/argcomplete/io.py 2020-02-02 01:00:00.000000000
+0100
+++ new/argcomplete-3.7.2/argcomplete/io.py 2020-02-02 01:00:00.000000000
+0100
@@ -1,19 +1,20 @@
import contextlib
import os
import sys
+from collections.abc import Generator
_DEBUG = "_ARC_DEBUG" in os.environ
debug_stream = sys.stderr
-def debug(*args):
+def debug(*args: object) -> None:
if _DEBUG:
print(file=debug_stream, *args)
@contextlib.contextmanager
-def mute_stdout():
+def mute_stdout() -> Generator[None]:
stdout = sys.stdout
sys.stdout = open(os.devnull, "w")
try:
@@ -23,7 +24,7 @@
@contextlib.contextmanager
-def mute_stderr():
+def mute_stderr() -> Generator[None]:
stderr = sys.stderr
sys.stderr = open(os.devnull, "w")
try:
@@ -33,7 +34,7 @@
sys.stderr = stderr
-def warn(*args):
+def warn(*args: object) -> None:
"""
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.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/argcomplete/lexers.py
new/argcomplete-3.7.2/argcomplete/lexers.py
--- old/argcomplete-3.6.3/argcomplete/lexers.py 2020-02-02 01:00:00.000000000
+0100
+++ new/argcomplete-3.7.2/argcomplete/lexers.py 2020-02-02 01:00:00.000000000
+0100
@@ -5,7 +5,7 @@
from .packages import _shlex
-def split_line(line, point=None):
+def split_line(line: str, point: int | None = None) -> tuple[str, str, str,
list[str], int | None]:
if point is None:
point = len(line)
line = line[:point]
@@ -14,7 +14,7 @@
lexer.wordbreaks = os.environ.get("_ARGCOMPLETE_COMP_WORDBREAKS", "")
words = []
- def split_word(word):
+ def split_word(word: str) -> tuple[str, str, str, list[str], int | None]:
# 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:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/argcomplete/packages/_argparse.py
new/argcomplete-3.7.2/argcomplete/packages/_argparse.py
--- old/argcomplete-3.6.3/argcomplete/packages/_argparse.py 2020-02-02
01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/argcomplete/packages/_argparse.py 2020-02-02
01:00:00.000000000 +0100
@@ -19,7 +19,13 @@
_SubParsersAction,
)
from gettext import gettext
-from typing import Dict, List, Set, Tuple
+from typing import Dict, List, Optional, Set, Tuple, Union, cast
+
+_OptionTuple = Union[
+ Tuple[Optional[Action], str, Optional[str]],
+ Tuple[Optional[Action], str, Optional[str], Optional[str]],
+]
+_OptionTupleEntry = Union[_OptionTuple, List[_OptionTuple]]
_num_consumed_args: Dict[Action, int] = {}
@@ -97,7 +103,7 @@
# 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 = {}
+ option_string_indices: Dict[int, _OptionTupleEntry] = {}
arg_string_pattern_parts = []
arg_strings_iter = iter(arg_strings)
for i, arg_string in enumerate(arg_strings_iter):
@@ -114,7 +120,7 @@
if option_tuple is None:
pattern = 'A'
else:
- option_string_indices[i] = option_tuple
+ option_string_indices[i] = cast(_OptionTupleEntry,
option_tuple)
pattern = 'O'
arg_string_pattern_parts.append(pattern)
@@ -161,9 +167,11 @@
# 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]
- if isinstance(option_tuple, list): # Python 3.12.7+
- option_tuple = option_tuple[0]
+ raw_option_tuple = option_string_indices[start_index]
+ if isinstance(raw_option_tuple, list): # Python 3.12.7+
+ option_tuple = raw_option_tuple[0]
+ else:
+ option_tuple = raw_option_tuple
if len(option_tuple) == 3:
action, option_string, explicit_arg = option_tuple
else: # Python 3.11.9+, 3.12.3+, 3.13+
@@ -241,8 +249,8 @@
# 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)
+ for optional_action, args, option_string in action_tuples:
+ take_action(optional_action, args, option_string)
return stop
# the list of Positionals left to be parsed; this is modified
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/argcomplete-3.6.3/argcomplete/scripts/activate_global_python_argcomplete.py
new/argcomplete-3.7.2/argcomplete/scripts/activate_global_python_argcomplete.py
---
old/argcomplete-3.6.3/argcomplete/scripts/activate_global_python_argcomplete.py
2020-02-02 01:00:00.000000000 +0100
+++
new/argcomplete-3.7.2/argcomplete/scripts/activate_global_python_argcomplete.py
2020-02-02 01:00:00.000000000 +0100
@@ -32,7 +32,9 @@
# End added by argcomplete
"""
-parser = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
+parser: argparse.ArgumentParser = argparse.ArgumentParser(
+ description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
+)
parser.add_argument("-y", "--yes", help="automatically answer yes for all
questions", action="store_true")
parser.add_argument("--dest", help='Specify the shell completion modules
directory to install into, or "-" for stdout')
parser.add_argument("--user", help="Install into user directory",
action="store_true")
@@ -40,18 +42,18 @@
args = None
-def get_local_dir():
+def get_local_dir() -> str:
try:
return subprocess.check_output(["brew", "--prefix"]).decode().strip()
except (FileNotFoundError, subprocess.CalledProcessError):
return "/usr/local"
-def get_zsh_system_dir():
+def get_zsh_system_dir() -> str:
return f"{get_local_dir()}/share/zsh/site-functions"
-def get_bash_system_dir():
+def get_bash_system_dir() -> str:
if "BASH_COMPLETION_COMPAT_DIR" in os.environ:
return os.environ["BASH_COMPLETION_COMPAT_DIR"]
elif sys.platform == "darwin":
@@ -60,15 +62,15 @@
return "/etc/bash_completion.d" # created by bash-completion
-def get_activator_dir():
+def get_activator_dir() -> str:
return
os.path.join(os.path.abspath(os.path.dirname(argcomplete.__file__)),
"bash_completion.d")
-def get_activator_path():
+def get_activator_path() -> str:
return os.path.join(get_activator_dir(), "_python-argcomplete")
-def install_to_destination(dest):
+def install_to_destination(dest: str) -> None:
activator = get_activator_path()
if dest == "-":
with open(activator) as fh:
@@ -92,7 +94,7 @@
)
-def get_consent():
+def get_consent() -> bool:
assert args is not None
if args.yes is True:
return True
@@ -106,7 +108,7 @@
return False
-def append_to_config_file(path, shellcode):
+def append_to_config_file(path: str | os.PathLike[str], shellcode: str) ->
None:
if os.path.exists(path):
with open(path, 'r') as fh:
if shellcode in fh.read():
@@ -124,23 +126,23 @@
print("Added.", file=sys.stderr)
-def link_zsh_user_rcfile(zsh_fpath=None):
+def link_zsh_user_rcfile(zsh_fpath: str | None = None) -> None:
zsh_rcfile = os.path.join(os.path.expanduser(os.environ.get("ZDOTDIR",
"~")), ".zshenv")
append_to_config_file(zsh_rcfile, zsh_shellcode.format(zsh_fpath=zsh_fpath
or get_activator_dir()))
-def link_bash_user_rcfile():
+def link_bash_user_rcfile() -> None:
bash_completion_user_file = os.path.expanduser("~/.bash_completion")
append_to_config_file(bash_completion_user_file,
bash_shellcode.format(activator=get_activator_path()))
-def link_user_rcfiles():
+def link_user_rcfiles() -> None:
# TODO: warn if running as superuser
link_zsh_user_rcfile()
link_bash_user_rcfile()
-def add_zsh_system_dir_to_fpath_for_user():
+def add_zsh_system_dir_to_fpath_for_user() -> None:
if "zsh" not in os.environ.get("SHELL", ""):
return
try:
@@ -154,7 +156,7 @@
pass
-def main():
+def main() -> None:
global args
args = parser.parse_args()
@@ -190,4 +192,4 @@
if __name__ == "__main__":
- sys.exit(main())
+ sys.exit(main()) # type: ignore[func-returns-value]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/argcomplete-3.6.3/argcomplete/scripts/python_argcomplete_check_easy_install_script.py
new/argcomplete-3.7.2/argcomplete/scripts/python_argcomplete_check_easy_install_script.py
---
old/argcomplete-3.6.3/argcomplete/scripts/python_argcomplete_check_easy_install_script.py
2020-02-02 01:00:00.000000000 +0100
+++
new/argcomplete-3.7.2/argcomplete/scripts/python_argcomplete_check_easy_install_script.py
1970-01-01 01:00:00.000000000 +0100
@@ -1,84 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright 2012-2023, 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
-
-# PEP 366
-__package__ = "argcomplete.scripts"
-
-
-def main():
- 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 # type: ignore
-
- re_match = re.match("# EASY-INSTALL-SCRIPT:
'(.+)','(.+)'", line)
- assert re_match is not None
- dist, script = re_match.groups()
- if "PYTHON_ARGCOMPLETE_OK" in
pkg_resources.get_distribution(dist).get_metadata(
- "scripts/" + script
- ):
- return 0
- elif line.startswith("# EASY-INSTALL-ENTRY-SCRIPT"):
- re_match = re.match("# EASY-INSTALL-ENTRY-SCRIPT:
'(.+)','(.+)','(.+)'", line)
- assert re_match is not None
- dist, group, name = re_match.groups()
- import pkgutil
-
- import pkg_resources # type: ignore
-
- entry_point_info =
pkg_resources.get_distribution(dist).get_entry_info(group, name)
- assert entry_point_info is not None
- module_name = entry_point_info.module_name
- with open(pkgutil.get_loader(module_name).get_filename())
as mod_fh: # type: ignore
- if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024):
- return 0
- elif line.startswith("# EASY-INSTALL-DEV-SCRIPT"):
- for line2 in lines:
- if line2.startswith("__file__"):
- re_match = re.match("__file__ = '(.+)'", line2)
- assert re_match is not None
- filename = re_match.group(1)
- with open(filename) as mod_fh:
- if "PYTHON_ARGCOMPLETE_OK" in
mod_fh.read(1024):
- return 0
- elif line.startswith("# PBR Generated"):
- re_match = re.search("from (.*) import", head)
- assert re_match is not None
- module = re_match.groups()[0]
- import pkgutil
-
- import pkg_resources # type: ignore
-
- with open(pkgutil.get_loader(module).get_filename()) as
mod_fh: # type: ignore
- if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024):
- return 0
-
- return 1
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/argcomplete-3.6.3/argcomplete/scripts/register_python_argcomplete.py
new/argcomplete-3.7.2/argcomplete/scripts/register_python_argcomplete.py
--- old/argcomplete-3.6.3/argcomplete/scripts/register_python_argcomplete.py
2020-02-02 01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/argcomplete/scripts/register_python_argcomplete.py
2020-02-02 01:00:00.000000000 +0100
@@ -32,7 +32,7 @@
__package__ = "argcomplete.scripts"
-def main():
+def main() -> None:
parser = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
@@ -76,4 +76,4 @@
if __name__ == "__main__":
- sys.exit(main())
+ sys.exit(main()) # type: ignore[func-returns-value]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/argcomplete/shell_integration.py
new/argcomplete-3.7.2/argcomplete/shell_integration.py
--- old/argcomplete-3.6.3/argcomplete/shell_integration.py 2020-02-02
01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/argcomplete/shell_integration.py 2020-02-02
01:00:00.000000000 +0100
@@ -3,6 +3,7 @@
# files with source copies of this package and derivative works is
**REQUIRED** as specified by the Apache License.
# See https://github.com/kislyuk/argcomplete for more info.
+from collections.abc import Iterable
from shlex import quote
bashcode = r"""#compdef %(executables)s
@@ -135,7 +136,13 @@
shell_codes = {"bash": bashcode, "tcsh": tcshcode, "fish": fishcode,
"powershell": powershell_code}
-def shellcode(executables, use_defaults=True, shell="bash",
complete_arguments=None, argcomplete_script=None):
+def shellcode(
+ executables: Iterable[str],
+ use_defaults: bool = True,
+ shell: str = "bash",
+ complete_arguments: Iterable[str] | None = None,
+ argcomplete_script: str | None = None,
+) -> str:
"""
Provide the shell code required to register a python executable for use
with the argcomplete module.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/pyproject.toml
new/argcomplete-3.7.2/pyproject.toml
--- old/argcomplete-3.6.3/pyproject.toml 2020-02-02 01:00:00.000000000
+0100
+++ new/argcomplete-3.7.2/pyproject.toml 2020-02-02 01:00:00.000000000
+0100
@@ -2,7 +2,7 @@
name = "argcomplete"
description = "Bash tab completion for argparse"
readme = "README.rst"
-requires-python = ">=3.8"
+requires-python = ">=3.10"
license = { text = "Apache Software License" }
authors = [{ name = "Andrey Kislyuk"}, {email = "[email protected]" }]
maintainers = [{ name = "Andrey Kislyuk"}, {email = "[email protected]" }]
@@ -15,12 +15,11 @@
"Operating System :: POSIX",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Development Status :: 5 - Production/Stable",
@@ -32,7 +31,6 @@
[project.scripts]
activate-global-python-argcomplete =
"argcomplete.scripts.activate_global_python_argcomplete:main"
-python-argcomplete-check-easy-install-script =
"argcomplete.scripts.python_argcomplete_check_easy_install_script:main"
register-python-argcomplete =
"argcomplete.scripts.register_python_argcomplete:main"
[project.optional-dependencies]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/argcomplete-3.6.3/test/test.py
new/argcomplete-3.7.2/test/test.py
--- old/argcomplete-3.6.3/test/test.py 2020-02-02 01:00:00.000000000 +0100
+++ new/argcomplete-3.7.2/test/test.py 2020-02-02 01:00:00.000000000 +0100
@@ -356,6 +356,15 @@
fp.write("test")
self.assertEqual(set(fc("a")), set(["abcdefж/", "abcaha/",
"abcxyz"]))
+ def test_file_completion_quotes_prefix(self):
+ with TempDir(prefix="test_dir_fc_quote", dir="."):
+ marker = "should_not_exist"
+ prefix = "'; touch {0}; echo '".format(marker)
+
+ self.assertFalse(os.path.exists(marker))
+ self.assertEqual(FilesCompleter()(prefix), [])
+ self.assertFalse(os.path.exists(marker))
+
def test_filescompleter_filetype_integration(self):
def make_parser():
parser = ArgumentParser()
@@ -405,6 +414,44 @@
self.assertEqual(c("def/k"), set([]))
return
+ def test_directory_completion_with_tilde(self):
+ # Regression test for https://github.com/kislyuk/argcomplete/issues/529
+ # "~/<TAB>" should expand the home directory instead of yielding
nothing
+ # (os.listdir("~") would otherwise raise FileNotFoundError).
+ completer = DirectoriesCompleter()
+
+ def c(prefix):
+ return set(completer(prefix))
+
+ old_home = os.environ.get("HOME")
+ old_userprofile = os.environ.get("USERPROFILE")
+ with TempDir(prefix="test_dir_tilde", dir=".") as tmp_dir:
+ home = os.path.realpath(tmp_dir)
+ os.environ["HOME"] = home
+ os.environ["USERPROFILE"] = home
+ try:
+ # Create some directories and files (files must be ignored)
+ os.makedirs(os.path.join(home, "abc", "baz"))
+ os.makedirs(os.path.join(home, "abb"))
+ with open(os.path.join(home, "abc1"), "w") as fp:
+ fp.write("A test")
+ # "~/" lists the home directory
+ self.assertEqual(c("~/"), set([os.path.join(home, "abb") +
"/", os.path.join(home, "abc") + "/"]))
+ # "~/ab" filters within the home directory
+ self.assertEqual(c("~/ab"), set([os.path.join(home, "abb") +
"/", os.path.join(home, "abc") + "/"]))
+ # "~/abc/" recurses into a subdirectory
+ self.assertEqual(c("~/abc/"), set([os.path.join(home, "abc",
"baz") + "/"]))
+ finally:
+ if old_home is None:
+ os.environ.pop("HOME", None)
+ else:
+ os.environ["HOME"] = old_home
+ if old_userprofile is None:
+ os.environ.pop("USERPROFILE", None)
+ else:
+ os.environ["USERPROFILE"] = old_userprofile
+ return
+
def test_default_completer(self):
def make_parser():
parser = ArgumentParser(add_help=False)
@@ -828,6 +875,7 @@
parser.add_argument("-1", choices=["bar<$>baz"])
parser.add_argument("-2", choices=[r"\* "])
parser.add_argument("-3", choices=["\"'"])
+ parser.add_argument("-4", choices=["data?", "file[1]",
"{x,--flag}"])
return parser
self.assertEqual(set(self.run_completer(make_parser(), "prog -1 ")),
{r"bar\<\$\>baz "})
@@ -835,6 +883,10 @@
self.assertEqual(set(self.run_completer(make_parser(), "prog -3 ")),
{r"\"\' "})
self.assertEqual(set(self.run_completer(make_parser(), 'prog -3 "')),
{r"\"'"})
self.assertEqual(set(self.run_completer(make_parser(), "prog -3 '")),
{"\"'\\''"})
+ self.assertEqual(
+ set(self.run_completer(make_parser(), "prog -4 ")),
+ {r"data\?", r"file\[1\]", r"\{x,--flag\}"},
+ )
self.assertEqual(set(self.run_completer(make_parser(), "prog -1 ",
shell="tcsh")), {"bar<$>baz "})
# The trailing space won't actually work correctly in tcsh.