This commit is contained in:
wzy 2023-04-11 13:57:28 +01:00 committed by GitHub
commit 5fe8661eae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 65 additions and 3 deletions

View File

@ -381,6 +381,7 @@ Wil Cooley
William Lee William Lee
Wim Glenn Wim Glenn
Wouter van Ackooy Wouter van Ackooy
Wu Zhenyu
Xixi Zhao Xixi Zhao
Xuan Luong Xuan Luong
Xuecong Liao Xuecong Liao

View File

@ -0,0 +1 @@
Added shell completions by shtab

View File

@ -65,6 +65,8 @@ console_scripts =
py.test=pytest:console_main py.test=pytest:console_main
[options.extras_require] [options.extras_require]
completion =
shtab
testing = testing =
argcomplete argcomplete
attrs>=19.2.0 attrs>=19.2.0
@ -73,6 +75,7 @@ testing =
nose nose
pygments>=2.7.2 pygments>=2.7.2
requests requests
shtab
xmlschema xmlschema
[options.package_data] [options.package_data]

View File

@ -1,4 +1,4 @@
__all__ = ["__version__", "version_tuple"] __all__ = ["__version__", "version_tuple", "shtab", "XML_FILE", "PREAMBLE"]
try: try:
from ._version import version as __version__, version_tuple from ._version import version as __version__, version_tuple
@ -7,3 +7,24 @@ except ImportError: # pragma: no cover
# unknown only works because we do poor mans version compare # unknown only works because we do poor mans version compare
__version__ = "unknown" __version__ = "unknown"
version_tuple = (0, 0, "unknown") # type:ignore[assignment] version_tuple = (0, 0, "unknown") # type:ignore[assignment]
try:
import shtab
except ImportError:
from . import _shtab as shtab
# https://github.com/iterative/shtab/blob/5358dda86e8ea98bf801a43a24ad73cd9f820c63/examples/customcomplete.py#L11-L22
XML_FILE = {
"bash": "_shtab_greeter_compgen_xml_files",
"zsh": "_files -g '*.xml'",
"tcsh": "f:*.xml",
}
PREAMBLE = {
"bash": """
# $1=COMP_WORDS[1]
_shtab_greeter_compgen_xml_files() {
compgen -d -- $1 # recurse into subdirs
compgen -f -X '!*?.xml' -- $1
}
"""
}

14
src/_pytest/_shtab.py Normal file
View File

@ -0,0 +1,14 @@
"""A shim of shtab."""
from argparse import Action
from argparse import ArgumentParser
from typing import Any
from typing import Dict
from typing import List
FILE = None
DIRECTORY = DIR = None
def add_argument_to(parser: ArgumentParser, *args: List[Any], **kwargs: Dict[str, Any]):
Action.complete = None # type: ignore
return parser

View File

@ -17,6 +17,8 @@ from typing import TYPE_CHECKING
from typing import Union from typing import Union
import _pytest._io import _pytest._io
from _pytest import PREAMBLE
from _pytest import shtab
from _pytest.compat import final from _pytest.compat import final
from _pytest.config.exceptions import UsageError from _pytest.config.exceptions import UsageError
from _pytest.deprecated import ARGUMENT_PERCENT_DEFAULT from _pytest.deprecated import ARGUMENT_PERCENT_DEFAULT
@ -27,6 +29,7 @@ from _pytest.deprecated import check_ispytest
if TYPE_CHECKING: if TYPE_CHECKING:
from typing_extensions import Literal from typing_extensions import Literal
FILE_OR_DIR = "file_or_dir" FILE_OR_DIR = "file_or_dir"
@ -124,11 +127,19 @@ class Parser:
if group.options: if group.options:
desc = group.description or group.name desc = group.description or group.name
arggroup = optparser.add_argument_group(desc) arggroup = optparser.add_argument_group(desc)
if group.name == "debugconfig":
shtab.add_argument_to(arggroup, preamble=PREAMBLE)
for option in group.options: for option in group.options:
n = option.names() n = option.names()
a = option.attrs() a = option.attrs()
arggroup.add_argument(*n, **a) complete = a.get("complete")
if complete:
del a["complete"] # type: ignore
action = arggroup.add_argument(*n, **a)
if complete:
action.complete = complete # type: ignore
file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs="*") file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs="*")
file_or_dir_arg.complete = shtab.FILE # type: ignore
# bash like autocompletion for dirs (appending '/') # bash like autocompletion for dirs (appending '/')
# Type ignored because typeshed doesn't know about argcomplete. # Type ignored because typeshed doesn't know about argcomplete.
file_or_dir_arg.completer = filescompleter # type: ignore file_or_dir_arg.completer = filescompleter # type: ignore

View File

