Coverage Report

Created: 2026-08-28 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython3/Python/context.c
Line
Count
Source
1
#include "Python.h"
2
#include "pycore_call.h"          // _PyObject_VectorcallTstate()
3
#include "pycore_context.h"
4
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
5
#include "pycore_freelist.h"      // _Py_FREELIST_FREE(), _Py_FREELIST_POP()
6
#include "pycore_gc.h"            // _PyObject_GC_MAY_BE_TRACKED()
7
#include "pycore_hamt.h"
8
#include "pycore_initconfig.h"    // _PyStatus_OK()
9
#include "pycore_object.h"
10
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_INT_RELAXED()
11
#include "pycore_pyerrors.h"
12
#include "pycore_pystate.h"       // _PyThreadState_GET()
13
14
15
16
#include "clinic/context.c.h"
17
/*[clinic input]
18
module _contextvars
19
[clinic start generated code]*/
20
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a0955718c8b8cea6]*/
21
22
23
#define ENSURE_Context(o, err_ret)                                  \
24
0
    if (!PyContext_CheckExact(o)) {                                 \
25
0
        PyErr_SetString(PyExc_TypeError,                            \
26
0
                        "an instance of Context was expected");     \
27
0
        return err_ret;                                             \
28
0
    }
29
30
#define ENSURE_ContextVar(o, err_ret)                               \
31
375k
    if (!PyContextVar_CheckExact(o)) {                              \
32
0
        PyErr_SetString(PyExc_TypeError,                            \
33
0
                       "an instance of ContextVar was expected");   \
34
0
        return err_ret;                                             \
35
0
    }
36
37
#define ENSURE_ContextToken(o, err_ret)                             \
38
0
    if (!PyContextToken_CheckExact(o)) {                            \
39
0
        PyErr_SetString(PyExc_TypeError,                            \
40
0
                        "an instance of Token was expected");       \
41
0
        return err_ret;                                             \
42
0
    }
