Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import hypothesis.strategies as st
import pytest
from hydra.core.config_store import ConfigStore
from hydra.core.plugins import Plugins
from hypothesis import settings
from omegaconf import DictConfig, ListConfig

Expand Down Expand Up @@ -68,6 +69,14 @@ def cleandir() -> Iterable[str]:
@pytest.fixture()
def clean_store() -> Iterable[dict]:
"""Provides access to configstore repo and restores state after test"""
# Hydra registers its plugin-provided configs (e.g. `hydra/sweeper/basic`)
# into the global ConfigStore lazily, upon first plugin discovery. We force
# discovery here so that the snapshot below includes those configs;
# otherwise restoring the snapshot would strip them from the global store
# and break later tests that compose the Hydra config (e.g. with
# `return_hydra_config=True`).
# See: https://github.com/mit-ll-responsible-ai/hydra-zen/issues/574
Plugins.instance().discover()
prev_state = deepcopy(_store.repo)
zen_prev_state = (store._internal_repo.copy(), store._queue.copy())
yield _store.repo
Expand Down
69 changes: 69 additions & 0 deletions tests/test_launch/test_global_state_isolation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright (c) 2026 Massachusetts Institute of Technology
# SPDX-License-Identifier: MIT
import os
import textwrap

import tests

# `pytester` is enabled via `pytest_plugins = "pytester"` in tests/conftest.py

_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(tests.__file__)))


def test_clean_store_preserves_hydra_plugin_configs(pytester):
"""Regression test for https://github.com/mit-ll-responsible-ai/hydra-zen/issues/574

The `clean_store` fixture snapshots and restores Hydra's global ConfigStore.
Hydra registers its plugin-provided configs (e.g. ``hydra/sweeper/basic``)
into that store lazily, upon first plugin discovery. If the fixture snapshots
the store *before* discovery occurs and then restores that snapshot, it
strips those plugin configs from the global store -- breaking any later test
that composes the Hydra config (e.g. with ``return_hydra_config=True``) with
``MissingConfigException: Could not find 'hydra/sweeper/basic'``.

This bug is order-dependent (hence the flakiness seen across Python versions
in CI). Here we reproduce the triggering order deterministically in an
isolated pytest subprocess that uses the *real* ``clean_store`` fixture: the
first test snapshots via ``clean_store`` and then triggers discovery; the
second test then requires ``hydra/sweeper/basic``.
"""
# Make the project's real `clean_store` fixture available in the subprocess.
pytester.makeconftest(
f"""
import sys
sys.path.insert(0, {_REPO_ROOT!r})
from tests.conftest import clean_store # noqa: F401
"""
)
pytester.makepyfile(
textwrap.dedent(
"""
import pytest
from hydra import compose, initialize
from hydra.core.config_store import ConfigStore


@pytest.mark.usefixtures("clean_store")
def test_snapshot_then_trigger_discovery():
# clean_store snapshots the ConfigStore here; this compose then
# triggers Hydra's lazy plugin discovery.
cs = ConfigStore.instance()
cs.store(name="tmp", node={"a": 1})
with initialize(config_path=None, version_base="1.3"):
compose(config_name="tmp", overrides=[])


def test_requires_hydra_sweeper_basic():
# Fails with MissingConfigException if the previous test's
# clean_store teardown stripped hydra/sweeper/basic.
cs = ConfigStore.instance()
cs.store(name="tmp2", node={"a": 1})
with initialize(config_path=None, version_base="1.3"):
compose(
config_name="tmp2", overrides=[], return_hydra_config=True
)
"""
)
)
result = pytester.runpytest_subprocess("-p", "no:randomly")
result.assert_outcomes(passed=2)
6 changes: 6 additions & 0 deletions tests/test_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import pytest
from hydra.conf import HydraConf
from hydra.core.config_store import ConfigStore
from hydra.core.plugins import Plugins
from hypothesis import assume, given, note, settings
from omegaconf import DictConfig, ListConfig

Expand All @@ -35,6 +36,11 @@
@contextmanager
def clean_store():
"""Provides access to configstore repo and restores state after test"""
# Force Hydra plugin discovery before snapshotting so the snapshot includes
# plugin-provided configs (e.g. `hydra/sweeper/basic`); otherwise restoring
# it would strip them from the global store. See:
# https://github.com/mit-ll-responsible-ai/hydra-zen/issues/574
Plugins.instance().discover()
prev_state = deepcopy(cs.repo)
zen_prev_state = (default_store._internal_repo.copy(), default_store._queue.copy())
try:
Expand Down