pytest-dev/pytest · error · ValueError
pytest_markeval_namespace() needs to return a dict, got
Error message
pytest_markeval_namespace() needs to return a dict, got {dictionary!r} What it means
When a skipif/xfail marker carries a string condition, pytest eval()s it in a globals namespace partly built from the pytest_markeval_namespace hook. Every plugin implementing that hook must return a Mapping (typically a dict) of extra names. The loop at skipping.py:106 reverses the hookresults and type-checks each entry; a non-Mapping return (None, a list, a custom object) aborts evaluation with this ValueError before the condition is ever compiled.
Solutions
- Make the hookimpl return a dict: `return {"myvar": value}` — never None.
- If you have nothing to add, return an empty dict `{}` rather than None.
- Type-check at the boundary in your hook: `result = build_namespace(); return result if isinstance(result, Mapping) else {}`.
Example fix
# before
@pytest.hookimpl
def pytest_markeval_namespace(config):
return MyNamespace() # not a Mapping
# after
@pytest.hookimpl
def pytest_markeval_namespace(config):
return {"myvar": 42} Defensive patterns
Strategy: type-guard
Validate before calling
from collections.abc import Mapping
@pytest.hookimpl
def pytest_markeval_namespace(config):
ns = _build_namespace(config)
# Validate at the source so pytest never sees a non-Mapping.
assert isinstance(ns, Mapping), f"namespace must be a Mapping, got {type(ns)!r}"
return ns Type guard
from collections.abc import Mapping
def is_markeval_namespace(value) -> bool:
"""True when value is acceptable as a pytest_markeval_namespace return."""
return isinstance(value, Mapping) Try / catch
# In a wrapper plugin that aggregates other plugins' namespaces:
try:
ns = hook.pytest_markeval_namespace(config=config)
except Exception:
ns = {}
if not isinstance(ns, Mapping):
ns = {} Prevention
- Always return a dict from pytest_markeval_namespace — never None and never a custom class.
- Return {} when you have nothing to contribute; the hook contract is Mapping, not Optional[Mapping].
- Add a unit test that asserts isinstance(result, Mapping) for your hookimpl.
When it happens
Trigger: A conftest.py or plugin registers @pytest.hookimpl(hookwrapper=False) for pytest_markeval_namespace and returns None (implicit), a list, a tuple, or a non-Mapping namespace object. The error fires the first time any test with a string skipif/xfail condition is collected and evaluated.
Common situations: Plugin author returns a custom Namespace class instead of a dict; a hookimpl that conditionally returns None; upgrading a plugin from an older pytest where the return contract was looser; copy-pasting a hookimpl that returns a list of items instead of a name->value mapping.
Related errors
- - maybe you meant pytest.mark.skipif?
- is not a string or pytest.mark.* Marker
- lowercase short options are reserved
- option dest already used by (this is the option that maps…
- Specifying plugins as objects is not supported in pytester…
AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11).
Data as JSON: /api/errors/4bf43087638a136e.
Report an issue: GitHub.
Appendix: source
Thrown at src/_pytest/skipping.py:110
If an old-style string condition is given, it is eval()'d, otherwise the
condition is bool()'d. If this fails, an appropriately formatted pytest.fail
is raised.
Returns (result, reason). The reason is only relevant if the result is True.
"""
# String condition.
if isinstance(condition, str):
globals_ = {
"os": os,
"sys": sys,
"platform": platform,
"config": item.config,
}
for dictionary in reversed(
item.ihook.pytest_markeval_namespace(config=item.config)
):
if not isinstance(dictionary, Mapping):
raise ValueError(
f"pytest_markeval_namespace() needs to return a dict, got {dictionary!r}"
)
globals_.update(dictionary)
if hasattr(item, "obj"):
globals_.update(item.obj.__globals__)
try:
filename = f"<{mark.name} condition>"
condition_code = compile(condition, filename, "eval")
result = eval(condition_code, globals_)
except SyntaxError as exc:
msglines = [
f"Error evaluating {mark.name!r} condition",
" " + condition,
" " + " " * (exc.offset or 0) + "^",
"SyntaxError: invalid syntax",
]
fail("\n".join(msglines), pytrace=False)
except Exception as exc:View on GitHub (pinned to 0d6fbdeffa)