43
44
45
/////////////////////////// Context API
46
47
48
static PyContext *
49
context_new_empty(void);
50
51
static PyContext *
52
context_new_from_vars(PyHamtObject *vars);
53
54
static inline PyContext *
55
context_get(void);
56
57
static PyContextToken *
58
token_new(PyContext *ctx, PyContextVar *var, PyObject *val);
59
60
static PyContextVar *
61
contextvar_new(PyObject *name, PyObject *def);
62
63
static int
64
contextvar_set(PyContextVar *var, PyObject *val);
65
66
static int
67
contextvar_del(PyContextVar *var);
68
69
static inline PyHamtObject *
70
context_get_vars(PyContext *ctx)
71
0
{
72
0
    PyHamtObject *vars;
73
0
    Py_BEGIN_CRITICAL_SECTION(ctx);
74
0
    vars = ctx->ctx_vars;
75
0
    assert(vars != NULL);
76
0
    Py_INCREF(vars);
77
0
    Py_END_CRITICAL_SECTION();
78
0
    return vars;
79
0
}
80
81
static inline PyHamtObject *
82
context_get_current_vars(PyContext *ctx)
83
281k
{
84
    // ctx_vars written only by the owning thread, and read by other threads
85
    // only under the context's lock, a plain (non-atomic) load is okay
86
281k
    PyHamtObject *vars = ctx->ctx_vars;
87
281k
    assert(vars != NULL);
88
281k
    return vars;
89
281k
}
90
91
// Note: steals a reference to new_vars and must only be called by the thread
92
// that has `ctx` as its current context.
93
static inline void
94
context_set_vars(PyContext *ctx, PyHamtObject *new_vars)
95
4
{
96
4
    PyHamtObject *old_vars;
97
4
    Py_BEGIN_CRITICAL_SECTION(ctx);
98
4
    old_vars = ctx->ctx_vars;
99
4
    ctx->ctx_vars = new_vars;
100
4
    Py_END_CRITICAL_SECTION();
101
4
    Py_XDECREF(old_vars);
102
4
}
103
104
105
PyObject *
106
_PyContext_NewHamtForTests(void)
107
0
{
108
0
    return (PyObject *)_PyHamt_New();
109
0
}
110
111
112
PyObject *
113
PyContext_New(void)
114
0
{
115
0
    return (PyObject *)context_new_empty();
116
0
}
117
118
119
PyObject *
120
PyContext_Copy(PyObject * octx)
121
0
{
122
0
    ENSURE_Context(octx, NULL)
123
0
    PyContext *ctx = (PyContext *)octx;
124
0
    PyHamtObject *vars = context_get_vars(ctx);
125
0
    PyObject *res = (PyObject *)context_new_from_vars(vars);
126
0
    Py_DECREF(vars);
127
0
    return res;
128
0
}
129
130
131
PyObject *
132
PyContext_CopyCurrent(void)
133
0
{
134
0
    PyContext *ctx = context_get();
135
0
    if (ctx == NULL) {
136
0
        return NULL;
137
0
    }
138
139
0
    return (PyObject *)context_new_from_vars(context_get_current_vars(ctx));
140
0
}
141
142
static const char *
143
0
context_event_name(PyContextEvent event) {
144
0
    switch (event) {
145
0
        case Py_CONTEXT_SWITCHED:
146
0
            return "Py_CONTEXT_SWITCHED";
147
0
        default:
148
0
            return "?";
149
0
    }
150
0
    Py_UNREACHABLE();
151
0
}
152
153
static void
154
notify_context_watchers(PyThreadState *ts, PyContextEvent event, PyObject *ctx)
155
0
{
156
0
    if (ctx == NULL) {
157
        // This will happen after exiting the last context in the stack, which
158
        // can occur if context_get was never called before entering a context
159
        // (e.g., called `contextvars.Context().run()` on a fresh thread, as
160
        // PyContext_Enter doesn't call context_get).
161
0
        ctx = Py_None;
162
0
    }
163
0
    assert(Py_REFCNT(ctx) > 0);
164
0
    PyInterpreterState *interp = ts->interp;
165
0
    assert(interp->_initialized);
166
0
    uint8_t bits = interp->active_context_watchers;
167
0
    int i = 0;
168
0
    while (bits) {
169
0
        assert(i < CONTEXT_MAX_WATCHERS);
170
0
        if (bits & 1) {
171
0
            PyContext_WatchCallback cb = interp->context_watchers[i];
172
0
            assert(cb != NULL);
173
0
            if (cb(event, ctx) < 0) {
174
0
                PyErr_FormatUnraisable(
175
0
                    "Exception ignored in %s watcher callback for %R",
176
0
                    context_event_name(event), ctx);
177
0
            }
178
0
        }
179
0
        i++;
180
0
        bits >>= 1;
181
0
    }
182
0
}
183
184
185
int
186
PyContext_AddWatcher(PyContext_WatchCallback callback)
187
0
{
188
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
189
0
    assert(interp->_initialized);
190
191
0
    for (int i = 0; i < CONTEXT_MAX_WATCHERS; i++) {
192
0
        if (!interp->context_watchers[i]) {
193
0
            interp->context_watchers[i] = callback;
194
0
            interp->active_context_watchers |= (1 << i);
195
0
            return i;
196
0
        }
197
0
    }
198
199
0
    PyErr_SetString(PyExc_RuntimeError, "no more context watcher IDs available");
200
0
    return -1;
201
0
}
202
203
204
int
205
PyContext_ClearWatcher(int watcher_id)
206
0
{
207
0
    PyInterpreterState *interp = _PyInterpreterState_GET();
208
0
    assert(interp->_initialized);
209
0
    if (watcher_id < 0 || watcher_id >= CONTEXT_MAX_WATCHERS) {
210
0
        PyErr_Format(PyExc_ValueError, "Invalid context watcher ID %d", watcher_id);
211
0
        return -1;
212
0
    }
213
0
    if (!interp->context_watchers[watcher_id]) {
214
0
        PyErr_Format(PyExc_ValueError, "No context watcher set for ID %d", watcher_id);
215
0
        return -1;
216
0
    }
217
0
    interp->context_watchers[watcher_id] = NULL;
218
0
    interp->active_context_watchers &= ~(1 << watcher_id);
219
0
    return 0;
220
0
}
221
222
223
static inline void
224
context_switched(PyThreadState *ts)
225
0
{
226
0
    ts->context_ver++;
227
    // ts->context is used instead of context_get() because context_get() might
228
    // throw if ts->context is NULL.
229
0
    notify_context_watchers(ts, Py_CONTEXT_SWITCHED, ts->context);
230
0
}
231
232
233
int
234
_PyContext_Enter(PyThreadState *ts, PyObject *octx)
235
0
{
236
0
    ENSURE_Context(octx, -1)
237
0
    PyContext *ctx = (PyContext *)octx;
238
#ifdef Py_GIL_DISABLED
239
    int already_entered = _Py_atomic_exchange_int(&ctx->ctx_entered, 1);
240
#else
241
0
    int already_entered = ctx->ctx_entered;
242
0
    ctx->ctx_entered = 1;
243
0
#endif
244
245
0
    if (already_entered) {
246
0
        _PyErr_Format(ts, PyExc_RuntimeError,
247
0
                      "cannot enter context: %R is already entered", ctx);
248
0
        return -1;
249
0
    }
250
251
0
    ctx->ctx_prev = (PyContext *)ts->context;  /* borrow */
252
0
    ts->context = Py_NewRef(ctx);
253
0
    context_switched(ts);
254
0
    return 0;
255
0
}
256
257
258
int
259
PyContext_Enter(PyObject *octx)
260
0
{
261
0
    PyThreadState *ts = _PyThreadState_GET();
262
0
    assert(ts != NULL);
263
0
    return _PyContext_Enter(ts, octx);
264
0
}
265
266
267
int
268
_PyContext_Exit(PyThreadState *ts, PyObject *octx)
269
0
{
270
0
    ENSURE_Context(octx, -1)
271
0
    PyContext *ctx = (PyContext *)octx;
272
0
    int already_entered = FT_ATOMIC_LOAD_INT_RELAXED(ctx->ctx_entered);
273
274
0
    if (!already_entered) {
275
0
        PyErr_Format(PyExc_RuntimeError,
276
0
                     "cannot exit context: %R has not been entered", ctx);
277
0
        return -1;
278
0
    }
279
280
0
    if (ts->context != (PyObject *)ctx) {
281
        /* Can only happen if someone misuses the C API */
282
0
        PyErr_SetString(PyExc_RuntimeError,
283
0
                        "cannot exit context: thread state references "
284
0
                        "a different context object");
285
0
        return -1;
286
0
    }
287
288
0
    Py_SETREF(ts->context, (PyObject *)ctx->ctx_prev);
289
290
0
    ctx->ctx_prev = NULL;
291
0
    FT_ATOMIC_STORE_INT(ctx->ctx_entered, 0);
292
0
    context_switched(ts);
293
0
    return 0;
294
0
}
295
296
int
297
PyContext_Exit(PyObject *octx)
298
0
{
299
0
    PyThreadState *ts = _PyThreadState_GET();
300
0
    assert(ts != NULL);
301
0
    return _PyContext_Exit(ts, octx);
302
0
}
303
304
305
PyObject *
306
PyContextVar_New(const char *name, PyObject *def)
307
21
{
308
21
    PyObject *pyname = PyUnicode_FromString(name);
309
21
    if (pyname == NULL) {
310
0
        return NULL;
311
0
    }
312
21
    PyContextVar *var = contextvar_new(pyname, def);
313
21
    Py_DECREF(pyname);
314
21
    return (PyObject *)var;
315
21
}
316
317
318
int
319
PyContextVar_Get(PyObject *ovar, PyObject *def, PyObject **val)
320
375k
{
321
375k
    ENSURE_ContextVar(ovar, -1)
322
375k
    PyContextVar *var = (PyContextVar *)ovar;
323
324
375k
    PyThreadState *ts = _PyThreadState_GET();
325
375k
    assert(ts != NULL);
326
375k
    if (ts->context == NULL) {
327
94.0k
        goto not_found;
328
94.0k
    }
329
330
281k
#ifndef Py_GIL_DISABLED
331
281k
    if (var->var_cached != NULL &&
332
0
            var->var_cached_tsid == ts->id &&
333
0
            var->var_cached_tsver == ts->context_ver)
334
0
    {
335
0
        *val = var->var_cached;
336
0
        goto found;
337
0
    }
338
281k
#endif
339
340
281k
    assert(PyContext_CheckExact(ts->context));
341
281k
    PyHamtObject *vars = context_get_current_vars((PyContext *)ts->context);
342
343
281k
    PyObject *found = NULL;
344
281k
    int res = _PyHamt_Find(vars, (PyObject*)var, &found);
345
281k
    if (res < 0) {
346
0
        goto error;
347
0
    }
348
281k
    if (res == 1) {
349
0
        assert(found != NULL);
350
0
#ifndef Py_GIL_DISABLED
351
0
        var->var_cached = found;  /* borrow */
352
0
        var->var_cached_tsid = ts->id;
353
0
        var->var_cached_tsver = ts->context_ver;
354
0
#endif
355
356
0
        *val = found;
357
0
        goto found;
358
0
    }
359
360
375k
not_found:
361
375k
    if (def == NULL) {
362
375k
        if (var->var_default != NULL) {
363
0
            *val = var->var_default;
364
0
            goto found;
365
0
        }
366
367
375k
        *val = NULL;
368
375k
        goto found;
369
375k
    }
370
0
    else {
371
0
        *val = def;
372
0
        goto found;
373
0
   }
374
375
375k
found:
376
375k
    Py_XINCREF(*val);
377
375k
    return 0;
378
379
0
error:
380
0
    *val = NULL;
381
0
    return -1;
382
375k
}
383
384
385
PyObject *
386
PyContextVar_Set(PyObject *ovar, PyObject *val)
387
4
{
388
4
    ENSURE_ContextVar(ovar, NULL)
389
4
    PyContextVar *var = (PyContextVar *)ovar;
390
391
4
    PyContext *ctx = context_get();
392
4
    if (ctx == NULL) {
393
0
        return NULL;
394
0
    }
395
396
4
    PyObject *old_val = NULL;
397
4
    int found = _PyHamt_Find(context_get_current_vars(ctx), (PyObject *)var,
398
4
                             &old_val);
399
4
    if (found < 0) {
400
0
        return NULL;
401
0
    }
402
403
4
    Py_XINCREF(old_val);
404
4
    PyContextToken *tok = token_new(ctx, var, old_val);
405
4
    Py_XDECREF(old_val);
406
4
    if (tok == NULL) {
407
0
        return NULL;
408
0
    }
409
410
4
    if (contextvar_set(var, val)) {
411
0
        Py_DECREF(tok);
412
0
        return NULL;
413
0
    }
414
415
4
    return (PyObject *)tok;
416
4
}
417
418
419
int
420
PyContextVar_Reset(PyObject *ovar, PyObject *otok)
421
0
{
422
0
    ENSURE_ContextVar(ovar, -1)
423
0
    ENSURE_ContextToken(otok, -1)
424
0
    PyContextVar *var = (PyContextVar *)ovar;
425
0
    PyContextToken *tok = (PyContextToken *)otok;
426
427
0
    if (tok->tok_used) {
428
0
        PyErr_Format(PyExc_RuntimeError,
429
0
                     "%R has already been used once", tok);
430
0
        return -1;
431
0
    }
432
433
0
    if (var != tok->tok_var) {
434
0
        PyErr_Format(PyExc_ValueError,
435
0
                     "%R was created by a different ContextVar", tok);
436
0
        return -1;
437
0
    }
438
439
0
    PyContext *ctx = context_get();
440
0
    if (ctx != tok->tok_ctx) {
441
0
        PyErr_Format(PyExc_ValueError,
442
0
                     "%R was created in a different Context", tok);
443
0
        return -1;
444
0
    }
445
446
0
    tok->tok_used = 1;
447
448
0
    if (tok->tok_oldval == NULL) {
449
0
        return contextvar_del(var);
450
0
    }
451
0
    else {
452
0
        return contextvar_set(var, tok->tok_oldval);
453
0
    }
454
0
}
455
456
457
/////////////////////////// PyContext
458
459
/*[clinic input]
460
class _contextvars.Context "PyContext *" "&PyContext_Type"
461
[clinic start generated code]*/
462
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=bdf87f8e0cb580e8]*/
463
464
465
340
#define _PyContext_CAST(op)     ((PyContext *)(op))
466
467
468
static inline PyContext *
469
_context_alloc(void)
470
4
{
471
4
    PyContext *ctx = _Py_FREELIST_POP(PyContext, contexts);
472
4
    if (ctx == NULL) {
473
4
        ctx = PyObject_GC_New(PyContext, &PyContext_Type);
474
4
        if (ctx == NULL) {
475
0
            return NULL;
476
0
        }
477
4
    }
478
479
4
    ctx->ctx_vars = NULL;
480
4
    ctx->ctx_prev = NULL;
481
4
    ctx->ctx_entered = 0;
482
4
    ctx->ctx_weakreflist = NULL;
483
484
4
    return ctx;
485
4
}
486
487
488
static PyContext *
489
context_new_empty(void)
490
4
{
491
4
    PyContext *ctx = _context_alloc();
492
4
    if (ctx == NULL) {
493
0
        return NULL;
494
0
    }
495
496
4
    ctx->ctx_vars = _PyHamt_New();
497
4
    if (ctx->ctx_vars == NULL) {
498
0
        Py_DECREF(ctx);
499
0
        return NULL;
500
0
    }
501
502
4
    _PyObject_GC_TRACK(ctx);
503
4
    return ctx;
504
4
}
505
506
507
static PyContext *
508
context_new_from_vars(PyHamtObject *vars)
509
0
{
510
0
    PyContext *ctx = _context_alloc();
511
0
    if (ctx == NULL) {
512
0
        return NULL;
513
0
    }
514
515
0
    ctx->ctx_vars = (PyHamtObject*)Py_NewRef(vars);
516
517
0
    _PyObject_GC_TRACK(ctx);
518
0
    return ctx;
519
0
}
520
521
522
static inline PyContext *
523
context_get(void)
524
8
{
525
8
    PyThreadState *ts = _PyThreadState_GET();
526
8
    assert(ts != NULL);
527
8
    PyContext *current_ctx = (PyContext *)ts->context;
528
8
    if (current_ctx == NULL) {
529
4
        current_ctx = context_new_empty();
530
4
        if (current_ctx == NULL) {
531
0
            return NULL;
532
0
        }
533
4
        ts->context = (PyObject *)current_ctx;
534
4
    }
535
8
    return current_ctx;
536
8
}
537
538
static int
539
context_check_key_type(PyObject *key)
540
0
{
541
0
    if (!PyContextVar_CheckExact(key)) {
542
        // abort();
543
0
        PyErr_Format(PyExc_TypeError,
544
0
                     "a ContextVar key was expected, got %R", key);
545
0
        return -1;
546
0
    }
547
0
    return 0;
548
0
}
549
550
static PyObject *
551
context_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
552
0
{
553
0
    if (PyTuple_Size(args) || (kwds != NULL && PyDict_Size(kwds))) {
554
0
        PyErr_SetString(
555
0
            PyExc_TypeError, "Context() does not accept any arguments");
556
0
        return NULL;
557
0
    }
558
0
    return PyContext_New();
559
0
}
560
561
static int
562
context_tp_clear(PyObject *op)
563
0
{
564
0
    PyContext *self = _PyContext_CAST(op);
565
0
    Py_CLEAR(self->ctx_prev);
566
0
    Py_CLEAR(self->ctx_vars);
567
0
    return 0;
568
0
}
569
570
static int
571
context_tp_traverse(PyObject *op, visitproc visit, void *arg)
572
340
{
573
340
    PyContext *self = _PyContext_CAST(op);
574
340
    Py_VISIT(self->ctx_prev);
575
340
    Py_VISIT(self->ctx_vars);
576
340
    return 0;
577
340
}
578
579
static void
580
context_tp_dealloc(PyObject *self)
581
0
{
582
0
    _PyObject_GC_UNTRACK(self);
583
0
    PyContext *ctx = _PyContext_CAST(self);
584
0
    if (ctx->ctx_weakreflist != NULL) {
585
0
        PyObject_ClearWeakRefs(self);
586
0
    }
587
0
    (void)context_tp_clear(self);
588
589
0
    _Py_FREELIST_FREE(contexts, self, Py_TYPE(self)->tp_free);
590
0
}
591
592
static PyObject *
593
context_tp_iter(PyObject *op)
594
0
{
595
0
    PyContext *self = _PyContext_CAST(op);
596
0
    PyHamtObject *vars = context_get_vars(self);
597
0
    PyObject *res = _PyHamt_NewIterKeys(vars);
598
0
    Py_DECREF(vars);
599
0
    return res;
600
0
}
601
602
static PyObject *
603
context_tp_richcompare(PyObject *v, PyObject *w, int op)
604
0
{
605
0
    if (!PyContext_CheckExact(v) || !PyContext_CheckExact(w) ||
606
0
            (op != Py_EQ && op != Py_NE))
607
0
    {
608
0
        Py_RETURN_NOTIMPLEMENTED;
609
0
    }
610
611
0
    PyHamtObject *v_vars = context_get_vars((PyContext *)v);
612
0
    PyHamtObject *w_vars = context_get_vars((PyContext *)w);
613
0
    int res = _PyHamt_Eq(v_vars, w_vars);
614
0
    Py_DECREF(v_vars);
615
0
    Py_DECREF(w_vars);
616
0
    if (res < 0) {
617
0
        return NULL;
618
0
    }
619
620
0
    if (op == Py_NE) {
621
0
        res = !res;
622
0
    }
623
624
0
    if (res) {
625
0
        Py_RETURN_TRUE;
626
0
    }
627
0
    else {
628
0
        Py_RETURN_FALSE;
629
0
    }
630
0
}
631
632
static Py_ssize_t
633
context_tp_len(PyObject *op)
634
0
{
635
0
    PyContext *self = _PyContext_CAST(op);
636
0
    PyHamtObject *vars = context_get_vars(self);
637
0
    Py_ssize_t res = _PyHamt_Len(vars);
638
0
    Py_DECREF(vars);
639
0
    return res;
640
0
}
641
642
static PyObject *
643
context_tp_subscript(PyObject *op, PyObject *key)
644
0
{
645
0
    if (context_check_key_type(key)) {
646
0
        return NULL;
647
0
    }
648
0
    PyObject *val = NULL;
649
0
    PyContext *self = _PyContext_CAST(op);
650
0
    PyHamtObject *vars = context_get_vars(self);
651
0
    int found = _PyHamt_Find(vars, key, &val);
652
0
    Py_XINCREF(val);
653
0
    Py_DECREF(vars);
654
0
    if (found < 0) {
655
0
        return NULL;
656
0
    }
657
0
    if (found == 0) {
658
0
        PyErr_SetObject(PyExc_KeyError, key);
659
0
        return NULL;
660
0
    }
661
0
    return val;
662
0
}
663
664
static int
665
context_tp_contains(PyObject *op, PyObject *key)
666
0
{
667
0
    if (context_check_key_type(key)) {
668
0
        return -1;
669
0
    }
670
0
    PyObject *val = NULL;
671
0
    PyContext *self = _PyContext_CAST(op);
672
0
    PyHamtObject *vars = context_get_vars(self);
673
0
    int res = _PyHamt_Find(vars, key, &val);
674
0
    Py_DECREF(vars);
675
0
    return res;
676
0
}
677
678
679
/*[clinic input]
680
@permit_long_summary
681
_contextvars.Context.get
682
    key: object
683
    default: object = None
684
    /
685
686
Return the value for `key` if `key` has the value in the context object.
687
688
If `key` does not exist, return `default`.  If `default` is not
689
given, return None.
690
[clinic start generated code]*/
691
692
static PyObject *
693
_contextvars_Context_get_impl(PyContext *self, PyObject *key,
694
                              PyObject *default_value)
