Skip to content

Commit 2ec872b

Browse files
authored
bpo-34762: Fix contextvars C API to use PyObject* pointer types. (GH-9473)
1 parent b46ad54 commit 2ec872b

File tree

6 files changed

+106
-45
lines changed

6 files changed

+106
-45
lines changed

Doc/c-api/contextvars.rst

+29-10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,25 @@
55
Context Variables Objects
66
-------------------------
77

8+
.. _contextvarsobjects_pointertype_change:
9+
.. versionchanged:: 3.7.1
10+
11+
.. note::
12+
13+
In Python 3.7.1 the signatures of all context variables
14+
C APIs were **changed** to use :c:type:`PyObject` pointers instead
15+
of :c:type:`PyContext`, :c:type:`PyContextVar`, and
16+
:c:type:`PyContextToken`, e.g.::
17+
18+
// in 3.7.0:
19+
PyContext *PyContext_New(void);
20+
21+
// in 3.7.1+:
22+
PyObject *PyContext_New(void);
23+
24+
See :issue:`34762` for more details.
25+
26+
827
.. versionadded:: 3.7
928

1029
This section details the public C API for the :mod:`contextvars` module.
@@ -56,27 +75,27 @@ Type-check macros:
5675
5776
Context object management functions:
5877
59-
.. c:function:: PyContext *PyContext_New(void)
78+
.. c:function:: PyObject *PyContext_New(void)
6079
6180
Create a new empty context object. Returns ``NULL`` if an error
6281
has occurred.
6382
64-
.. c:function:: PyContext *PyContext_Copy(PyContext *ctx)
83+
.. c:function:: PyObject *PyContext_Copy(PyObject *ctx)
6584
6685
Create a shallow copy of the passed *ctx* context object.
6786
Returns ``NULL`` if an error has occurred.
6887
69-
.. c:function:: PyContext *PyContext_CopyCurrent(void)
88+
.. c:function:: PyObject *PyContext_CopyCurrent(void)
7089
7190
Create a shallow copy of the current thread context.
7291
Returns ``NULL`` if an error has occurred.
7392
74-
.. c:function:: int PyContext_Enter(PyContext *ctx)
93+
.. c:function:: int PyContext_Enter(PyObject *ctx)
7594
7695
Set *ctx* as the current context for the current thread.
7796
Returns ``0`` on success, and ``-1`` on error.
7897
79-
.. c:function:: int PyContext_Exit(PyContext *ctx)
98+
.. c:function:: int PyContext_Exit(PyObject *ctx)
8099
81100
Deactivate the *ctx* context and restore the previous context as the
82101
current context for the current thread. Returns ``0`` on success,
@@ -90,14 +109,14 @@ Context object management functions:
90109
91110
Context variable functions:
92111
93-
.. c:function:: PyContextVar *PyContextVar_New(const char *name, PyObject *def)
112+
.. c:function:: PyObject *PyContextVar_New(const char *name, PyObject *def)
94113
95114
Create a new ``ContextVar`` object. The *name* parameter is used
96115
for introspection and debug purposes. The *def* parameter may optionally
97116
specify the default value for the context variable. If an error has
98117
occurred, this function returns ``NULL``.
99118
100-
.. c:function:: int PyContextVar_Get(PyContextVar *var, PyObject *default_value, PyObject **value)
119+
.. c:function:: int PyContextVar_Get(PyObject *var, PyObject *default_value, PyObject **value)
101120
102121
Get the value of a context variable. Returns ``-1`` if an error has
103122
occurred during lookup, and ``0`` if no error occurred, whether or not
@@ -112,13 +131,13 @@ Context variable functions:
112131
113132
If the value was found, the function will create a new reference to it.
114133
115-
.. c:function:: PyContextToken *PyContextVar_Set(PyContextVar *var, PyObject *value)
134+
.. c:function:: PyObject *PyContextVar_Set(PyObject *var, PyObject *value)
116135
117136
Set the value of *var* to *value* in the current context. Returns a
118-
pointer to a :c:type:`PyContextToken` object, or ``NULL`` if an error
137+
pointer to a :c:type:`PyObject` object, or ``NULL`` if an error
119138
has occurred.
120139
121-
.. c:function:: int PyContextVar_Reset(PyContextVar *var, PyContextToken *token)
140+
.. c:function:: int PyContextVar_Reset(PyObject *var, PyObject *token)
122141
123142
Reset the state of the *var* context variable to that it was in before
124143
:c:func:`PyContextVar_Set` that returned the *token* was called.

