@@ -144,16 +144,72 @@ Manual Context Management
144144 To get a copy of the current context use the
145145 :func: `~contextvars.copy_context ` function.
146146
147+ When *entering * a Context, either by calling the :meth: `Context.run ` method
148+ or using the Context object as a :term: `context manager `, the Context becomes
149+ the *current Context *. When *exiting * the current Context, either by
150+ returning from the callback passed to :meth: `Context.run ` or by exiting the
151+ :keyword: `with ` statement suite, the current Context reverts back to what it
152+ was before the exited Context was entered.
153+
154+ Attempting to do any of the following will raise a :exc: `RuntimeError `:
155+
156+ * Entering an already entered Context.
157+ * Exiting from a Context that is not the current Context.
158+ * Exiting a Context from a different thread than the one used to enter the
159+ Context.
160+
161+ After exiting a Context, it can later be re-entered (from any thread).
162+
163+ Any changes to :class: `ContextVar ` values via the :meth: `ContextVar.set `
164+ method are recorded in the current Context. The :meth: `ContextVar.get `
165+ method returns the value associated with the current Context. Thus, exiting
166+ a Context effectively reverts any changes made to context variables while the
167+ Context was entered. (If desired, the values can be restored by re-entering
168+ the Context.)
169+
147170 Context implements the :class: `collections.abc.Mapping ` interface.
148171
172+ .. versionadded :: 3.12
173+ A Context object can be used as a :term: `context manager `. The
174+ :meth: `Context.__enter__ ` and :meth: `Context.__exit__ ` methods
175+ (automatically called by the :keyword: `with ` statement) enters and exits
176+ the Context, respectively. The value returned from
177+ :meth: `Context.__enter__ `, and thus bound to the identifier given in the
178+ :keyword: `with ` statement's :keyword: `!as ` clause if present, is the
179+ Context object itself.
180+
181+ Example:
182+
183+ .. testcode ::
184+
185+ import contextvars
186+
187+ var = contextvars.ContextVar("var")
188+ var.set("initial")
189+
190+ # Copy the current Context and enter it.
191+ with contextvars.copy_context() as ctx:
192+ var.set("updated")
193+ assert var in ctx
194+ assert ctx[var] == "updated"
195+ assert var.get() == "updated"
196+
197+ # Exited ctx, so the value of var should have reverted.
198+ assert var.get() == "initial"
199+ # But the updated value is still recorded in ctx.
200+ assert ctx[var] == "updated"
201+
202+ # Re-entering ctx should restore the updated value of var.
203+ with ctx:
204+ assert var.get() == "updated"
205+
149206 .. method :: run(callable, *args, **kwargs)
150207
151- Execute ``callable(*args, **kwargs) `` code in the context object
152- the * run * method is called on. Return the result of the execution
153- or propagate an exception if one occurred.
208+ Enters the Context, executes ``callable(*args, **kwargs) ``, then exits the
209+ context. Returns `` callable ``'s return value, or propagates an exception
210+ if one occurred.
154211
155- Any changes to any context variables that *callable * makes will
156- be contained in the context object::
212+ Example::
157213
158214 var = ContextVar('var')
159215 var.set('spam')
@@ -181,10 +237,6 @@ Manual Context Management
181237 # However, outside of 'ctx', 'var' is still set to 'spam':
182238 # var.get() == 'spam'
183239
184- The method raises a :exc: `RuntimeError ` when called on the same
185- context object from more than one OS thread, or when called
186- recursively.
187-
188240 .. method :: copy()
189241
190242 Return a shallow copy of the context object.
0 commit comments