Skip to content

Commit 5c56397

Browse files
committed
Add shell completions by shtab
See #1992 pytest --print-completion bash | sudo tee /usr/share/bash-completion/completions/pytest pytest --print-completion tcsh | sudo tee /etc/profile.d/pytest.completion.csh pytest --print-completion zsh | sudo tee /usr/share/zsh/site-functions/_pytest
1 parent abb61df commit 5c56397

File tree

10 files changed

+168
-3
lines changed

10 files changed

+168
-3
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ Will Riley
479479
William Lee
480480
Wim Glenn
481481
Wouter van Ackooy
482+
Wu Zhenyu
482483
Xixi Zhao
483484
Xuan Luong
484485
Xuecong Liao

changelog/10304.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added shell completions by shtab

setup.cfg

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
[metadata]
2+
name = pytest
3+
description = pytest: simple powerful testing with Python
4+
long_description = file: README.rst
5+
long_description_content_type = text/x-rst
6+
url = https://docs.pytest.org/en/latest/
7+
author = Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others
8+
license = MIT
9+
license_file = LICENSE
10+
platforms = unix, linux, osx, cygwin, win32
11+
classifiers =
12+
Development Status :: 6 - Mature
13+
Intended Audience :: Developers
14+
License :: OSI Approved :: MIT License
15+
Operating System :: MacOS :: MacOS X
16+
Operating System :: Microsoft :: Windows
17+
Operating System :: POSIX
18+
Programming Language :: Python :: 3
19+
Programming Language :: Python :: 3 :: Only
20+
Programming Language :: Python :: 3.7
21+
Programming Language :: Python :: 3.8
22+
Programming Language :: Python :: 3.9
23+
Programming Language :: Python :: 3.10
24+
Topic :: Software Development :: Libraries
25+
Topic :: Software Development :: Testing
26+
Topic :: Utilities
27+
keywords = test, unittest
28+
project_urls =
29+
Changelog=https://docs.pytest.org/en/stable/changelog.html
30+
Twitter=https://twitter.com/pytestdotorg
31+
Source=https://github.com/pytest-dev/pytest
32+
Tracker=https://github.com/pytest-dev/pytest/issues
33+
34+
[options]
35+
packages =
36+
_pytest
37+
_pytest._code
38+
_pytest._io
39+
_pytest.assertion
40+
_pytest.config
41+
_pytest.mark
42+
pytest
43+
install_requires =
44+
attrs>=19.2.0
45+
iniconfig
46+
packaging
47+
pluggy>=0.12,<2.0
48+
py>=1.8.2
49+
colorama;sys_platform=="win32"
50+
exceptiongroup>=1.0.0rc8;python_version<"3.11"
51+
importlib-metadata>=0.12;python_version<"3.8"
52+
tomli>=1.0.0;python_version<"3.11"
53+
python_requires = >=3.7
54+
package_dir =
55+
=src
56+
setup_requires =
57+
setuptools
58+
setuptools-scm>=6.0
59+
zip_safe = no
60+
61+
[options.entry_points]
62+
console_scripts =
63+
pytest=pytest:console_main
64+
py.test=pytest:console_main
65+
66+
[options.extras_require]
67+
completion =
68+
shtab
69+
testing =
70+
argcomplete
71+
hypothesis>=3.56
72+
mock
73+
nose
74+
pygments>=2.7.2
75+
requests
76+
shtab
77+
xmlschema
78+
79+
[options.package_data]
80+
_pytest = py.typed
81+
pytest = py.typed
82+
83+
[build_sphinx]
84+
source_dir = doc/en/
85+
build_dir = doc/build
86+
all_files = 1
87+
88+
[check-manifest]
89+
ignore =
90+
src/_pytest/_version.py
91+
92+
[devpi:upload]
93+
formats = sdist.tgz,bdist_wheel
94+
95+
[mypy]
96+
mypy_path = src
97+
check_untyped_defs = True
98+
disallow_any_generics = True
99+
ignore_missing_imports = True
100+
no_implicit_optional = True
101+
show_error_codes = True
102+
strict_equality = True
103+
warn_redundant_casts = True
104+
warn_return_any = True
105+
warn_unreachable = True
106+
warn_unused_configs = True
107+
no_implicit_reexport = True

src/_pytest/__init__.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33

4-
__all__ = ["__version__", "version_tuple"]
4+
__all__ = ["__version__", "version_tuple", "shtab", "XML_FILE", "PREAMBLE"]
55

66
try:
77
from ._version import version as __version__
@@ -11,3 +11,24 @@
1111
# unknown only works because we do poor mans version compare
1212
__version__ = "unknown"
1313
version_tuple = (0, 0, "unknown")
14+
15+
try:
16+
import shtab
17+
except ImportError:
18+
from . import _shtab as shtab
19+
20+
# https://github.com/iterative/shtab/blob/5358dda86e8ea98bf801a43a24ad73cd9f820c63/examples/customcomplete.py#L11-L22
21+
XML_FILE = {
22+
"bash": "_shtab_greeter_compgen_xml_files",
23+
"zsh": "_files -g '*.xml'",
24+
"tcsh": "f:*.xml",
25+
}
26+
PREAMBLE = {
27+
"bash": """
28+
# $1=COMP_WORDS[1]
29+
_shtab_greeter_compgen_xml_files() {
30+
compgen -d -- $1 # recurse into subdirs
31+
compgen -f -X '!*?.xml' -- $1
32+
}
33+
"""
34+
}

