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