6363_GITHUB_TOKEN_ENV = "GITHUB_TOKEN"
6464
6565
66+ class _StreamCaptureBinding :
67+ """Shared capture state inherited by child execution contexts."""
68+
69+ def __init__ (self , target ) -> None :
70+ self .target = target
71+ self .active = True
72+
73+ def deactivate (self ) -> None :
74+ """Stop routing writes and release the completed capture buffer."""
75+ self .active = False
76+ self .target = None
77+
78+
6679class _ContextualTextStream :
6780 """Route writes to a task-local stream while preserving a process fallback.
6881
@@ -75,17 +88,25 @@ class _ContextualTextStream:
7588
7689 def __init__ (self , fallback , * , context_name : str ) -> None :
7790 self ._fallback = fallback
78- self ._target : contextvars .ContextVar [Any | None ] = contextvars .ContextVar (context_name , default = None )
91+ self ._binding : contextvars .ContextVar [_StreamCaptureBinding | None ] = contextvars .ContextVar (
92+ context_name , default = None
93+ )
7994
8095 def _current (self ):
81- target = self ._target .get ()
82- return self ._fallback if target is None else target
83-
84- def activate (self , target ) -> contextvars .Token [Any | None ]:
85- return self ._target .set (target )
86-
87- def reset (self , token : contextvars .Token [Any | None ]) -> None :
88- self ._target .reset (token )
96+ binding = self ._binding .get ()
97+ return self ._fallback if binding is None or not binding .active else binding .target
98+
99+ def activate (self , target ) -> tuple [contextvars .Token [_StreamCaptureBinding | None ], _StreamCaptureBinding ]:
100+ binding = _StreamCaptureBinding (target )
101+ return self ._binding .set (binding ), binding
102+
103+ def reset (
104+ self ,
105+ activation : tuple [contextvars .Token [_StreamCaptureBinding | None ], _StreamCaptureBinding ],
106+ ) -> None :
107+ token , binding = activation
108+ binding .deactivate ()
109+ self ._binding .reset (token )
89110
90111 def write (self , data ):
91112 return self ._current ().write (data )
0 commit comments