Doc/whatsnew/3.7.rst

+4
Original file line numberDiff line numberDiff line change
@@ -2494,3 +2494,7 @@ versions, it respected an ill-defined subset of those environment variables,
24942494
while in Python 3.7.0 it didn't read any of them due to :issue:`34247`). If
24952495
this behavior is unwanted, set :c:data:`Py_IgnoreEnvironmentFlag` to 1 before
24962496
calling :c:func:`Py_Initialize`.
2497+
2498+
In 3.7.1 the C API for Context Variables
2499+
:ref:`was updated <contextvarsobjects_pointertype_change>` to use
2500+
:c:type:`PyObject` pointers. See also :issue:`34762`.

Include/context.h

+9-11
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,19 @@ typedef struct _pycontexttokenobject PyContextToken;
2222
#define PyContextToken_CheckExact(o) (Py_TYPE(o) == &PyContextToken_Type)
2323

2424

25-
PyAPI_FUNC(PyContext *) PyContext_New(void);
26-
PyAPI_FUNC(PyContext *) PyContext_Copy(PyContext *);
27-
PyAPI_FUNC(PyContext *) PyContext_CopyCurrent(void);
25+
PyAPI_FUNC(PyObject *) PyContext_New(void);
26+
PyAPI_FUNC(PyObject *) PyContext_Copy(PyObject *);
27+
PyAPI_FUNC(PyObject *) PyContext_CopyCurrent(void);
2828

29-
PyAPI_FUNC(int) PyContext_Enter(PyContext *);
30-
PyAPI_FUNC(int) PyContext_Exit(PyContext *);
29+
PyAPI_FUNC(int) PyContext_Enter(PyObject *);
30+
PyAPI_FUNC(int) PyContext_Exit(PyObject *);
3131

3232

3333
/* Create a new context variable.
3434
3535
default_value can be NULL.
3636
*/
37-
PyAPI_FUNC(PyContextVar *) PyContextVar_New(
37+
PyAPI_FUNC(PyObject *) PyContextVar_New(
3838
const char *name, PyObject *default_value);
3939

4040

@@ -54,21 +54,19 @@ PyAPI_FUNC(PyContextVar *) PyContextVar_New(
5454
'*value' will be a new ref, if not NULL.
5555
*/
5656
PyAPI_FUNC(int) PyContextVar_Get(
57-
PyContextVar *var, PyObject *default_value, PyObject **value);
57+
PyObject *var, PyObject *default_value, PyObject **value);
5858

5959

6060
/* Set a new value for the variable.
6161
Returns NULL if an error occurs.
6262
*/
63-
PyAPI_FUNC(PyContextToken *) PyContextVar_Set(
64-
PyContextVar *var, PyObject *value);
63+
PyAPI_FUNC(PyObject *) PyContextVar_Set(PyObject *var, PyObject *value);
6564

6665

6766
/* Reset a variable to its previous value.
6867
Returns 0 on success, -1 on error.
6968
*/
70-
PyAPI_FUNC(int) PyContextVar_Reset(
71-
PyContextVar *var, PyContextToken *token);
69+
PyAPI_FUNC(int) PyContextVar_Reset(PyObject *var, PyObject *token);
7270

7371

7472
/* This method is exposed only for CPython tests. Don not use it. */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix contextvars C API to use PyObject* pointer types.

Modules/_contextvarsmodule.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ static PyObject *
1616
_contextvars_copy_context_impl(PyObject *module)
1717
/*[clinic end generated code: output=1fcd5da7225c4fa9 input=89bb9ae485888440]*/
1818
{
19-
return (PyObject *)PyContext_CopyCurrent();
19+
return PyContext_CopyCurrent();
2020
}
2121

2222

Python/context.c

+62-23
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,28 @@ module _contextvars
1818
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a0955718c8b8cea6]*/
1919

2020

21+
#define ENSURE_Context(o, err_ret) \
22+
if (!PyContext_CheckExact(o)) { \
23+
PyErr_SetString(PyExc_TypeError, \
24+
"an instance of Context was expected"); \
25+
return err_ret; \
26+
}
27+
28+
#define ENSURE_ContextVar(o, err_ret) \
29+
if (!PyContextVar_CheckExact(o)) { \
30+
PyErr_SetString(PyExc_TypeError, \
31+
"an instance of ContextVar was expected"); \
32+
return err_ret; \
33+
}
34+
35+
#define ENSURE_ContextToken(o, err_ret) \
36+
if (!PyContextToken_CheckExact(o)) { \
37+
PyErr_SetString(PyExc_TypeError, \
38+
"an instance of Token was expected"); \
39+
return err_ret; \
40+
}
41+
42+
2143
/////////////////////////// Context API
2244

2345

@@ -50,35 +72,40 @@ _PyContext_NewHamtForTests(void)
5072
}
5173

5274

53-
PyContext *
75+
PyObject *
5476
PyContext_New(void)
5577
{
56-
return context_new_empty();
78+
return (PyObject *)context_new_empty();
5779
}
5880

5981

60-
PyContext *
61-
PyContext_Copy(PyContext * ctx)
82+
PyObject *
83+
PyContext_Copy(PyObject * octx)
6284
{
63-
return context_new_from_vars(ctx->ctx_vars);
85+
ENSURE_Context(octx, NULL)
86+
PyContext *ctx = (PyContext *)octx;
87+
return (PyObject *)context_new_from_vars(ctx->ctx_vars);
6488
}
6589

6690

67-
PyContext *
91+
PyObject *
6892
PyContext_CopyCurrent(void)
6993
{
7094
PyContext *ctx = context_get();
7195
if (ctx == NULL) {
7296
return NULL;
7397
}
7498

75-
return context_new_from_vars(ctx->ctx_vars);
99+
return (PyObject *)context_new_from_vars(ctx->ctx_vars);
76100
}
77101

78102

