Skip to content

Non-deterministic logging.INFO capture in pytest depending on test scope (monorepo vs single package) #14388

@VincentMalara

Description

@VincentMalara
  • a detailed description of the bug or problem you are having
  • output of pip list from the virtual environment you are using
  • pytest and operating system versions
  • minimal example if possible

Detailed description

pytest captures logging.info() calls differently depending on whether you run the full monorepo test suite or a single package. The same logging bug can be visible in one context and completely invisible in the other.

# Full monorepo — logging.info() formatting errors surface → tests FAIL
uv run pytest

# Single package — logging.info() formatting errors are silently swallowed → tests PASS
uv run pytest components/my-package/

Root cause

No log_level is set in [tool.pytest.ini_options]. The root logger therefore starts at Python's default level (WARNING). When logger.info() is called:

  • effective level = WARNINGisEnabledFor(INFO) returns False → call is a no-op, no record is created, no formatting is attempted → bug invisible
  • effective level = INFO → record is created, reaches pytest's handler, formatting is attempted → bug visible

When running the full monorepo, a fixture in another component sets the root logger to INFO. This state leaks into the rest of the test session. The my-package tests then run with INFO active and a malformed logger.info("message", value) call (missing %s placeholder) raises:

TypeError: not all arguments converted during string formatting

When running only my-package, no component ever sets the root logger to INFO, so it stays at WARNING and the bug is invisible.


Minimal example

# src/mymodule.py
import logging
logger = logging.getLogger(__name__)

def buggy():
    logger.info("value is", 42)  # ← missing %s placeholder
# tests/test_mymodule.py
from mymodule import buggy

def test_buggy():
    buggy()  # PASS if root logger is WARNING, FAIL if INFO

Running pytest tests/ alone → passes.
Running alongside a test suite that configures INFO logging → fails.


Fix

Add log_level = "INFO" to [tool.pytest.ini_options] in pyproject.toml so pytest always sets the root logger to INFO at session startup, making behavior deterministic regardless of test scope or execution order:

[tool.pytest.ini_options]
log_level = "INFO"

pytest and OS versions

  • pytest 8.4.2
  • OS: Windows 10 (win32 10.0.26100)

pip list

Full output
Package                        Version     Editable project location
------------------------------ ----------- -----------------------------------------------------------------------
altair                         5.5.0
annotated-types                0.7.0
anyio                          4.12.1
networkx                       3.3
numpy                          1.26.4
packaging                      25.0
pandas                         2.2.3
pluggy                         1.6.0
polyfactory                    3.2.0
pydantic                       2.12.5
pydantic-core                  2.41.5
pyhamcrest                     2.1.0
pytest                         8.4.2
pytest-custom-exit-code        0.3.0
pytest-mock                    3.15.1
pytest-timeout                 2.4.0
python-dotenv                  1.2.1
ruff                           0.5.7
scikit-learn                   1.3.2
scikit-learn-extra             0.3.0
scipy                          1.14.1
my-package             0.1.0       components/my-package
umap-learn                     0.5.7

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions