Skip to content

multiple python_functions patterns in pyproject.toml causes only first pattern to be recognized #110

@sruizr

Description

@sruizr

Bug: multiple python_functions patterns in pyproject.toml causes only first pattern to be recognized

Description

When python_functions is configured with multiple space-separated patterns in pyproject.toml, neotest-python only recognizes test functions matching the first pattern. Functions matching subsequent patterns are silently ignored in the neotest UI, even though pytest itself collects them correctly.

Environment

  • neotest-python version: e6df4f1 (origin/master, origin/HEAD)
  • Neovim version: v0.12.1
  • pytest version: 9.0.3

Configuration

pyproject.toml:

[tool.pytest.ini_options]
python_functions = "should_* scenario_* test_*"

neotest setup:

require("neotest-python")({
  runner = "pytest",
  pytest_discover_instances = true,
})

Steps to reproduce

  1. Configure python_functions with multiple patterns in pyproject.toml as above
  2. Enable pytest_discover_instances = true in neotest-python config
  3. Open a test file containing functions with different prefixes, e.g. should_foo, scenario_bar, test_baz
  4. Observe that only should_* functions appear in neotest — scenario_* and test_* are not discovered

Expected behaviour

All functions matching any of the configured patterns should be discovered and displayed by neotest.

Actual behaviour

Only functions matching the first pattern (should_*) is discovered.

Running the extraction script manually confirms the problem:

cd /your/project && python ~/.local/share/nvim/lazy/neotest-python/neotest.py --pytest-extract-test-name-template
# returns: {"python_functions": "should_*"}
# expected: {"python_functions": "should_* scenario_* test_*"}

Root cause (two layers)

Layer 1 — Python script (neotest_python/pytest.py line 243):

config.getini("python_functions") returns a list of all configured patterns, but only the first element is taken:

# bug
config = {"python_functions": config.getini("python_functions")[0]}

This means the JSON output sent to the Lua layer already loses all patterns after the first.

Layer 2 — Lua (lua/neotest-python/base.lua line ~120):

Even if the Python script were fixed to return all patterns as a space-separated string, the Lua layer assigns the raw string directly into a treesitter #match? predicate:

-- bug
test_function_pattern = pytest_option.python_functions
-- result: "should_* scenario_* test_*" passed as a single regex

The treesitter #match? predicate expects a single regex pattern. A space-separated glob string is not valid regex alternation.

Fix (both files required)

neotest_python/pytest.py line 243:

# before
config = {"python_functions": config.getini("python_functions")[0]}
# after
config = {"python_functions": " ".join(config.getini("python_functions"))}

lua/neotest-python/base.lua line ~120:

-- before
test_function_pattern = pytest_option.python_functions
-- after
local patterns = vim.split(pytest_option.python_functions, " ", { trimempty = true })
local regex_parts = vim.tbl_map(function(p)
  return "^" .. p:gsub("%*", "")
end, patterns)
test_function_pattern = table.concat(regex_parts, "|")
-- result: "^should_|^scenario_|^test_"

This produces a valid treesitter regex alternation that correctly matches all configured prefixes.

Verification

After applying both fixes, all should_*, scenario_* and test_* functions are correctly discovered by neotest. Running pytest --collect-only was always collecting them correctly, confirming the issue was entirely within neotest-python's pattern handling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions