Skip to content

Do not derive rootdir from a non-regular config file - #14671

Open
apoorvdarshan wants to merge 2 commits into
pytest-dev:mainfrom
apoorvdarshan:fix-11502-devnull-rootdir
Open

apoorvdarshan wants to merge 2 commits into
pytest-dev:mainfrom
apoorvdarshan:fix-11502-devnull-rootdir

Conversation

@apoorvdarshan

Copy link
Copy Markdown

Closes #11502

Problem

Passing a non-regular file as the config file — the common case being --config-file=/dev/null to deliberately disable config file loading — made pytest derive the rootdir from its parent directory. This resulted in rootdir: /dev, and the cache plugin then emitted a confusing warning on every run when it failed to create the cache directory there:

PytestCacheWarning: could not create cache path /dev/.pytest_cache/v/cache/nodeids: [Errno 1] Operation not permitted

Reproduced on current main (9.2.0.dev) on macOS; same behavior as originally reported in #11502.

Fix

In determine_setup(), only derive the rootdir from the config file's parent directory when the given config file is a regular file. For non-regular files (character devices like /dev/null, FIFOs), fall back to the common-ancestor logic already used elsewhere in the function (with the existing filesystem-root guard). The explicitly given config file is still honored as inipath — only the rootdir derivation changes.

With the fix, pytest --config-file=/dev/null run from a project directory yields rootdir: <project dir> and no cache warning.

Checklist

  • Regression test added (TestRootdir::test_explicit_config_file_non_regular_file, skipped on Windows).
  • Changelog entry: changelog/11502.bugfix.rst.
  • Added myself to AUTHORS.
  • Ran testing/test_config.py, testing/test_findpaths.py (278 passed) and testing/test_cacheprovider.py (58 passed) locally; ruff check/format clean.
  • Allow maintainers to push and squash when merging my commits.

AI disclosure

Per the project's AI/LLM-Assisted Contributions Policy: this fix was developed with the assistance of an AI tool (Claude Code). I have reviewed the change, reproduced the bug and verified the fix locally, and I take responsibility for the contribution and will respond to review feedback personally.

When an explicitly given config file is not a regular file (e.g.
--config-file=/dev/null to disable config file loading), do not use its
parent directory as the rootdir. Previously rootdir became e.g. /dev,
and the cache plugin emitted a confusing PytestCacheWarning when it
failed to create /dev/.pytest_cache.

Closes pytest-dev#11502
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Jul 2, 2026
RonnyPfannschmidt added a commit to RonnyPfannschmidt/pytest that referenced this pull request Jul 31, 2026
Consolidates three open reports that all bottom out in how an explicitly
given config file is located and parsed. The preceding refactor turns each
of them into a small change rather than another special case in a
conditional.