@ -7,6 +7,7 @@ from typing import Optional
from typing import Union from typing import Union
import pytest import pytest
from _pytest import shtab
from _pytest.config import Config from _pytest.config import Config
from _pytest.config import ExitCode from _pytest.config import ExitCode
from _pytest.config import PrintHelp from _pytest.config import PrintHelp
@ -86,6 +87,7 @@ def pytest_addoption(parser: Parser) -> None:
help="Store internal tracing debug information in this log file. " help="Store internal tracing debug information in this log file. "
"This file is opened with 'w' and truncated as a result, care advised. " "This file is opened with 'w' and truncated as a result, care advised. "
"Default: pytestdebug.log.", "Default: pytestdebug.log.",
complete=shtab.FILE,
) )
group._addoption( group._addoption(
"-o", "-o",

View File

@ -21,6 +21,7 @@ from typing import Tuple
from typing import Union from typing import Union
import pytest import pytest
from . import XML_FILE
from _pytest import nodes from _pytest import nodes
from _pytest import timing from _pytest import timing
from _pytest._code.code import ExceptionRepr from _pytest._code.code import ExceptionRepr
@ -33,7 +34,6 @@ from _pytest.reports import TestReport
from _pytest.stash import StashKey from _pytest.stash import StashKey
from _pytest.terminal import TerminalReporter from _pytest.terminal import TerminalReporter
xml_key = StashKey["LogXML"]() xml_key = StashKey["LogXML"]()
@ -390,6 +390,7 @@ def pytest_addoption(parser: Parser) -> None:
type=functools.partial(filename_arg, optname="--junitxml"), type=functools.partial(filename_arg, optname="--junitxml"),
default=None, default=None,
help="Create junit-xml style report file at given path", help="Create junit-xml style report file at given path",
complete=XML_FILE,
) )
group.addoption( group.addoption(
"--junitprefix", "--junitprefix",

View File

@ -19,6 +19,7 @@ from typing import TypeVar
from typing import Union from typing import Union
from _pytest import nodes from _pytest import nodes
from _pytest import shtab
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.capture import CaptureManager from _pytest.capture import CaptureManager
from _pytest.compat import final from _pytest.compat import final
@ -35,6 +36,7 @@ from _pytest.main import Session
from _pytest.stash import StashKey from _pytest.stash import StashKey
from _pytest.terminal import TerminalReporter from _pytest.terminal import TerminalReporter
if TYPE_CHECKING: if TYPE_CHECKING:
logging_StreamHandler = logging.StreamHandler[StringIO] logging_StreamHandler = logging.StreamHandler[StringIO]
@ -272,6 +274,7 @@ def pytest_addoption(parser: Parser) -> None:
dest="log_file", dest="log_file",
default=None, default=None,
help="Path to a file when logging will be written to", help="Path to a file when logging will be written to",
complete=shtab.FILE,
) )
add_option_ini( add_option_ini(
"--log-file-level", "--log-file-level",

View File

@ -22,6 +22,7 @@ from typing import Union
import _pytest._code import _pytest._code
from _pytest import nodes from _pytest import nodes
from _pytest import shtab
from _pytest.compat import final from _pytest.compat import final
from _pytest.compat import overload from _pytest.compat import overload
from _pytest.config import Config from _pytest.config import Config
@ -127,6 +128,7 @@ def pytest_addoption(parser: Parser) -> None:
dest="inifilename", dest="inifilename",
help="Load configuration from `file` instead of trying to locate one of the " help="Load configuration from `file` instead of trying to locate one of the "
"implicit configuration files", "implicit configuration files",
complete=shtab.FILE,
) )
group._addoption( group._addoption(
"--continue-on-collection-errors", "--continue-on-collection-errors",
@ -142,6 +144,7 @@ def pytest_addoption(parser: Parser) -> None:
help="Define root directory for tests. Can be relative path: 'root_dir', './root_dir', " help="Define root directory for tests. Can be relative path: 'root_dir', './root_dir', "
"'root_dir/another_dir/'; absolute path: '/home/user/root_dir'; path with variables: " "'root_dir/another_dir/'; absolute path: '/home/user/root_dir'; path with variables: "
"'$HOME/root_dir'.", "'$HOME/root_dir'.",
complete=shtab.DIR,
) )
group = parser.getgroup("collect", "collection") group = parser.getgroup("collect", "collection")
@ -182,6 +185,7 @@ def pytest_addoption(parser: Parser) -> None:
metavar="dir", metavar="dir",
type=functools.partial(directory_arg, optname="--confcutdir"), type=functools.partial(directory_arg, optname="--confcutdir"),
help="Only load conftest.py's relative to specified dir", help="Only load conftest.py's relative to specified dir",
complete=shtab.DIR,
) )
group.addoption( group.addoption(
"--noconftest", "--noconftest",
@ -225,6 +229,7 @@ def pytest_addoption(parser: Parser) -> None:
"Base temporary directory for this test run. " "Base temporary directory for this test run. "
"(Warning: this directory is removed if it exists.)" "(Warning: this directory is removed if it exists.)"
), ),
complete=shtab.DIR,
) )