Add scoped singleton registries to SingletonConfigurable - #953
Draft
Carreau wants to merge 5 commits into
Draft
Conversation
Introduce SingletonScope, an isolated singleton registry activated for a dynamic extent (current thread / async task) via a contextvars.ContextVar. While active, instance() on any subclass of the scope's base resolves within the scope -- creating fresh instances on first use -- without ever reading, creating, or mutating the process-global _instance. The classic (no-scope) instance()/initialized()/clear_instance() paths are left textually unchanged; scoped resolution is prepended as an early branch. - SingletonScope with covers()/get() (MRO-walk parity)/add() (write-through pre-seeding) and a re-enterable __call__ context manager - SingletonConfigurable.scope() factory and _current_scope() helper - Export SingletonScope from traitlets.config - Tests covering isolation, lazy creation by uncontrolled code, no global side effects, initialized() semantics, re-entry, pre-seeding, blast radius, nesting/LIFO restore, MRO sibling MultipleInstanceError parity, in-scope clear_instance, thread isolation, and asyncio task propagation - Docs: "Singleton scopes" section with usage/semantics and a per-thread singleton example
On the free-threaded build (e.g. 3.14t) sys.flags.thread_inherit_context defaults to true, so a new threading.Thread starts with a copy of the parent's contextvars.Context. The active SingletonScope therefore propagates into child threads and MyApp.instance() resolves within the scope instead of falling through to the process-global registry, which made test_thread_isolation fail on all 3.14t jobs. Skip the test whenever thread_inherit_context is enabled (the default on free-threaded builds, opt-in elsewhere) so CI stays green; the classic GIL-build behavior remains covered. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159wyFjYsXoVKyUN9NkNxzw
…erit_context The "threads don't inherit the scope" guarantee only holds when sys.flags.thread_inherit_context is false (the default GIL build). On the free-threaded build (e.g. 3.14t) it defaults to true, so a child thread starts with a copy of the parent context and resolves .instance() within the scope. - Docs: add a warning admonition to the "Singleton scopes" section and the SingletonScope docstring spelling out the build-dependent behavior, and correct the propagation bullet; reframe the per-thread example as working on both builds. - Emit a RuntimeWarning when a scope is activated while thread_inherit_context is enabled, pointing users at per-thread scopes. - Test the warning fires when the flag is enabled and stays silent when not. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159wyFjYsXoVKyUN9NkNxzw
The test mocked the whole sys.flags structseq with a Mock, so other code that reads integer fields off sys.flags during the scope block (surfaced on pypy-3.11) failed with "expected integer, got Mock object". Read the flag through a small _threads_inherit_context() helper and patch that in the test instead of replacing sys.flags wholesale. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159wyFjYsXoVKyUN9NkNxzw
Claude/pr 953 failure 490pjp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Introduce SingletonScope, an isolated singleton registry activated for a dynamic extent (current thread / async task) via a contextvars.ContextVar. While active, instance() on any subclass of the scope's base resolves within the scope -- creating fresh instances on first use -- without ever reading, creating, or mutating the process-global _instance. The classic (no-scope) instance()/initialized()/clear_instance() paths are left textually unchanged; scoped resolution is prepended as an early branch.