Custom TOML files read [pytest], not [tool.pytest] (pytest-dev#14705)

  [tool.pytest] is reserved for the project file. A TOML file passed via -c
  now reads its configuration from [pytest] directly, exactly like
  pytest.toml does. Previously any .toml that was not pytest.toml or
  .pytest.toml was parsed with pyproject.toml semantics, so the documented
  [pytest] table in a custom file was silently ignored. This is one entry
  in CONFIG_SUFFIXES.

-c/--config-file validates its argument (pytest-dev#14716)

  Previously an invalid path either silently produced an empty
  configuration -- while still reporting `configfile:` in the header -- or
  crashed with a raw FileNotFoundError traceback, depending on its
  extension. Now a path that does not exist, a directory, and a regular
  file pytest has no loader for are each a UsageError. The supported
  extensions in the message are derived from CONFIG_SUFFIXES rather than
  restated in a separate constant.

  This is breaking for invocations that passed an unparsable file to -c and
  relied on it being ignored. The pytest-dev#14683 regression test did exactly that
  with a conftest.py and now uses a real config file; it passes --rootdir
  explicitly, so the config file was incidental to what it covers.

The rootdir is not derived from a non-regular config file (pytest-dev#11502)

  --config-file=/dev/null is a common way to load no configuration at all.
  Deriving the rootdir from its parent made the rootdir /dev, and the cache
  plugin then warned on every run that it could not create
  /dev/.pytest_cache. Such a path says nothing about where the project
  lives, so fall back to the usual common-ancestor logic.

The diagnoses come from pytest-dev#14707 (@DebadityaHait), pytest-dev#14723 (@wanxiankai) and
pytest-dev#14671 (@apoorvdarshan); the implementations differ because the table-based
dispatch makes each one smaller.

Closes pytest-dev#14705
Closes pytest-dev#14716
Closes pytest-dev#11502

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RonnyPfannschmidt added a commit to RonnyPfannschmidt/pytest that referenced this pull request Jul 31, 2026
Consolidates three open reports that all bottom out in how an explicitly
given config file is located and parsed. The preceding refactor turns each
of them into a small change rather than another special case in a
conditional.

Custom TOML files read [pytest], not [tool.pytest] (pytest-dev#14705)

  [tool.pytest] is reserved for the project file. A TOML file passed via -c
  now reads its configuration from [pytest] directly, exactly like
  pytest.toml does. Previously any .toml that was not pytest.toml or
  .pytest.toml was parsed with pyproject.toml semantics, so the documented
  [pytest] table in a custom file was silently ignored. This is one entry
  in CONFIG_SUFFIXES.

-c/--config-file validates its argument (pytest-dev#14716)

  Previously an invalid path either silently produced an empty
  configuration -- while still reporting `configfile:` in the header -- or
  crashed with a raw FileNotFoundError traceback, depending on its
  extension. Now a path that does not exist, a directory, and a regular
  file pytest has no loader for are each a UsageError. The supported
  extensions in the message are derived from CONFIG_SUFFIXES rather than
  restated in a separate constant.

  This is breaking for invocations that passed an unparsable file to -c and
  relied on it being ignored. The pytest-dev#14683 regression test did exactly that
  with a conftest.py and now uses a real config file; it passes --rootdir
  explicitly, so the config file was incidental to what it covers.

The rootdir is not derived from a non-regular config file (pytest-dev#11502)

  --config-file=/dev/null is a common way to load no configuration at all.
  Deriving the rootdir from its parent made the rootdir /dev, and the cache
  plugin then warned on every run that it could not create
  /dev/.pytest_cache. Such a path says nothing about where the project
  lives, so fall back to the usual common-ancestor logic.

The diagnoses come from pytest-dev#14707 (@DebadityaHait), pytest-dev#14723 (@wanxiankai) and
pytest-dev#14671 (@apoorvdarshan); the implementations differ because the table-based
dispatch makes each one smaller.

Closes pytest-dev#14705
Closes pytest-dev#14716
Closes pytest-dev#11502

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RonnyPfannschmidt added a commit to RonnyPfannschmidt/pytest that referenced this pull request Aug 6, 2026
Consolidates three open reports that all bottom out in how an explicitly
given config file is located and parsed. The preceding refactor turns each
of them into a small change rather than another special case in a
conditional.

Custom TOML files also read [pytest] (pytest-dev#14705)

  pytest documents [pytest] as the table its own TOML configuration files
  use, but a TOML file passed via -c was parsed with pyproject.toml
  semantics, so a [pytest] table in it was silently ignored. Such files now
  read [pytest] as well, while the [tool.pytest]/[tool.pytest.ini_options]
  tables they were previously restricted to keep working -- writing both
  styles into one file is a UsageError. Suffix dispatch makes this one
  loader; pyproject.toml itself is unaffected, as name dispatch wins.

-c/--config-file validates its argument (pytest-dev#14716)

  Previously an invalid path either silently produced an empty
  configuration -- while still reporting `configfile:` in the header -- or
  crashed with a raw FileNotFoundError traceback, depending on its
  extension. Now a path that does not exist, a directory, and a regular
  file pytest has no loader for are each a UsageError. The supported
  extensions in the message are derived from CONFIG_LOADERS_BY_SUFFIX
  rather than restated in a separate constant.

  This is breaking for invocations that passed an unparsable file to -c and
  relied on it being ignored. The pytest-dev#14683 regression test did exactly that
  with a conftest.py and now uses a real config file; it passes --rootdir
  explicitly, so the config file was incidental to what it covers.

The rootdir is not derived from a non-regular config file (pytest-dev#11502)

  --config-file=/dev/null is a common way to load no configuration at all.
  Deriving the rootdir from its parent made the rootdir /dev, and the cache
  plugin then warned on every run that it could not create
  /dev/.pytest_cache. Such a path says nothing about where the project
  lives, so fall back to the usual common-ancestor logic.

  Whether a path is parsed and whether its directory decides the rootdir
  are kept separate: a loader runs whenever one matches the name or suffix,
  so a config file that happens to be a fifo is still read, and only a path
  with no loader *and* no chance of holding configuration -- a character
  device such as /dev/null -- means "no configuration" instead of an error.

The diagnoses come from pytest-dev#14707 (@DebadityaHait), pytest-dev#14723 (@wanxiankai) and
pytest-dev#14671 (@apoorvdarshan); the implementations differ because the table-based
dispatch makes each one smaller.

Closes pytest-dev#14705
Closes pytest-dev#14716
Closes pytest-dev#11502

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unexpected PytestCacheWarning when passing /dev/null as config file for pytest

1 participant