feat(pytest): use python_functions config option (#100)

This commit is contained in:
nicos68
2025-06-23 23:28:18 +02:00
committed by GitHub
parent 34c9f6f3dc
commit ed9b4d794b
4 changed files with 64 additions and 17 deletions

View File

@@ -52,6 +52,7 @@ return function(config)
---@type neotest.Adapter
return {
name = "neotest-python",
root = base.get_root,
filter_dir = function(name)
@@ -64,7 +65,7 @@ return function(config)
local python_command = config.get_python_command(root)
local runner = config.get_runner(python_command)
local positions = lib.treesitter.parse_positions(path, base.treesitter_queries, {
local positions = lib.treesitter.parse_positions(path, base.treesitter_queries(runner, config, python_command), {
require_namespaces = runner == "unittest",
})

View File

@@ -92,18 +92,56 @@ function M.get_python_command(root)
return python_command_mem[root]
end
M.treesitter_queries = [[
---@return string
function M.get_script_path()
local paths = vim.api.nvim_get_runtime_file("neotest.py", true)
for _, path in ipairs(paths) do
if vim.endswith(path, ("neotest-python%sneotest.py"):format(lib.files.sep)) then
return path
end
end
error("neotest.py not found")
end
---@param python_command string[]
---@param config neotest-python._AdapterConfig
---@param runner string
---@return string
local function scan_test_function_pattern(runner, config, python_command)
local test_function_pattern = "^test"
if runner == "pytest" and config.pytest_discovery then
local cmd = vim.tbl_flatten({ python_command, M.get_script_path(), "--pytest-extract-test-name-template" })
local _, data = lib.process.run(cmd, { stdout = true, stderr = true })
for line in vim.gsplit(data.stdout, "\n", true) do
if string.sub(line, 1, 1) == "{" and string.find(line, "python_functions") ~= nil then
local pytest_option = vim.json.decode(line)
test_function_pattern = pytest_option.python_functions
end
end
end
return test_function_pattern
end
---@param python_command string[]
---@param config neotest-python._AdapterConfig
---@param runner string
---@return string
M.treesitter_queries = function(runner, config, python_command)
local test_function_pattern = scan_test_function_pattern(runner, config, python_command)
return string.format([[
;; Match undecorated functions
((function_definition
name: (identifier) @test.name)
(#match? @test.name "^test"))
(#match? @test.name "%s"))
@test.definition
;; Match decorated function, including decorators in definition
(decorated_definition
((function_definition
name: (identifier) @test.name)
(#match? @test.name "^test")))
(#match? @test.name "%s")))
@test.definition
;; Match decorated classes, including decorators in definition
@@ -120,23 +158,12 @@ M.treesitter_queries = [[
@namespace.definition
(#not-has-parent? @namespace.definition decorated_definition)
)
]]
]], test_function_pattern, test_function_pattern)
end
M.get_root =
lib.files.match_root_pattern("pyproject.toml", "setup.cfg", "mypy.ini", "pytest.ini", "setup.py")
---@return string
function M.get_script_path()
local paths = vim.api.nvim_get_runtime_file("neotest.py", true)
for _, path in ipairs(paths) do
if vim.endswith(path, ("neotest-python%sneotest.py"):format(lib.files.sep)) then
return path
end
end
error("neotest.py not found")
end
function M.create_dap_config(python_path, script_path, script_args, dap_args)
return vim.tbl_extend("keep", {
type = "python",

View File

@@ -58,6 +58,13 @@ def main(argv: List[str]):
collect(argv)
return
if "--pytest-extract-test-name-template" in argv:
argv.remove("--pytest-extract-test-name-template")
from .pytest import extract_test_name_template
extract_test_name_template(argv)
return
args = parser.parse_args(argv)
adapter = get_adapter(TestRunner(args.runner), args.emit_parameterized_ids)

View File

@@ -1,4 +1,5 @@
from io import StringIO
import json
from pathlib import Path
from typing import Callable, Dict, List, Optional, Union
@@ -205,5 +206,16 @@ class NeotestDebugpyPlugin:
additional_info.is_tracing -= 1
class TestNameTemplateExtractor:
@staticmethod
def pytest_collection_modifyitems(config):
config = {"python_functions": config.getini("python_functions")[0]}
print(f"\n{json.dumps(config)}\n")
def extract_test_name_template(args):
pytest.main(args=["-k", "neotest_none"], plugins=[TestNameTemplateExtractor])
def collect(args):
pytest.main(["--collect-only", "--verbosity=0", "-q"] + args)