695
/*[clinic end generated code: output=0c54aa7664268189 input=d669a0d56fabb0a5]*/
696
0
{
697
0
    if (context_check_key_type(key)) {
698
0
        return NULL;
699
0
    }
700
701
0
    PyObject *val = NULL;
702
0
    PyHamtObject *vars = context_get_vars(self);
703
0
    int found = _PyHamt_Find(vars, key, &val);
704
0
    Py_XINCREF(val);
705
0
    Py_DECREF(vars);
706
0
    if (found < 0) {
707
0
        return NULL;
708
0
    }
709
0
    if (found == 0) {
710
0
        return Py_NewRef(default_value);
711
0
    }
712
0
    return val;
713
0
}
714
715
716
/*[clinic input]
717
_contextvars.Context.items
718
719
Return all variables and their values in the context object.
720
721
The result is returned as a list of 2-tuples (variable, value).
722
[clinic start generated code]*/
723
724
static PyObject *
725
_contextvars_Context_items_impl(PyContext *self)
726
/*[clinic end generated code: output=fa1655c8a08502af input=00db64ae379f9f42]*/
727
0
{
728
0
    PyHamtObject *vars = context_get_vars(self);
729
0
    PyObject *res = _PyHamt_NewIterItems(vars);
730
0
    Py_DECREF(vars);
731
0
    return res;
732
0
}
733
734
735
/*[clinic input]
736
_contextvars.Context.keys
737
738
Return a list of all variables in the context object.
739
[clinic start generated code]*/
740
741
static PyObject *
742
_contextvars_Context_keys_impl(PyContext *self)
743
/*[clinic end generated code: output=177227c6b63ec0e2 input=114b53aebca3449c]*/
744
0
{
745
0
    PyHamtObject *vars = context_get_vars(self);
746
0
    PyObject *res = _PyHamt_NewIterKeys(vars);
747
0
    Py_DECREF(vars);
748
0
    return res;
749
0
}
750
751
752
/*[clinic input]
753
_contextvars.Context.values
754
755
Return a list of all variables' values in the context object.
756
[clinic start generated code]*/
757
758
static PyObject *
759
_contextvars_Context_values_impl(PyContext *self)
760
/*[clinic end generated code: output=d286dabfc8db6dde input=ce8075d04a6ea526]*/
761
0
{
762
0
    PyHamtObject *vars = context_get_vars(self);
763
0
    PyObject *res = _PyHamt_NewIterValues(vars);
764
0
    Py_DECREF(vars);
765
0
    return res;
766
0
}
767
768
769
/*[clinic input]
770
_contextvars.Context.copy
771
772
Return a shallow copy of the context object.
773
[clinic start generated code]*/
774
775
static PyObject *
776
_contextvars_Context_copy_impl(PyContext *self)
777
/*[clinic end generated code: output=30ba8896c4707a15 input=ebafdbdd9c72d592]*/
778
0
{
779
0
    PyHamtObject *vars = context_get_vars(self);
780
0
    PyObject *res = (PyObject *)context_new_from_vars(vars);
781
0
    Py_DECREF(vars);
782
0
    return res;
783
0
}
784
785
786
static PyObject *
787
context_run(PyObject *self, PyObject *const *args,
788
            Py_ssize_t nargs, PyObject *kwnames)
