pytest config ignored when python_functions / python_classes / describe_prefixes are customised
Problem
When using custom pytest naming conventions (e.g. should_*, it_* for functions or A_*, Describe* for classes), neotest-python silently ignores the project configuration and falls back to defaults. Tests with non-default prefixes are never discovered.
A secondary symptom is that on large projects the plugin hangs Neovim during test discovery.
Environment
- neotest-python (latest)
- pytest with custom
[tool.pytest.ini_options] in pyproject.toml
- optionally:
pytest-describe plugin
Reproduction
pyproject.toml:
[tool.pytest.ini_options]
python_functions = "should_* it_* test_*"
python_classes = "Describe* A_* Test*"
describe_prefixes = ["describe", "context"]
def describe_math():
def it_adds():
assert 1 + 1 == 2 # not discovered
class DescribeMath:
def should_multiply(self):
assert 2 * 3 == 6 # not discovered
def test_fallback():
assert True # discovered (default prefix)
Only test_fallback is discovered. Functions with it_, should_ prefixes and
classes with Describe* prefix are invisible to the plugin.
Root causes
1. Subprocess runs without cwd — reads the wrong pyproject.toml
scan_pytest_config in base.lua launches the config-extraction subprocess via
lib.process.run with no working directory. The process inherits Neovim's cwd
(which may differ from the project root), so pytest finds the wrong — or no —
pyproject.toml and returns defaults.
2. Hang on large projects
The fix attempted by passing the project root as a pytest collection path
(pytest.main(["-k", "neotest_none", root])) causes pytest to walk the entire
testpaths tree before printing the config JSON, blocking Neovim indefinitely on
large projects.
3. config.getini("describe_prefixes") crashes silently
In pytest.py, TestNameTemplateExtractor.pytest_collection_modifyitems calls
config.getini("describe_prefixes") without a try/except. When pytest-describe
is not installed this raises ValueError, crashing the subprocess before any JSON
is printed. Lua receives no output and uses hardcoded defaults.
4. Wrong fallback default for test_function_pattern
The hardcoded Lua fallback is "^test", which matches unintended names like
testing_helper or testutils and misses the it_* prefix entirely. It should
be "^test_|^it_".
5. Missing treesitter query for decorated namespace functions
Decorated test functions (@decorator\ndef test_foo()) and decorated classes are
handled, but decorated namespace/container functions are not:
@some_decorator
def describe_math(): # ← not captured as a namespace
def it_adds(): ...
Proposed fix
neotest_python/pytest.py — replace the pytest.main() call in
extract_test_name_template with direct parsing of the config files
(pyproject.toml via tomllib/tomli, pytest.ini / setup.cfg / tox.ini
via configparser). This is instantaneous, requires no pytest collection, and
correctly reads the project config regardless of the process working directory.
Also wrap getini("describe_prefixes") in try/except ValueError.
lua/neotest-python/base.lua — pass root (already computed in
discover_positions) through treesitter_queries → scan_pytest_config → the
subprocess argument list, so the Python script receives the project root explicitly.
Fix the fallback default from "^test" to "^test_|^it_". Add a sixth treesitter
query to capture decorated namespace/container functions.
lua/neotest-python/adapter.lua — pass root to base.treesitter_queries.
pytest config ignored when
python_functions/python_classes/describe_prefixesare customisedProblem
When using custom pytest naming conventions (e.g.
should_*,it_*for functions orA_*,Describe*for classes), neotest-python silently ignores the project configuration and falls back to defaults. Tests with non-default prefixes are never discovered.A secondary symptom is that on large projects the plugin hangs Neovim during test discovery.
Environment
[tool.pytest.ini_options]inpyproject.tomlpytest-describepluginReproduction
pyproject.toml:Only
test_fallbackis discovered. Functions withit_,should_prefixes andclasses with
Describe*prefix are invisible to the plugin.Root causes
1. Subprocess runs without
cwd— reads the wrongpyproject.tomlscan_pytest_configinbase.lualaunches the config-extraction subprocess vialib.process.runwith no working directory. The process inherits Neovim'scwd(which may differ from the project root), so pytest finds the wrong — or no —
pyproject.tomland returns defaults.2. Hang on large projects
The fix attempted by passing the project root as a pytest collection path
(
pytest.main(["-k", "neotest_none", root])) causes pytest to walk the entiretestpathstree before printing the config JSON, blocking Neovim indefinitely onlarge projects.
3.
config.getini("describe_prefixes")crashes silentlyIn
pytest.py,TestNameTemplateExtractor.pytest_collection_modifyitemscallsconfig.getini("describe_prefixes")without atry/except. Whenpytest-describeis not installed this raises
ValueError, crashing the subprocess before any JSONis printed. Lua receives no output and uses hardcoded defaults.
4. Wrong fallback default for
test_function_patternThe hardcoded Lua fallback is
"^test", which matches unintended names liketesting_helperortestutilsand misses theit_*prefix entirely. It shouldbe
"^test_|^it_".5. Missing treesitter query for decorated namespace functions
Decorated test functions (
@decorator\ndef test_foo()) and decorated classes arehandled, but decorated namespace/container functions are not:
Proposed fix
neotest_python/pytest.py— replace thepytest.main()call inextract_test_name_templatewith direct parsing of the config files(
pyproject.tomlviatomllib/tomli,pytest.ini/setup.cfg/tox.inivia
configparser). This is instantaneous, requires no pytest collection, andcorrectly reads the project config regardless of the process working directory.
Also wrap
getini("describe_prefixes")intry/except ValueError.lua/neotest-python/base.lua— passroot(already computed indiscover_positions) throughtreesitter_queries→scan_pytest_config→ thesubprocess argument list, so the Python script receives the project root explicitly.
Fix the fallback default from
"^test"to"^test_|^it_". Add a sixth treesitterquery to capture decorated namespace/container functions.
lua/neotest-python/adapter.lua— passroottobase.treesitter_queries.