feat: init commit
This commit is contained in:
69
lua/neotest-python/base.lua
Normal file
69
lua/neotest-python/base.lua
Normal file
@@ -0,0 +1,69 @@
|
||||
local lib = require("neotest.lib")
|
||||
local Path = require("plenary.path")
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.is_test_file(file_path)
|
||||
if not vim.endswith(file_path, ".py") then
|
||||
return false
|
||||
end
|
||||
local elems = vim.split(file_path, Path.path.sep)
|
||||
local file_name = elems[#elems]
|
||||
return vim.startswith(file_name, "test_")
|
||||
end
|
||||
|
||||
---@return string[]
|
||||
function M.get_python_command(root)
|
||||
-- Use activated virtualenv.
|
||||
if vim.env.VIRTUAL_ENV then
|
||||
return { Path:new(vim.env.VIRTUAL_ENV, "bin", "python").filename }
|
||||
end
|
||||
|
||||
for _, pattern in ipairs({ "*", ".*" }) do
|
||||
local match = vim.fn.glob(Path:new(root or vim.fn.getcwd(), pattern, "pyvenv.cfg").filename)
|
||||
if match ~= "" then
|
||||
return { (Path:new(match):parent() / "bin" / "python").filename }
|
||||
end
|
||||
end
|
||||
|
||||
if lib.files.exists("Pipfile") then
|
||||
return { "pipenv", "run", "python" }
|
||||
end
|
||||
-- Fallback to system Python.
|
||||
return { vim.fn.exepath("python3") or vim.fn.exepath("python") or "python" }
|
||||
end
|
||||
|
||||
function M.parse_positions(file_path)
|
||||
local query = [[
|
||||
((function_definition
|
||||
name: (identifier) @test.name)
|
||||
(#match? @test.name "^test_"))
|
||||
@test.definition
|
||||
|
||||
(class_definition
|
||||
name: (identifier) @namespace.name)
|
||||
@namespace.definition
|
||||
]]
|
||||
return lib.treesitter.parse_positions(file_path, query)
|
||||
end
|
||||
|
||||
function M.get_strategy_config(strategy, python, python_script, args)
|
||||
local config = {
|
||||
dap = function()
|
||||
return {
|
||||
type = "python",
|
||||
name = "Neotest Debugger",
|
||||
request = "launch",
|
||||
python = python,
|
||||
program = python_script,
|
||||
cwd = vim.fn.getcwd(),
|
||||
args = args,
|
||||
}
|
||||
end,
|
||||
}
|
||||
if config[strategy] then
|
||||
return config[strategy]()
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
131
lua/neotest-python/init.lua
Normal file
131
lua/neotest-python/init.lua
Normal file
@@ -0,0 +1,131 @@
|
||||
local logger = require("neotest.logging")
|
||||
local Path = require("plenary.path")
|
||||
local lib = require("neotest.lib")
|
||||
local base = require("neotest-python.base")
|
||||
|
||||
local function script_path()
|
||||
local str = debug.getinfo(2, "S").source:sub(2)
|
||||
return str:match("(.*/)")
|
||||
end
|
||||
|
||||
local python_script = (Path.new(script_path()):parent():parent() / "neotest.py").filename
|
||||
|
||||
local get_args = function(runner, position)
|
||||
if runner == "unittest" then
|
||||
runner = "pyunit"
|
||||
end
|
||||
return lib.vim_test.collect_args("python", runner, position)
|
||||
end
|
||||
|
||||
local get_runner = function()
|
||||
local vim_test_runner = vim.g["test#python#runner"]
|
||||
if vim_test_runner == "pyunit" then
|
||||
return "unittest"
|
||||
end
|
||||
if vim_test_runner and lib.func_util.index({ "unittest", "pytest" }, vim_test_runner) then
|
||||
return vim_test_runner
|
||||
end
|
||||
if vim.fn.executable("pytest") == 1 then
|
||||
return "pytest"
|
||||
end
|
||||
return "unittest"
|
||||
end
|
||||
|
||||
---@type NeotestAdapter
|
||||
local PythonNeotestAdapter = {name = "neotest-python"}
|
||||
|
||||
function PythonNeotestAdapter.is_test_file(file_path)
|
||||
return base.is_test_file(file_path)
|
||||
end
|
||||
|
||||
---@async
|
||||
---@return Tree | nil
|
||||
function PythonNeotestAdapter.discover_positions(path)
|
||||
if path and not lib.files.is_dir(path) then
|
||||
local query = [[
|
||||
((function_definition
|
||||
name: (identifier) @test.name)
|
||||
(#match? @test.name "^test_"))
|
||||
@test.definition
|
||||
|
||||
(class_definition
|
||||
name: (identifier) @namespace.name)
|
||||
@namespace.definition
|
||||
]]
|
||||
return lib.treesitter.parse_positions(path, query, {
|
||||
require_namespaces = get_runner() == "unittest",
|
||||
})
|
||||
end
|
||||
local files = lib.func_util.filter_list(base.is_test_file, lib.files.find({ path }))
|
||||
return lib.files.parse_dir_from_files(path, files)
|
||||
end
|
||||
|
||||
---@param args NeotestRunArgs
|
||||
---@return NeotestRunSpec
|
||||
function PythonNeotestAdapter.build_spec(args)
|
||||
local results_path = vim.fn.tempname()
|
||||
local runner = get_runner()
|
||||
local python = base.get_python_command(vim.fn.getcwd())
|
||||
local script_args = vim.tbl_flatten({
|
||||
"--results-file",
|
||||
results_path,
|
||||
"--runner",
|
||||
runner,
|
||||
"--",
|
||||
get_args(runner, args.position),
|
||||
})
|
||||
if args.position then
|
||||
local pos = args.position
|
||||
table.insert(script_args, pos.id)
|
||||
end
|
||||
local command = vim.tbl_flatten({
|
||||
python,
|
||||
python_script,
|
||||
script_args,
|
||||
})
|
||||
return {
|
||||
command = command,
|
||||
context = {
|
||||
results_path = results_path,
|
||||
},
|
||||
strategy = base.get_strategy_config(args.strategy, python, python_script, script_args),
|
||||
}
|
||||
end
|
||||
|
||||
---@async
|
||||
---@param spec NeotestRunSpec
|
||||
---@param result NeotestStrategyResult
|
||||
---@return NeotestResult[]
|
||||
function PythonNeotestAdapter.results(spec, result)
|
||||
-- TODO: Find out if this JSON option is supported in future
|
||||
local success, data = pcall(lib.files.read, spec.context.results_path)
|
||||
if not success then
|
||||
data = "{}"
|
||||
end
|
||||
local results = vim.json.decode(data, { luanil = { object = true } })
|
||||
for _, pos_result in pairs(results) do
|
||||
result.output_path = pos_result.output_path
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
setmetatable(PythonNeotestAdapter, {
|
||||
__call = function(_, opts)
|
||||
if type(opts.args) == "function" or (type(opts.args) == "table" and opts.args.__call) then
|
||||
get_args = opts.args
|
||||
elseif opts.args then
|
||||
get_args = function()
|
||||
return opts.args
|
||||
end
|
||||
end
|
||||
if type(opts.runner) == "function" or (type(opts.runner) == "table" and opts.runner.__call) then
|
||||
get_runner = opts.runner
|
||||
elseif opts.runner then
|
||||
get_runner = function()
|
||||
return opts.runner
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
return PythonNeotestAdapter
|
Reference in New Issue
Block a user