src/_pytest/_shtab.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""A shim of shtab."""
2+
from argparse import Action
3+
from argparse import ArgumentParser
4+
from typing import Any
5+
from typing import Dict
6+
from typing import List
7+
8+
FILE = None
9+
DIRECTORY = DIR = None
10+
11+
12+
def add_argument_to(parser: ArgumentParser, *args: List[Any], **kwargs: Dict[str, Any]):
13+
Action.complete = None # type: ignore
14+
return parser

src/_pytest/config/argparsing.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313
from typing import NoReturn
1414

1515
import _pytest._io
16+
from _pytest import PREAMBLE
17+
from _pytest import shtab
1618
from _pytest.config.exceptions import UsageError
1719
from _pytest.deprecated import check_ispytest
1820

1921

22+
2023
FILE_OR_DIR = "file_or_dir"
2124

2225

@@ -122,11 +125,19 @@ def _getparser(self) -> MyOptionParser:
122125
if group.options:
123126
desc = group.description or group.name
124127
arggroup = optparser.add_argument_group(desc)
128+
if group.name == "debugconfig":
129+
shtab.add_argument_to(arggroup, preamble=PREAMBLE)
125130
for option in group.options:
126131
n = option.names()
127132
a = option.attrs()
128-
arggroup.add_argument(*n, **a)
133+
complete = a.get("complete")
134+
if complete:
135+
del a["complete"] # type: ignore
136+
action = arggroup.add_argument(*n, **a)
137+
if complete:
138+
action.complete = complete # type: ignore
129139
file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs="*")
140+
file_or_dir_arg.complete = shtab.FILE # type: ignore
130141
# bash like autocompletion for dirs (appending '/')
131142
# Type ignored because typeshed doesn't know about argcomplete.
132143
file_or_dir_arg.completer = filescompleter # type: ignore

src/_pytest/helpconfig.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import os
99
import sys
1010

11+
from _pytest import shtab
1112
from _pytest.config import Config
1213
from _pytest.config import ExitCode
1314
from _pytest.config import PrintHelp
@@ -96,6 +97,7 @@ def pytest_addoption(parser: Parser) -> None:
9697
help="Store internal tracing debug information in this log file. "
9798
"This file is opened with 'w' and truncated as a result, care advised. "
9899
"Default: pytestdebug.log.",
100+
complete=shtab.FILE,
99101
)
100102
group._addoption( # private to use reserved lower-case short option
101103
"-o",

src/_pytest/junitxml.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import re
1818
import xml.etree.ElementTree as ET
1919

20+
from . import XML_FILE
2021
from _pytest import nodes
2122
from _pytest import timing
2223
from _pytest._code.code import ExceptionRepr
@@ -30,7 +31,6 @@
3031
from _pytest.terminal import TerminalReporter
3132
import pytest
3233

33-
3434
xml_key = StashKey["LogXML"]()
3535

3636

@@ -384,6 +384,7 @@ def pytest_addoption(parser: Parser) -> None:
384384
type=functools.partial(filename_arg, optname="--junitxml"),
385385
default=None,
386386
help="Create junit-xml style report file at given path",
387+
complete=XML_FILE,
387388
)
388389
group.addoption(
389390
"--junitprefix",

src/_pytest/logging.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from typing import TypeVar
2727

2828
from _pytest import nodes
29+
from _pytest import shtab
2930
from _pytest._io import TerminalWriter
3031
from _pytest.capture import CaptureManager
3132
from _pytest.config import _strtobool
@@ -294,6 +295,7 @@ def add_option_ini(option, dest, default=None, type=None, **kwargs):
294295
dest="log_file",
295296
default=None,
296297
help="Path to a file when logging will be written to",
298+
complete=shtab.FILE,
297299
)
298300
add_option_ini(
299301
"--log-file-mode",

src/_pytest/main.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
from _pytest import nodes
2828
import _pytest._code
29+
from _pytest import shtab
2930
from _pytest.config import Config
3031
from _pytest.config import directory_arg
3132
from _pytest.config import ExitCode
@@ -143,6 +144,7 @@ def pytest_addoption(parser: Parser) -> None:
143144
metavar="dir",
144145
type=functools.partial(directory_arg, optname="--confcutdir"),
145146
help="Only load conftest.py's relative to specified dir",
147+
complete=shtab.DIR,
146148
)
147149
group.addoption(
148150
"--noconftest",
@@ -226,6 +228,7 @@ def pytest_addoption(parser: Parser) -> None:
226228
dest="inifilename",
227229
help="Load configuration from `FILE` instead of trying to locate one of the "
228230
"implicit configuration files.",
231+
complete=shtab.FILE,
229232
)
230233
group.addoption(
231234
"--rootdir",
@@ -234,6 +237,7 @@ def pytest_addoption(parser: Parser) -> None:
234237
help="Define root directory for tests. Can be relative path: 'root_dir', './root_dir', "
235238
"'root_dir/another_dir/'; absolute path: '/home/user/root_dir'; path with variables: "
236239
"'$HOME/root_dir'.",
240+
complete=shtab.DIR,
237241
)
238242
group.addoption(
239243
"--basetemp",
@@ -245,6 +249,7 @@ def pytest_addoption(parser: Parser) -> None:
245249
"Base temporary directory for this test run. "
246250
"(Warning: this directory is removed if it exists.)"
247251
),
252+
complete=shtab.DIR,
248253
)
249254

250255

0 commit comments

Comments
 (0)