789
0
{
790
0
    PyThreadState *ts = _PyThreadState_GET();
791
792
0
    if (nargs < 1) {
793
0
        _PyErr_SetString(ts, PyExc_TypeError,
794
0
                         "run() missing 1 required positional argument");
795
0
        return NULL;
796
0
    }
797
798
0
    if (_PyContext_Enter(ts, self)) {
799
0
        return NULL;
800
0
    }
801
802
0
    PyObject *call_result = _PyObject_VectorcallTstate(
803
0
        ts, args[0], args + 1, nargs - 1, kwnames);
804
805
0
    if (_PyContext_Exit(ts, self)) {
806
0
        Py_XDECREF(call_result);
807
0
        return NULL;
808
0
    }
809
810
0
    return call_result;
811
0
}
812
813
814
static PyMethodDef PyContext_methods[] = {
815
    _CONTEXTVARS_CONTEXT_GET_METHODDEF
816
    _CONTEXTVARS_CONTEXT_ITEMS_METHODDEF
817
    _CONTEXTVARS_CONTEXT_KEYS_METHODDEF
818
    _CONTEXTVARS_CONTEXT_VALUES_METHODDEF
819
    _CONTEXTVARS_CONTEXT_COPY_METHODDEF
820
    {"run", _PyCFunction_CAST(context_run), METH_FASTCALL | METH_KEYWORDS, NULL},
821
    {NULL, NULL}
822
};
823
824
static PySequenceMethods PyContext_as_sequence = {
825
    .sq_contains = context_tp_contains
826
};
827
828
static PyMappingMethods PyContext_as_mapping = {
829
    .mp_length = context_tp_len,
830
    .mp_subscript = context_tp_subscript
831
};
832
833
PyTypeObject PyContext_Type = {
834
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
835
    "_contextvars.Context",
836
    sizeof(PyContext),
837
    .tp_methods = PyContext_methods,
838
    .tp_as_mapping = &PyContext_as_mapping,
839
    .tp_as_sequence = &PyContext_as_sequence,
840
    .tp_iter = context_tp_iter,
841
    .tp_dealloc = context_tp_dealloc,
842
    .tp_getattro = PyObject_GenericGetAttr,
843
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
844
    .tp_richcompare = context_tp_richcompare,
845
    .tp_traverse = context_tp_traverse,
846
    .tp_clear = context_tp_clear,
847
    .tp_new = context_tp_new,
848
    .tp_weaklistoffset = offsetof(PyContext, ctx_weakreflist),
849
    .tp_hash = PyObject_HashNotImplemented,
850
};
851
852
853
/////////////////////////// ContextVar
854
855
856
static int
857
contextvar_set(PyContextVar *var, PyObject *val)
858
4
{
859
4
#ifndef Py_GIL_DISABLED
860
4
    var->var_cached = NULL;
861
4
    PyThreadState *ts = _PyThreadState_GET();
862
4
#endif
863
864
4
    PyContext *ctx = context_get();
865
4
    if (ctx == NULL) {
866
0
        return -1;
867
0
    }
868
869
4
    PyHamtObject *new_vars = _PyHamt_Assoc(
870
4
        context_get_current_vars(ctx), (PyObject *)var, val);
871
4
    if (new_vars == NULL) {
872
0
        return -1;
873
0
    }
874
875
4
    context_set_vars(ctx, new_vars);
876
877
4
#ifndef Py_GIL_DISABLED
878
4
    var->var_cached = val;  /* borrow */
879
4
    var->var_cached_tsid = ts->id;
880
4
    var->var_cached_tsver = ts->context_ver;
881
4
#endif
882
4
    return 0;
883
4
}
884
885
static int
886
contextvar_del(PyContextVar *var)
887
0
{
888
0
#ifndef Py_GIL_DISABLED
889
0
    var->var_cached = NULL;
890
0
#endif
891
892
0
    PyContext *ctx = context_get();
893
0
    if (ctx == NULL) {
894
0
        return -1;
895
0
    }
896
897
0
    PyHamtObject *vars = context_get_current_vars(ctx);
898
0
    PyHamtObject *new_vars = _PyHamt_Without(vars, (PyObject *)var);
899
0
    if (new_vars == NULL) {
900
0
        return -1;
901
0
    }
902
903
0
    if (vars == new_vars) {
904
0
        Py_DECREF(new_vars);
905
0
        PyErr_SetObject(PyExc_LookupError, (PyObject *)var);
906
0
        return -1;
907
0
    }
908
909
0
    context_set_vars(ctx, new_vars);
910
0
    return 0;
911
0
}
912
913
static Py_hash_t
914
contextvar_generate_hash(void *addr, PyObject *name)
915
28
{
916
    /* Take hash of `name` and XOR it with the object's addr.
917
918
       The structure of the tree is encoded in objects' hashes, which
919
       means that sufficiently similar hashes would result in tall trees
920
       with many Collision nodes.  Which would, in turn, result in slower
921
       get and set operations.
922
923
       The XORing helps to ensure that:
924
925
       (1) sequentially allocated ContextVar objects have
926
           different hashes;
927
928
       (2) context variables with equal names have
929
           different hashes.
930
    */
931
932
28
    Py_hash_t name_hash = PyObject_Hash(name);
933
28
    if (name_hash == -1) {
934
0
        return -1;
935
0
    }
936
937
28
    Py_hash_t res = Py_HashPointer(addr) ^ name_hash;
938
28
    return res == -1 ? -2 : res;
939
28
}
940
941
static PyContextVar *
942
contextvar_new(PyObject *name, PyObject *def)
943
28
{
944
28
    if (!PyUnicode_Check(name)) {
945
0
        PyErr_SetString(PyExc_TypeError,
946
0
                        "context variable name must be a str");
947
0
        return NULL;
948
0
    }
949
950
28
    PyContextVar *var = PyObject_GC_New(PyContextVar, &PyContextVar_Type);
951
28
    if (var == NULL) {
952
0
        return NULL;
953
0
    }
954
955
28
    var->var_name = Py_NewRef(name);
956
28
    var->var_default = Py_XNewRef(def);
957
958
28
#ifndef Py_GIL_DISABLED
959
28
    var->var_cached = NULL;
960
28
    var->var_cached_tsid = 0;
961
28
    var->var_cached_tsver = 0;
962
28
#endif
963
964
28
    var->var_hash = contextvar_generate_hash(var, name);
965
28
    if (var->var_hash == -1) {
966
0
        Py_DECREF(var);
967
0
        return NULL;
968
0
    }
969
970
28
    if (_PyObject_GC_MAY_BE_TRACKED(name) ||
971
28
            (def != NULL && _PyObject_GC_MAY_BE_TRACKED(def)))
972
0
    {
973
0
        PyObject_GC_Track(var);
974
0
    }
975
28
    return var;
976
28
}
977
978
979
/*[clinic input]
980
class _contextvars.ContextVar "PyContextVar *" "&PyContextVar_Type"
981
[clinic start generated code]*/
982
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=445da935fa8883c3]*/
983
984
985
281k
#define _PyContextVar_CAST(op)  ((PyContextVar *)(op))
986
987
988
static PyObject *
989
contextvar_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
990
7
{
991
7
    static char *kwlist[] = {"", "default", NULL};
992
7
    PyObject *name;
993
7
    PyObject *def = NULL;
994
995
7
    if (!PyArg_ParseTupleAndKeywords(
996
7
            args, kwds, "O|$O:ContextVar", kwlist, &name, &def))
997
0
    {
998
0
        return NULL;
999
0
    }
1000
1001
7
    return (PyObject *)contextvar_new(name, def);
1002
7
}
1003
1004
static int
1005
contextvar_tp_clear(PyObject *op)
1006
0
{
1007
0
    PyContextVar *self = _PyContextVar_CAST(op);
1008
0
    Py_CLEAR(self->var_name);
1009
0
    Py_CLEAR(self->var_default);
1010
0
#ifndef Py_GIL_DISABLED
1011
0
    self->var_cached = NULL;
1012
0
    self->var_cached_tsid = 0;
1013
0
    self->var_cached_tsver = 0;
1014
0
#endif
1015
0
    return 0;
1016
0
}
1017
1018
static int
1019
contextvar_tp_traverse(PyObject *op, visitproc visit, void *arg)
1020
0
{
1021
0
    PyContextVar *self = _PyContextVar_CAST(op);
1022
0
    Py_VISIT(self->var_name);
1023
0
    Py_VISIT(self->var_default);
1024
0
    return 0;
1025
0
}
1026
1027
static void
1028
contextvar_tp_dealloc(PyObject *self)
1029
0
{
1030
0
    PyObject_GC_UnTrack(self);
1031
0
    (void)contextvar_tp_clear(self);
1032
0
    Py_TYPE(self)->tp_free(self);
1033
0
}
1034
1035
static Py_hash_t
1036
contextvar_tp_hash(PyObject *op)
1037
281k
{
1038
281k
    PyContextVar *self = _PyContextVar_CAST(op);
1039
281k
    return self->var_hash;
1040
281k
}
1041
1042
static PyObject *
1043
contextvar_tp_repr(PyObject *op)
1044
0
{
1045
0
    PyContextVar *self = _PyContextVar_CAST(op);
1046
    // Estimation based on the shortest name and default value,
1047
    // but maximize the pointer size.
1048
    // "<ContextVar name='a' at 0x1234567812345678>"
1049
    // "<ContextVar name='a' default=1 at 0x1234567812345678>"
1050
0
    Py_ssize_t estimate = self->var_default ? 53 : 43;
1051
0
    PyUnicodeWriter *writer = PyUnicodeWriter_Create(estimate);
1052
0
    if (writer == NULL) {
1053
0
        return NULL;
1054
0
    }
1055
1056
0
    if (PyUnicodeWriter_WriteASCII(writer, "<ContextVar name=", 17) < 0) {
1057
0
        goto error;
1058
0
    }
1059
0
    if (PyUnicodeWriter_WriteRepr(writer, self->var_name) < 0) {
1060
0
        goto error;
1061
0
    }
1062
1063
0
    if (self->var_default != NULL) {
1064
0
        if (PyUnicodeWriter_WriteASCII(writer, " default=", 9) < 0) {
1065
0
            goto error;
1066
0
        }
1067
0
        if (PyUnicodeWriter_WriteRepr(writer, self->var_default) < 0) {
1068
0
            goto error;
1069
0
        }
1070
0
    }
1071
1072
0
    if (PyUnicodeWriter_Format(writer, " at %p>", self) < 0) {
1073
0
        goto error;
1074
0
    }
1075
0
    return PyUnicodeWriter_Finish(writer);
1076
1077
0
error:
1078
0
    PyUnicodeWriter_Discard(writer);
1079
0
    return NULL;
1080
0
}
1081
1082
1083
/*[clinic input]
1084
_contextvars.ContextVar.get
1085
    default: object = NULL
1086
    /
1087
1088
Return a value for the context variable for the current context.
1089
1090
If there is no value for the variable in the current context, the
1091
method will:
1092
 * return the value of the default argument of the method, if
1093
   provided; or
1094
 * return the default value for the context variable, if it was
1095
   created with one; or
1096
 * raise a LookupError.
1097
[clinic start generated code]*/
1098
1099
static PyObject *
1100
_contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value)
1101
/*[clinic end generated code: output=0746bd0aa2ced7bf input=83814c6aef4a9fe3]*/
1102
4
{
1103
4
    PyObject *val;
1104
4
    if (PyContextVar_Get((PyObject *)self, default_value, &val) < 0) {
1105
0
        return NULL;
1106
0
    }
1107
1108
4
    if (val == NULL) {
1109
4
        PyErr_SetObject(PyExc_LookupError, (PyObject *)self);
1110
4
        return NULL;
1111
4
    }
1112
1113
0
    return val;
1114
4
}
1115
1116
/*[clinic input]
1117
@permit_long_summary
1118
_contextvars.ContextVar.set
1119
    value: object
1120
    /
1121
1122
Call to set a new value for the context variable in the current context.
1123
1124
The required value argument is the new value for the context
1125
variable.
1126
1127
Returns a Token object that can be used to restore the variable to
1128
its previous value via the `ContextVar.reset()` method.
1129
[clinic start generated code]*/
1130
1131
static PyObject *
1132
_contextvars_ContextVar_set_impl(PyContextVar *self, PyObject *value)
1133
/*[clinic end generated code: output=1b562d35cc79c806 input=04ef8dcd810f5be6]*/
1134
4
{
1135
4
    return PyContextVar_Set((PyObject *)self, value);
1136
4
}
1137
1138
/*[clinic input]
1139
_contextvars.ContextVar.reset
1140
    token: object
1141
    /
1142
1143
Reset the context variable.
1144
1145
The variable is reset to the value it had before the
1146
`ContextVar.set()` that created the token was used.
1147
[clinic start generated code]*/
1148
1149
static PyObject *
1150
_contextvars_ContextVar_reset_impl(PyContextVar *self, PyObject *token)
1151
/*[clinic end generated code: output=3205d2bdff568521 input=dd33cfcb18c00e37]*/
1152
0
{
1153
0
    if (!PyContextToken_CheckExact(token)) {
1154
0
        PyErr_Format(PyExc_TypeError,
1155
0
                     "expected an instance of Token, got %R", token);
1156
0
        return NULL;
1157
0
    }
1158
1159
0
    if (PyContextVar_Reset((PyObject *)self, token)) {
1160
0
        return NULL;
1161
0
    }
1162
1163
0
    Py_RETURN_NONE;
1164
0
}
1165
1166
1167
static PyMemberDef PyContextVar_members[] = {
1168
    {"name", _Py_T_OBJECT, offsetof(PyContextVar, var_name), Py_READONLY},
1169
    {NULL}
1170
};
1171
1172
static PyMethodDef PyContextVar_methods[] = {
1173
    _CONTEXTVARS_CONTEXTVAR_GET_METHODDEF
1174
    _CONTEXTVARS_CONTEXTVAR_SET_METHODDEF
1175
    _CONTEXTVARS_CONTEXTVAR_RESET_METHODDEF
1176
    {"__class_getitem__", Py_GenericAlias,
1177
    METH_O|METH_CLASS,
1178
    PyDoc_STR("ContextVars are generic over the type of their contained values")},
1179
    {NULL, NULL}
1180
};
1181
1182
PyTypeObject PyContextVar_Type = {
1183
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
1184
    "_contextvars.ContextVar",
1185
    sizeof(PyContextVar),
1186
    .tp_methods = PyContextVar_methods,
1187
    .tp_members = PyContextVar_members,
1188
    .tp_dealloc = contextvar_tp_dealloc,
1189
    .tp_getattro = PyObject_GenericGetAttr,
1190
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1191
    .tp_traverse = contextvar_tp_traverse,
1192
    .tp_clear = contextvar_tp_clear,
1193
    .tp_new = contextvar_tp_new,
1194
    .tp_free = PyObject_GC_Del,
1195
    .tp_hash = contextvar_tp_hash,
1196
    .tp_repr = contextvar_tp_repr,
1197
};
1198
1199
1200
/////////////////////////// Token
1201
1202
static PyObject * get_token_missing(void);
1203
1204
1205
/*[clinic input]
1206
class _contextvars.Token "PyContextToken *" "&PyContextToken_Type"
1207
[clinic start generated code]*/
1208
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=338a5e2db13d3f5b]*/
1209
1210
1211
4
#define _PyContextToken_CAST(op)    ((PyContextToken *)(op))
1212
1213
1214
static PyObject *
1215
token_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1216
0
{
1217
0
    PyErr_SetString(PyExc_RuntimeError,
1218
0
                    "Tokens can only be created by ContextVars");
1219
0
    return NULL;
1220
0
}
1221
1222
static int
1223
token_tp_clear(PyObject *op)
1224
4
{
1225
4
    PyContextToken *self = _PyContextToken_CAST(op);
1226
4
    Py_CLEAR(self->tok_ctx);
1227
4
    Py_CLEAR(self->tok_var);
1228
4
    Py_CLEAR(self->tok_oldval);
1229
4
    return 0;
1230
4
}
1231
1232
static int
1233
token_tp_traverse(PyObject *op, visitproc visit, void *arg)
1234
0
{
1235
0
    PyContextToken *self = _PyContextToken_CAST(op);
1236
0
    Py_VISIT(self->tok_ctx);
1237
0
    Py_VISIT(self->tok_var);
1238
0
    Py_VISIT(self->tok_oldval);
1239
0
    return 0;
1240
0
}
1241
1242
static void
1243
token_tp_dealloc(PyObject *self)
1244
4
{
1245
4
    PyObject_GC_UnTrack(self);
1246
4
    (void)token_tp_clear(self);
1247
4
    Py_TYPE(self)->tp_free(self);
1248
4
}
1249
1250
static PyObject *
1251
token_tp_repr(PyObject *op)
1252
0
{
1253
0
    PyContextToken *self = _PyContextToken_CAST(op);
1254
0
    PyUnicodeWriter *writer = PyUnicodeWriter_Create(0);
1255
0
    if (writer == NULL) {
1256
0
        return NULL;
1257
0
    }
1258
0
    if (PyUnicodeWriter_WriteASCII(writer, "<Token", 6) < 0) {
1259
0
        goto error;
1260
0
    }
1261
0
    if (self->tok_used) {
1262
0
        if (PyUnicodeWriter_WriteASCII(writer, " used", 5) < 0) {
1263
0
            goto error;
1264
0
        }
1265
0
    }
1266
0
    if (PyUnicodeWriter_WriteASCII(writer, " var=", 5) < 0) {
1267
0
        goto error;
1268
0
    }
1269
0
    if (PyUnicodeWriter_WriteRepr(writer, (PyObject *)self->tok_var) < 0) {
1270
0
        goto error;
1271
0
    }
1272
0
    if (PyUnicodeWriter_Format(writer, " at %p>", self) < 0) {
1273
0
        goto error;
1274
0
    }
1275
0
    return PyUnicodeWriter_Finish(writer);
1276
1277
0
error:
1278
0
    PyUnicodeWriter_Discard(writer);
1279
0
    return NULL;
1280
0
}
1281
1282
static PyObject *
1283
token_get_var(PyObject *op, void *Py_UNUSED(ignored))
1284
0
{
1285
0
    PyContextToken *self = _PyContextToken_CAST(op);
1286
0
    return Py_NewRef(self->tok_var);;
1287
0
}
1288
1289
static PyObject *
1290
token_get_old_value(PyObject *op, void *Py_UNUSED(ignored))
1291
0
{
1292
0
    PyContextToken *self = _PyContextToken_CAST(op);
1293
0
    if (self->tok_oldval == NULL) {
1294
0
        return get_token_missing();
1295
0
    }
1296
1297
0
    return Py_NewRef(self->tok_oldval);
1298
0
}
1299
1300
static PyGetSetDef PyContextTokenType_getsetlist[] = {
1301
    {"var", token_get_var, NULL, NULL},
1302
    {"old_value", token_get_old_value, NULL, NULL},
1303
    {NULL}
1304
};
1305
1306
/*[clinic input]
1307
_contextvars.Token.__enter__ as token_enter
1308
1309
Enter into Token context manager.
1310
[clinic start generated code]*/
1311
1312
static PyObject *
1313
token_enter_impl(PyContextToken *self)
1314
/*[clinic end generated code: output=9af4d2054e93fb75 input=41a3d6c4195fd47a]*/
1315
0
{
1316
0
    return Py_NewRef(self);
1317
0
}
1318
1319
/*[clinic input]
1320
_contextvars.Token.__exit__ as token_exit
1321
1322
    type: object
1323
    val: object
1324
    tb: object
1325
    /
1326
1327
Exit from Token context manager, restore the linked ContextVar.
1328
[clinic start generated code]*/
1329
1330
static PyObject *
1331
token_exit_impl(PyContextToken *self, PyObject *type, PyObject *val,
1332
                PyObject *tb)
