Skip to content

Cleanup: remove runtime-init globals, add public reset_logging() (follow-up to #755) #805

Description

@nabinchha

Summary

Follow-up cleanup to #755 (which fixes #388). #755 is correct and should ship as-is, but it leaves two module-global boolean latches in the runtime-init path that force tests to monkeypatch private globals:

  • data_designer.logging._logging_configured
  • data_designer.interface.data_designer._interface_runtime_initialized

This issue tracks removing both in favor of observable/idempotent state, and adding a public reset_logging() API.

Goals

  1. Derive "did Data Designer configure logging?" from observable handler state instead of a private boolean latch.
  2. Remove _interface_runtime_initialized entirely — the work it guards (configure_logging() and resolve_seed_default_model_settings()) is already idempotent (the CLI's ensure_cli_default_model_settings() already depends on this).
  3. Add a public reset_logging() so external apps can undo Data Designer's logging setup — and so tests need zero private patching.
  4. As a side effect, fix the auto_configure_logging "first-construction-wins" footgun: with the memo gone, the flag takes effect on every DataDesigner() instead of only the first one in a process.

Design sketch

logging.py

  • Mark DD-installed handlers via type (_DataDesignerStreamHandler / _DataDesignerFileHandler sharing a _DataDesignerManagedHandler mixin); _create_handler() returns these.
  • is_logging_configured()any(isinstance(h, _DataDesignerManagedHandler) for h in logging.getLogger().handlers). Delete _logging_configured.
  • Add public reset_logging() that surgically removes only DD-managed handlers (leaving foreign handlers such as pytest's caplog capture handler intact) and resets root/data_designer levels to defaults.

data_designer.py

  • Simplify _initialize_interface_runtime() to be idempotent and drop _interface_runtime_initialized + the early-return guard.
  • Trim the "first construction only" caveat from the auto_configure_logging docstring.

Behavior changes to note in the follow-up PR

  • is_logging_configured() shifts from "was ever configured this process" to "is currently configured" (only observable if someone configures then manually clears handlers; the sole internal consumer checks once at construction).
  • Runtime-init side effects are re-evaluated per construction (idempotent; no new log spam).
  • auto_configure_logging now applies per-instance.

Test plan (zero private patching)

  • Shared autouse fixture calling reset_logging() before and after each test (replaces all monkeypatch.setattr(..., "_logging_configured"/"_interface_runtime_initialized", ...) and try/finally blocks from fix: preserve preconfigured logging #755). caplog stays intact because the reset is surgical.
  • reset_logging() contract tests: clears DD handlers / flips is_logging_configured() to False, preserves foreign handlers, idempotent no-op on a clean process.
  • Interface tests: delete test_initialize_interface_runtime_runs_once (property no longer holds) → replace with an idempotency test; add a per-instance auto_configure_logging test; optionally a subprocess fresh-interpreter configure_logging() called before DataDesigner() is silently overwritten #388 regression test (reuse _run_python_snippet from test_lazy_imports.py).

Docs

  • Document reset_logging() as the public "undo" affordance that pairs with configure_logging().

Notes

  • Full design + decisions log: plans/logging-runtime-init-cleanup.md.
  • Cost: ~3 extra Path.exists() stats per DataDesigner() construction — negligible (heavyweight top-level object, never hot-looped).
  • Do not start until fix: preserve preconfigured logging #755 is merged.

Follow-up to #755. Closes #388's remaining tech debt.

Metadata

Metadata

Assignees

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