feat: async build_spec
This commit is contained in:
@@ -1,40 +1,49 @@
|
|||||||
|
local async = require("plenary.async")
|
||||||
local lib = require("neotest.lib")
|
local lib = require("neotest.lib")
|
||||||
local Path = require("plenary.path")
|
local Path = require("plenary.path")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
function M.is_test_file(file_path)
|
function M.is_test_file(file_path)
|
||||||
if not vim.endswith(file_path, ".py") then
|
if not vim.endswith(file_path, ".py") then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local elems = vim.split(file_path, Path.path.sep)
|
local elems = vim.split(file_path, Path.path.sep)
|
||||||
local file_name = elems[#elems]
|
local file_name = elems[#elems]
|
||||||
return vim.startswith(file_name, "test_")
|
return vim.startswith(file_name, "test_")
|
||||||
|
end
|
||||||
|
|
||||||
|
M.module_exists = function(module, python_command)
|
||||||
|
return lib.process.run(vim.tbl_flatten({
|
||||||
|
python_command,
|
||||||
|
"-c",
|
||||||
|
"import imp; imp.find_module('"..module.."')",
|
||||||
|
})) == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return string[]
|
---@return string[]
|
||||||
function M.get_python_command(root)
|
function M.get_python_command(root)
|
||||||
-- Use activated virtualenv.
|
-- Use activated virtualenv.
|
||||||
if vim.env.VIRTUAL_ENV then
|
if vim.env.VIRTUAL_ENV then
|
||||||
return { Path:new(vim.env.VIRTUAL_ENV, "bin", "python").filename }
|
return { Path:new(vim.env.VIRTUAL_ENV, "bin", "python").filename }
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, pattern in ipairs({ "*", ".*" }) do
|
for _, pattern in ipairs({ "*", ".*" }) do
|
||||||
local match = vim.fn.glob(Path:new(root or vim.fn.getcwd(), pattern, "pyvenv.cfg").filename)
|
local match = async.fn.glob(Path:new(root or async.fn.getcwd(), pattern, "pyvenv.cfg").filename)
|
||||||
if match ~= "" then
|
if match ~= "" then
|
||||||
return { (Path:new(match):parent() / "bin" / "python").filename }
|
return { (Path:new(match):parent() / "bin" / "python").filename }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if lib.files.exists("Pipfile") then
|
if lib.files.exists("Pipfile") then
|
||||||
return { "pipenv", "run", "python" }
|
return { "pipenv", "run", "python" }
|
||||||
end
|
end
|
||||||
-- Fallback to system Python.
|
-- Fallback to system Python.
|
||||||
return { vim.fn.exepath("python3") or vim.fn.exepath("python") or "python" }
|
return { async.fn.exepath("python3") or async.fn.exepath("python") or "python" }
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.parse_positions(file_path)
|
function M.parse_positions(file_path)
|
||||||
local query = [[
|
local query = [[
|
||||||
((function_definition
|
((function_definition
|
||||||
name: (identifier) @test.name)
|
name: (identifier) @test.name)
|
||||||
(#match? @test.name "^test_"))
|
(#match? @test.name "^test_"))
|
||||||
@@ -44,18 +53,19 @@ function M.parse_positions(file_path)
|
|||||||
name: (identifier) @namespace.name)
|
name: (identifier) @namespace.name)
|
||||||
@namespace.definition
|
@namespace.definition
|
||||||
]]
|
]]
|
||||||
return lib.treesitter.parse_positions(file_path, query)
|
return lib.treesitter.parse_positions(file_path, query)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.get_strategy_config(strategy, python_script, args)
|
function M.get_strategy_config(strategy, python, python_script, args)
|
||||||
local config = {
|
local config = {
|
||||||
dap = function()
|
dap = function()
|
||||||
return {
|
return {
|
||||||
type = "python",
|
type = "python",
|
||||||
name = "Neotest Debugger",
|
name = "Neotest Debugger",
|
||||||
request = "launch",
|
request = "launch",
|
||||||
|
python = python,
|
||||||
program = python_script,
|
program = python_script,
|
||||||
cwd = vim.fn.getcwd(),
|
cwd = async.fn.getcwd(),
|
||||||
args = args,
|
args = args,
|
||||||
justMyCode= false,
|
justMyCode= false,
|
||||||
}
|
}
|
||||||
@@ -67,3 +77,4 @@ function M.get_strategy_config(strategy, python_script, args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
local logger = require("neotest.logging")
|
local async = require("plenary.async")
|
||||||
local Path = require("plenary.path")
|
local Path = require("plenary.path")
|
||||||
local lib = require("neotest.lib")
|
local lib = require("neotest.lib")
|
||||||
local base = require("neotest-python.base")
|
local base = require("neotest-python.base")
|
||||||
@@ -17,7 +17,13 @@ local get_args = function(runner, position)
|
|||||||
return lib.vim_test.collect_args("python", runner, position)
|
return lib.vim_test.collect_args("python", runner, position)
|
||||||
end
|
end
|
||||||
|
|
||||||
local get_runner = function()
|
local stored_runners = {}
|
||||||
|
|
||||||
|
local get_runner = function(python_command)
|
||||||
|
local command_str = table.concat(python_command, " ")
|
||||||
|
if stored_runners[command_str] then
|
||||||
|
return stored_runners[command_str]
|
||||||
|
end
|
||||||
local vim_test_runner = vim.g["test#python#runner"]
|
local vim_test_runner = vim.g["test#python#runner"]
|
||||||
if vim_test_runner == "pyunit" then
|
if vim_test_runner == "pyunit" then
|
||||||
return "unittest"
|
return "unittest"
|
||||||
@@ -25,16 +31,21 @@ local get_runner = function()
|
|||||||
if vim_test_runner and lib.func_util.index({ "unittest", "pytest" }, vim_test_runner) then
|
if vim_test_runner and lib.func_util.index({ "unittest", "pytest" }, vim_test_runner) then
|
||||||
return vim_test_runner
|
return vim_test_runner
|
||||||
end
|
end
|
||||||
if vim.fn.executable("pytest") == 1 then
|
local runner = base.module_exists("pytest", python_command) and "pytest" or "unittest"
|
||||||
return "pytest"
|
stored_runners[command_str] = runner
|
||||||
end
|
return runner
|
||||||
return "unittest"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@type NeotestAdapter
|
---@type NeotestAdapter
|
||||||
local PythonNeotestAdapter = { name = "neotest-python" }
|
local PythonNeotestAdapter = { name = "neotest-python" }
|
||||||
|
|
||||||
PythonNeotestAdapter.root = lib.files.match_root_pattern("pyproject.toml", "setup.cfg", "mypy.ini", "pytest.ini", "setup.py")
|
PythonNeotestAdapter.root = lib.files.match_root_pattern(
|
||||||
|
"pyproject.toml",
|
||||||
|
"setup.cfg",
|
||||||
|
"mypy.ini",
|
||||||
|
"pytest.ini",
|
||||||
|
"setup.py"
|
||||||
|
)
|
||||||
|
|
||||||
function PythonNeotestAdapter.is_test_file(file_path)
|
function PythonNeotestAdapter.is_test_file(file_path)
|
||||||
return base.is_test_file(file_path)
|
return base.is_test_file(file_path)
|
||||||
@@ -53,18 +64,23 @@ function PythonNeotestAdapter.discover_positions(path)
|
|||||||
name: (identifier) @namespace.name)
|
name: (identifier) @namespace.name)
|
||||||
@namespace.definition
|
@namespace.definition
|
||||||
]]
|
]]
|
||||||
|
local root = PythonNeotestAdapter.root(path)
|
||||||
|
local python = base.get_python_command(root)
|
||||||
|
local runner = get_runner(python)
|
||||||
return lib.treesitter.parse_positions(path, query, {
|
return lib.treesitter.parse_positions(path, query, {
|
||||||
require_namespaces = get_runner() == "unittest",
|
require_namespaces = runner == "unittest",
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@async
|
||||||
---@param args NeotestRunArgs
|
---@param args NeotestRunArgs
|
||||||
---@return NeotestRunSpec
|
---@return NeotestRunSpec
|
||||||
function PythonNeotestAdapter.build_spec(args)
|
function PythonNeotestAdapter.build_spec(args)
|
||||||
local position = args.tree:data()
|
local position = args.tree:data()
|
||||||
local results_path = vim.fn.tempname()
|
local results_path = async.fn.tempname()
|
||||||
local runner = get_runner()
|
local root = PythonNeotestAdapter.root(position.path)
|
||||||
local python = base.get_python_command(vim.fn.getcwd())
|
local python = base.get_python_command(root)
|
||||||
|
local runner = get_runner(python)
|
||||||
local script_args = vim.tbl_flatten({
|
local script_args = vim.tbl_flatten({
|
||||||
"--results-file",
|
"--results-file",
|
||||||
results_path,
|
results_path,
|
||||||
@@ -86,7 +102,7 @@ function PythonNeotestAdapter.build_spec(args)
|
|||||||
context = {
|
context = {
|
||||||
results_path = results_path,
|
results_path = results_path,
|
||||||
},
|
},
|
||||||
strategy = base.get_strategy_config(args.strategy, python_script, script_args),
|
strategy = base.get_strategy_config(args.strategy, python, python_script, script_args),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -76,19 +76,8 @@ class PytestNeotestAdapter(NeotestAdapter):
|
|||||||
"errors": errors,
|
"errors": errors,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
results[abs_path] = self.update_result(
|
|
||||||
results.get(abs_path),
|
|
||||||
{
|
|
||||||
"short": None,
|
|
||||||
"status": NeotestResultStatus(report.outcome),
|
|
||||||
"errors": errors,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
pytest.main(args=args, plugins=[NeotestResultCollector])
|
pytest.main(args=args, plugins=[NeotestResultCollector])
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def update_report(self, report: Optional[Dict], update: Dict):
|
|
||||||
...
|
|
||||||
|
@@ -76,14 +76,6 @@ class UnittestNeotestAdapter(NeotestAdapter):
|
|||||||
"short": None,
|
"short": None,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
results[case_file] = self.update_result(
|
|
||||||
results.get(case_file),
|
|
||||||
{
|
|
||||||
"status": NeotestResultStatus.FAILED,
|
|
||||||
"errors": [{"message": message, "line": error_line}],
|
|
||||||
"short": None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
for case, message in result.skipped:
|
for case, message in result.skipped:
|
||||||
results[self.case_id(case)] = self.update_result(
|
results[self.case_id(case)] = self.update_result(
|
||||||
results[self.case_id(case)],
|
results[self.case_id(case)],
|
||||||
|
Reference in New Issue
Block a user