79103
int
80-
PyContext_Enter(PyContext *ctx)
104+
PyContext_Enter(PyObject *octx)
81105
{
106+
ENSURE_Context(octx, -1)
107+
PyContext *ctx = (PyContext *)octx;
108+
82109
if (ctx->ctx_entered) {
83110
PyErr_Format(PyExc_RuntimeError,
84111
"cannot enter context: %R is already entered", ctx);
@@ -100,8 +127,11 @@ PyContext_Enter(PyContext *ctx)
100127

101128

102129
int
103-
PyContext_Exit(PyContext *ctx)
130+
PyContext_Exit(PyObject *octx)
104131
{
132+
ENSURE_Context(octx, -1)
133+
PyContext *ctx = (PyContext *)octx;
134+
105135
if (!ctx->ctx_entered) {
106136
PyErr_Format(PyExc_RuntimeError,
107137
"cannot exit context: %R has not been entered", ctx);
@@ -129,7 +159,7 @@ PyContext_Exit(PyContext *ctx)
129159
}
130160

131161

132-
PyContextVar *
162+
PyObject *
133163
PyContextVar_New(const char *name, PyObject *def)
134164
{
135165
PyObject *pyname = PyUnicode_FromString(name);
@@ -138,14 +168,15 @@ PyContextVar_New(const char *name, PyObject *def)
138168
}
139169
PyContextVar *var = contextvar_new(pyname, def);
140170
Py_DECREF(pyname);
141-
return var;
171+
return (PyObject *)var;
142172
}
143173

144174

145175
int
146-
PyContextVar_Get(PyContextVar *var, PyObject *def, PyObject **val)
176+
PyContextVar_Get(PyObject *ovar, PyObject *def, PyObject **val)
147177
{
148-
assert(PyContextVar_CheckExact(var));
178+
ENSURE_ContextVar(ovar, -1)
179+
PyContextVar *var = (PyContextVar *)ovar;
149180

150181
PyThreadState *ts = PyThreadState_GET();
151182
assert(ts != NULL);
@@ -204,9 +235,12 @@ PyContextVar_Get(PyContextVar *var, PyObject *def, PyObject **val)
204235
}
205236

206237

207-
PyContextToken *
208-
PyContextVar_Set(PyContextVar *var, PyObject *val)
238+
PyObject *
239+
PyContextVar_Set(PyObject *ovar, PyObject *val)
209240
{
241+
ENSURE_ContextVar(ovar, NULL)
242+
PyContextVar *var = (PyContextVar *)ovar;
243+
210244
if (!PyContextVar_CheckExact(var)) {
211245
PyErr_SetString(
212246
PyExc_TypeError, "an instance of ContextVar was expected");
@@ -233,13 +267,18 @@ PyContextVar_Set(PyContextVar *var, PyObject *val)
233267
return NULL;
234268
}
235269

236-
return tok;
270+
return (PyObject *)tok;
237271
}
238272

239273

240274
int
241-
PyContextVar_Reset(PyContextVar *var, PyContextToken *tok)
275+
PyContextVar_Reset(PyObject *ovar, PyObject *otok)
242276
{
277+
ENSURE_ContextVar(ovar, -1)
278+
ENSURE_ContextToken(otok, -1)
279+
PyContextVar *var = (PyContextVar *)ovar;
280+
PyContextToken *tok = (PyContextToken *)otok;
281+
243282
if (tok->tok_used) {
244283
PyErr_Format(PyExc_RuntimeError,
245284
"%R has already been used once", tok);
@@ -376,7 +415,7 @@ context_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
376415
PyExc_TypeError, "Context() does not accept any arguments");
377416
return NULL;
378417
}
379-
return (PyObject *)PyContext_New();
418+
return PyContext_New();
380419
}
381420

382421
static int
@@ -587,14 +626,14 @@ context_run(PyContext *self, PyObject *const *args,
587626
return NULL;
588627
}
589628

590-
if (PyContext_Enter(self)) {
629+
if (PyContext_Enter((PyObject *)self)) {
591630
return NULL;
592631
}
593632

594633
PyObject *call_result = _PyObject_FastCallKeywords(
595634
args[0], args + 1, nargs - 1, kwnames);
596635

597-
if (PyContext_Exit(self)) {
636+
if (PyContext_Exit((PyObject *)self)) {
598637
return NULL;
599638
}
600639

@@ -908,7 +947,7 @@ _contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value)
908947
}
909948

910949
PyObject *val;
911-
if (PyContextVar_Get(self, default_value, &val) < 0) {
950+
if (PyContextVar_Get((PyObject *)self, default_value, &val) < 0) {
912951
return NULL;
913952
}
914953

@@ -937,7 +976,7 @@ static PyObject *
937976
_contextvars_ContextVar_set(PyContextVar *self, PyObject *value)
938977
/*[clinic end generated code: output=446ed5e820d6d60b input=c0a6887154227453]*/
939978
{
940-
return (PyObject *)PyContextVar_Set(self, value);
979+
return PyContextVar_Set((PyObject *)self, value);
941980
}
942981

943982
/*[clinic input]
@@ -961,7 +1000,7 @@ _contextvars_ContextVar_reset(PyContextVar *self, PyObject *token)
9611000
return NULL;
9621001
}
9631002

964-
if (PyContextVar_Reset(self, (PyContextToken *)token)) {
1003+
if (PyContextVar_Reset((PyObject *)self, token)) {
9651004
return NULL;
9661005
}
9671006

0 commit comments

Comments
 (0)