@@ -144,51 +144,89 @@ Manual Context Management
144
144
To get a copy of the current context use the
145
145
:func: `~contextvars.copy_context ` function.
146
146
147
- Every thread will have a different top-level :class: `~contextvars.Context `
148
- object. This means that a :class: `ContextVar ` object behaves in a similar
149
- fashion to :func: `threading.local ` when values are assigned in different
150
- threads.
147
+ Each thread has its own effective stack of :class: `!Context ` objects. The
148
+ :term: `current context ` is the :class: `!Context ` object at the top of the
149
+ current thread's stack. All :class: `!Context ` objects in the stacks are
150
+ considered to be *entered *.
151
+
152
+ *Entering * a context, which can be done by calling its :meth: `~Context.run `
153
+ method, makes the context the current context by pushing it onto the top of
154
+ the current thread's context stack.
155
+
156
+ *Exiting * from the current context, which can be done by returning from the
157
+ callback passed to the :meth: `~Context.run ` method, restores the current
158
+ context to what it was before the context was entered by popping the context
159
+ off the top of the context stack.
160
+
161
+ Since each thread has its own context stack, :class: `ContextVar ` objects
162
+ behave in a similar fashion to :func: `threading.local ` when values are
163
+ assigned in different threads.
164
+
165
+ Attempting to enter an already entered context, including contexts entered in
166
+ other threads, raises a :exc: `RuntimeError `.
167
+
168
+ After exiting a context, it can later be re-entered (from any thread).
169
+
170
+ Any changes to :class: `ContextVar ` values via the :meth: `ContextVar.set `
171
+ method are recorded in the current context. The :meth: `ContextVar.get `
172
+ method returns the value associated with the current context. Exiting a
173
+ context effectively reverts any changes made to context variables while the
174
+ context was entered (if needed, the values can be restored by re-entering the
175
+ context).
151
176
152
177
Context implements the :class: `collections.abc.Mapping ` interface.
153
178
154
179
.. method :: run(callable, *args, **kwargs)
155
180
156
- Execute ``callable(*args, **kwargs) `` code in the context object
157
- the *run * method is called on. Return the result of the execution
158
- or propagate an exception if one occurred.
181
+ Enters the Context, executes ``callable(*args, **kwargs) ``, then exits the
182
+ Context. Returns *callable *'s return value, or propagates an exception if
183
+ one occurred.
184
+
185
+ Example:
186
+
187
+ .. testcode ::
188
+
189
+ import contextvars
159
190
160
- Any changes to any context variables that *callable * makes will
161
- be contained in the context object::
191
+ var = contextvars.ContextVar('var')
192
+ var.set('spam')
193
+ print(var.get()) # 'spam'
162
194
163
- var = ContextVar('var')
164
- var.set('spam')
195
+ ctx = contextvars.copy_context()
165
196
166
- def main():
167
- # 'var' was set to 'spam' before
168
- # calling 'copy_context()' and 'ctx.run(main)', so:
169
- # var.get() == ctx[var] == 'spam'
197
+ def main():
198
+ # 'var' was set to 'spam' before
199
+ # calling 'copy_context()' and 'ctx.run(main)', so:
200
+ print(var.get()) # 'spam'
201
+ print(ctx[var]) # 'spam'
170
202
171
- var.set('ham')
203
+ var.set('ham')
172
204
173
- # Now, after setting 'var' to 'ham':
174
- # var.get() == ctx[var] == 'ham'
205
+ # Now, after setting 'var' to 'ham':
206
+ print(var.get()) # 'ham'
207
+ print(ctx[var]) # 'ham'
175
208
176
- ctx = copy_context()
209
+ # Any changes that the 'main' function makes to 'var'
210
+ # will be contained in 'ctx'.
211
+ ctx.run(main)
177
212
178
- # Any changes that the 'main' function makes to 'var'
179
- # will be contained in 'ctx'.
180
- ctx.run(main)
213
+ # The 'main() ' function was run in the 'ctx' context,
214
+ # so changes to 'var' are contained in it:
215
+ print(ctx[var]) # 'ham'
181
216
182
- # The 'main()' function was run in the 'ctx' context,
183
- # so changes to 'var' are contained in it:
184
- # ctx[var] == 'ham'
217
+ # However, outside of 'ctx', 'var' is still set to 'spam':
218
+ print(var.get()) # 'spam'
185
219
186
- # However, outside of 'ctx', 'var' is still set to 'spam' :
187
- # var.get() == 'spam'
220
+ .. testoutput : :
221
+ :hide:
188
222
189
- The method raises a :exc: `RuntimeError ` when called on the same
190
- context object from more than one OS thread, or when called
191
- recursively.
223
+ spam
224
+ spam
225
+ spam
226
+ ham
227
+ ham
228
+ ham
229
+ spam
192
230
193
231
.. method :: copy()
194
232
0 commit comments