Skip to content

Commit

Permalink
pytest: rewrite in bash, support toplevel funcs, avoid nondef ones an…
Browse files Browse the repository at this point in the history
…d classes
  • Loading branch information
scop committed Jul 25, 2020
1 parent 436b0cb commit bf3f720
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ repos:
- id: mypy
args: [--config-file=test/setup.cfg]
# Intentionally not run on helpers/python (support very old versions)
exclude: completions/
exclude: completions/|test/fixtures/pytest/

- repo: https://github.com/asottile/pyupgrade
rev: v2.7.2
Expand Down
45 changes: 26 additions & 19 deletions completions/pytest
Original file line number Diff line number Diff line change
Expand Up @@ -93,29 +93,36 @@ _pytest()

if [[ $cur == *.py::*:* ]]; then
local file=${cur/.py:*/.py}
local class=${cur#*.py::}
local class=${cur#*.py::} in_class=false
local line
class=${class%%:*}
local -a methods=(
"$(awk "
/^[ \t]*class[ \t]+${class}[ \t:(]/ {flag=1; next}
\$1 == \"class\" {flag=0}
flag && \$1 == \"def\" {
sub(\"\\\(.*\",\"\",\$2); print \$2
}
flag && \$1 == \"async\" && \$2 == \"def\" {
sub(\"\\\(.*\",\"\",\$3); print \$3
}
" <"$file")")
COMPREPLY=($(compgen -P "$file::$class::" -W "${methods[@]}" -- "${cur##*:?(:)}"))
while IFS= read -r line; do
if [[ $line =~ ^class[[:space:]]+${class}[[:space:]:\(] ]]; then
in_class=true
elif [[ $line =~ ^class[[:space:]] ]]; then
in_class=false
fi
if $in_class && [[ $line =~ ^[[:space:]]+(async[[:space:]]+)?def[[:space:]]+(test_[A-Za-z0-9_]+) ]]; then
COMPREPLY+=(${BASH_REMATCH[2]})
fi
done 2>/dev/null <"$file"
((!${#COMPREPLY[@]})) ||
COMPREPLY=($(compgen -P "$file::$class::" -W '${COMPREPLY[@]}' \
-- "${cur##*:?(:)}"))
__ltrim_colon_completions "$cur"
return
elif [[ $cur == *.py:* ]]; then
local file="${cur/.py:*/.py}"
local -a classes=(
"$(awk '$1 == "class" {
sub("[:(].*","",$2); print $2
}' 2>/dev/null <"$file")")
COMPREPLY=($(compgen -P "$file::" -W "${classes[@]}" -- "${cur##*.py:?(:)}"))
local file="${cur/.py:*/.py}" line
while IFS= read -r line; do
if [[ $line =~ ^class[[:space:]]+(Test[A-Za-z0-9_]+) ]]; then
COMPREPLY+=(${BASH_REMATCH[1]})
elif [[ $line =~ ^(async[[:space:]]+)?def[[:space:]]+(test_[A-Za-z0-9_]+) ]]; then
COMPREPLY+=(${BASH_REMATCH[2]})
fi
done 2>/dev/null <"$file"
((!${#COMPREPLY[@]})) ||
COMPREPLY=($(compgen -P "$file::" -W '${COMPREPLY[@]}' \
-- "${cur##*.py:?(:)}"))
__ltrim_colon_completions "$cur"
return
fi
Expand Down
17 changes: 17 additions & 0 deletions test/fixtures/pytest/test_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Async function pytest completion fixture."""


async def test_positive():
pass


async def non_test_negative():
pass


class Testing:
async def test_positive(self):
pass

async def non_test_negative(self):
pass
37 changes: 32 additions & 5 deletions test/t/test_pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,38 @@ def test_2(self, completion):
assert completion

@pytest.mark.complete("pytest ../t/test_pytest.py:")
def test_classes(self, completion):
assert completion == ":TestPytest"
def test_classes_and_functions(self, completion):
assert completion == ":TestPytest :test_function_canary".split()

@pytest.mark.complete("pytest ../t/test_pytest.py::TestPytest::")
def test_class_methods(self, completion):
methods = inspect.getmembers(self, predicate=inspect.ismethod)
assert len(completion) == len(methods)
assert completion == [x[0] for x in methods]
methods = [
x[0]
for x in inspect.getmembers(self, predicate=inspect.ismethod)
if x[0].startswith("test_")
]
assert completion == methods

@pytest.mark.complete("pytest pytest/test_async.py:")
def test_classes_and_async_functions(self, completion):
assert completion == ":Testing :test_positive".split()

@pytest.mark.complete("pytest pytest/test_async.py::Testing::")
def test_async_class_methods(self, completion):
assert completion == "test_positive"

def non_test_cananary_method(self):
pass


def test_function_canary():
pass


def non_test_canary():
pass


class NonTestCanaryClass:
def test_is_this_function_not(self):
pass

0 comments on commit bf3f720

Please sign in to comment.