1333
/*[clinic end generated code: output=3e6a1c95d3da703a input=7f117445f0ccd92e]*/
1334
0
{
1335
0
    int ret = PyContextVar_Reset((PyObject *)self->tok_var, (PyObject *)self);
1336
0
    if (ret < 0) {
1337
0
        return NULL;
1338
0
    }
1339
0
    Py_RETURN_NONE;
1340
0
}
1341
1342
static PyMethodDef PyContextTokenType_methods[] = {
1343
    {"__class_getitem__",    Py_GenericAlias,
1344
    METH_O|METH_CLASS,
1345
    PyDoc_STR("Tokens are generic over the same type as the ContextVar which created them.")},
1346
    TOKEN_ENTER_METHODDEF
1347
    TOKEN_EXIT_METHODDEF
1348
    {NULL}
1349
};
1350
1351
PyTypeObject PyContextToken_Type = {
1352
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
1353
    "_contextvars.Token",
1354
    sizeof(PyContextToken),
1355
    .tp_methods = PyContextTokenType_methods,
1356
    .tp_getset = PyContextTokenType_getsetlist,
1357
    .tp_dealloc = token_tp_dealloc,
1358
    .tp_getattro = PyObject_GenericGetAttr,
1359
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1360
    .tp_traverse = token_tp_traverse,
1361
    .tp_clear = token_tp_clear,
1362
    .tp_new = token_tp_new,
1363
    .tp_free = PyObject_GC_Del,
1364
    .tp_hash = PyObject_HashNotImplemented,
1365
    .tp_repr = token_tp_repr,
1366
};
1367
1368
static PyContextToken *
1369
token_new(PyContext *ctx, PyContextVar *var, PyObject *val)
1370
4
{
1371
4
    PyContextToken *tok = PyObject_GC_New(PyContextToken, &PyContextToken_Type);
1372
4
    if (tok == NULL) {
1373
0
        return NULL;
1374
0
    }
1375
1376
4
    tok->tok_ctx = (PyContext*)Py_NewRef(ctx);
1377
1378
4
    tok->tok_var = (PyContextVar*)Py_NewRef(var);
1379
1380
4
    tok->tok_oldval = Py_XNewRef(val);
1381
1382
4
    tok->tok_used = 0;
1383
1384
4
    PyObject_GC_Track(tok);
1385
4
    return tok;
1386
4
}
1387
1388
1389
/////////////////////////// Token.MISSING
1390
1391
1392
static PyObject *
1393
context_token_missing_tp_repr(PyObject *self)
1394
0
{
1395
0
    return PyUnicode_FromString("<Token.MISSING>");
1396
0
}
1397
1398
static void
1399
context_token_missing_tp_dealloc(PyObject *Py_UNUSED(self))
1400
0
{
1401
#ifdef Py_DEBUG
1402
    /* The singleton is statically allocated. */
1403
    _Py_FatalRefcountError("deallocating the token missing singleton");
1404
#else
1405
0
    return;
1406
0
#endif
1407
0
}
1408
1409
1410
PyTypeObject _PyContextTokenMissing_Type = {
1411
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
1412
    "Token.MISSING",
1413
    sizeof(_PyContextTokenMissing),
1414
    .tp_dealloc = context_token_missing_tp_dealloc,
1415
    .tp_getattro = PyObject_GenericGetAttr,
1416
    .tp_flags = Py_TPFLAGS_DEFAULT,
1417
    .tp_repr = context_token_missing_tp_repr,
1418
};
1419
1420
1421
static PyObject *
1422
get_token_missing(void)
1423
21
{
1424
21
    return (PyObject *)&_Py_SINGLETON(context_token_missing);
1425
21
}
1426
1427
1428
///////////////////////////
1429
1430
1431
PyStatus
1432
_PyContext_Init(PyInterpreterState *interp)
1433
21
{
1434
21
    PyObject *missing = get_token_missing();
1435
21
    assert(PyUnstable_IsImmortal(missing));
1436
21
    if (PyDict_SetItemString(
1437
21
        _PyType_GetDict(&PyContextToken_Type), "MISSING", missing))
1438
0
    {
1439
0
        Py_DECREF(missing);
1440
0
        return _PyStatus_ERR("can't init context types");
1441
0
    }
1442
21
    Py_DECREF(missing);
1443
1444
21
    return _PyStatus_OK();
1445
21
}