Commit 2ec872b3 authored by Yury Selivanov's avatar Yury Selivanov Committed by GitHub

bpo-34762: Fix contextvars C API to use PyObject* pointer types. (GH-9473)

parent b46ad543
...@@ -5,6 +5,25 @@ ...@@ -5,6 +5,25 @@
Context Variables Objects Context Variables Objects
------------------------- -------------------------
.. _contextvarsobjects_pointertype_change:
.. versionchanged:: 3.7.1
.. note::
In Python 3.7.1 the signatures of all context variables
C APIs were **changed** to use :c:type:`PyObject` pointers instead
of :c:type:`PyContext`, :c:type:`PyContextVar`, and
:c:type:`PyContextToken`, e.g.::
// in 3.7.0:
PyContext *PyContext_New(void);
// in 3.7.1+:
PyObject *PyContext_New(void);
See :issue:`34762` for more details.
.. versionadded:: 3.7 .. versionadded:: 3.7
This section details the public C API for the :mod:`contextvars` module. This section details the public C API for the :mod:`contextvars` module.
...@@ -56,27 +75,27 @@ Type-check macros: ...@@ -56,27 +75,27 @@ Type-check macros:
Context object management functions: Context object management functions:
.. c:function:: PyContext *PyContext_New(void) .. c:function:: PyObject *PyContext_New(void)
Create a new empty context object. Returns ``NULL`` if an error Create a new empty context object. Returns ``NULL`` if an error
has occurred. has occurred.
.. c:function:: PyContext *PyContext_Copy(PyContext *ctx) .. c:function:: PyObject *PyContext_Copy(PyObject *ctx)
Create a shallow copy of the passed *ctx* context object. Create a shallow copy of the passed *ctx* context object.
Returns ``NULL`` if an error has occurred. Returns ``NULL`` if an error has occurred.
.. c:function:: PyContext *PyContext_CopyCurrent(void) .. c:function:: PyObject *PyContext_CopyCurrent(void)
Create a shallow copy of the current thread context. Create a shallow copy of the current thread context.
Returns ``NULL`` if an error has occurred. Returns ``NULL`` if an error has occurred.
.. c:function:: int PyContext_Enter(PyContext *ctx) .. c:function:: int PyContext_Enter(PyObject *ctx)
Set *ctx* as the current context for the current thread. Set *ctx* as the current context for the current thread.
Returns ``0`` on success, and ``-1`` on error. Returns ``0`` on success, and ``-1`` on error.
.. c:function:: int PyContext_Exit(PyContext *ctx) .. c:function:: int PyContext_Exit(PyObject *ctx)
Deactivate the *ctx* context and restore the previous context as the Deactivate the *ctx* context and restore the previous context as the
current context for the current thread. Returns ``0`` on success, current context for the current thread. Returns ``0`` on success,
...@@ -90,14 +109,14 @@ Context object management functions: ...@@ -90,14 +109,14 @@ Context object management functions:
Context variable functions: Context variable functions:
.. c:function:: PyContextVar *PyContextVar_New(const char *name, PyObject *def) .. c:function:: PyObject *PyContextVar_New(const char *name, PyObject *def)
Create a new ``ContextVar`` object. The *name* parameter is used Create a new ``ContextVar`` object. The *name* parameter is used
for introspection and debug purposes. The *def* parameter may optionally for introspection and debug purposes. The *def* parameter may optionally
specify the default value for the context variable. If an error has specify the default value for the context variable. If an error has
occurred, this function returns ``NULL``. occurred, this function returns ``NULL``.
.. c:function:: int PyContextVar_Get(PyContextVar *var, PyObject *default_value, PyObject **value) .. c:function:: int PyContextVar_Get(PyObject *var, PyObject *default_value, PyObject **value)
Get the value of a context variable. Returns ``-1`` if an error has Get the value of a context variable. Returns ``-1`` if an error has
occurred during lookup, and ``0`` if no error occurred, whether or not occurred during lookup, and ``0`` if no error occurred, whether or not
...@@ -112,13 +131,13 @@ Context variable functions: ...@@ -112,13 +131,13 @@ Context variable functions:
If the value was found, the function will create a new reference to it. If the value was found, the function will create a new reference to it.
.. c:function:: PyContextToken *PyContextVar_Set(PyContextVar *var, PyObject *value) .. c:function:: PyObject *PyContextVar_Set(PyObject *var, PyObject *value)
Set the value of *var* to *value* in the current context. Returns a Set the value of *var* to *value* in the current context. Returns a
pointer to a :c:type:`PyContextToken` object, or ``NULL`` if an error pointer to a :c:type:`PyObject` object, or ``NULL`` if an error
has occurred. has occurred.
.. c:function:: int PyContextVar_Reset(PyContextVar *var, PyContextToken *token) .. c:function:: int PyContextVar_Reset(PyObject *var, PyObject *token)
Reset the state of the *var* context variable to that it was in before Reset the state of the *var* context variable to that it was in before
:c:func:`PyContextVar_Set` that returned the *token* was called. :c:func:`PyContextVar_Set` that returned the *token* was called.
......
...@@ -2494,3 +2494,7 @@ versions, it respected an ill-defined subset of those environment variables, ...@@ -2494,3 +2494,7 @@ versions, it respected an ill-defined subset of those environment variables,
while in Python 3.7.0 it didn't read any of them due to :issue:`34247`). If while in Python 3.7.0 it didn't read any of them due to :issue:`34247`). If
this behavior is unwanted, set :c:data:`Py_IgnoreEnvironmentFlag` to 1 before this behavior is unwanted, set :c:data:`Py_IgnoreEnvironmentFlag` to 1 before
calling :c:func:`Py_Initialize`. calling :c:func:`Py_Initialize`.
In 3.7.1 the C API for Context Variables
:ref:`was updated <contextvarsobjects_pointertype_change>` to use
:c:type:`PyObject` pointers. See also :issue:`34762`.
...@@ -22,19 +22,19 @@ typedef struct _pycontexttokenobject PyContextToken; ...@@ -22,19 +22,19 @@ typedef struct _pycontexttokenobject PyContextToken;
#define PyContextToken_CheckExact(o) (Py_TYPE(o) == &PyContextToken_Type) #define PyContextToken_CheckExact(o) (Py_TYPE(o) == &PyContextToken_Type)
PyAPI_FUNC(PyContext *) PyContext_New(void); PyAPI_FUNC(PyObject *) PyContext_New(void);
PyAPI_FUNC(PyContext *) PyContext_Copy(PyContext *); PyAPI_FUNC(PyObject *) PyContext_Copy(PyObject *);
PyAPI_FUNC(PyContext *) PyContext_CopyCurrent(void); PyAPI_FUNC(PyObject *) PyContext_CopyCurrent(void);
PyAPI_FUNC(int) PyContext_Enter(PyContext *); PyAPI_FUNC(int) PyContext_Enter(PyObject *);
PyAPI_FUNC(int) PyContext_Exit(PyContext *); PyAPI_FUNC(int) PyContext_Exit(PyObject *);
/* Create a new context variable. /* Create a new context variable.
default_value can be NULL. default_value can be NULL.
*/ */
PyAPI_FUNC(PyContextVar *) PyContextVar_New( PyAPI_FUNC(PyObject *) PyContextVar_New(
const char *name, PyObject *default_value); const char *name, PyObject *default_value);
...@@ -54,21 +54,19 @@ PyAPI_FUNC(PyContextVar *) PyContextVar_New( ...@@ -54,21 +54,19 @@ PyAPI_FUNC(PyContextVar *) PyContextVar_New(
'*value' will be a new ref, if not NULL. '*value' will be a new ref, if not NULL.
*/ */
PyAPI_FUNC(int) PyContextVar_Get( PyAPI_FUNC(int) PyContextVar_Get(
PyContextVar *var, PyObject *default_value, PyObject **value); PyObject *var, PyObject *default_value, PyObject **value);
/* Set a new value for the variable. /* Set a new value for the variable.
Returns NULL if an error occurs. Returns NULL if an error occurs.
*/ */
PyAPI_FUNC(PyContextToken *) PyContextVar_Set( PyAPI_FUNC(PyObject *) PyContextVar_Set(PyObject *var, PyObject *value);
PyContextVar *var, PyObject *value);
/* Reset a variable to its previous value. /* Reset a variable to its previous value.
Returns 0 on success, -1 on error. Returns 0 on success, -1 on error.
*/ */
PyAPI_FUNC(int) PyContextVar_Reset( PyAPI_FUNC(int) PyContextVar_Reset(PyObject *var, PyObject *token);
PyContextVar *var, PyContextToken *token);
/* This method is exposed only for CPython tests. Don not use it. */ /* This method is exposed only for CPython tests. Don not use it. */
......
Fix contextvars C API to use PyObject* pointer types.
...@@ -16,7 +16,7 @@ static PyObject * ...@@ -16,7 +16,7 @@ static PyObject *
_contextvars_copy_context_impl(PyObject *module) _contextvars_copy_context_impl(PyObject *module)
/*[clinic end generated code: output=1fcd5da7225c4fa9 input=89bb9ae485888440]*/ /*[clinic end generated code: output=1fcd5da7225c4fa9 input=89bb9ae485888440]*/
{ {
return (PyObject *)PyContext_CopyCurrent(); return PyContext_CopyCurrent();
} }
......
...@@ -18,6 +18,28 @@ module _contextvars ...@@ -18,6 +18,28 @@ module _contextvars
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a0955718c8b8cea6]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=a0955718c8b8cea6]*/
#define ENSURE_Context(o, err_ret) \
if (!PyContext_CheckExact(o)) { \
PyErr_SetString(PyExc_TypeError, \
"an instance of Context was expected"); \
return err_ret; \
}
#define ENSURE_ContextVar(o, err_ret) \
if (!PyContextVar_CheckExact(o)) { \
PyErr_SetString(PyExc_TypeError, \
"an instance of ContextVar was expected"); \
return err_ret; \
}
#define ENSURE_ContextToken(o, err_ret) \
if (!PyContextToken_CheckExact(o)) { \
PyErr_SetString(PyExc_TypeError, \
"an instance of Token was expected"); \
return err_ret; \
}
/////////////////////////// Context API /////////////////////////// Context API
...@@ -50,21 +72,23 @@ _PyContext_NewHamtForTests(void) ...@@ -50,21 +72,23 @@ _PyContext_NewHamtForTests(void)
} }
PyContext * PyObject *
PyContext_New(void) PyContext_New(void)
{ {
return context_new_empty(); return (PyObject *)context_new_empty();
} }
PyContext * PyObject *
PyContext_Copy(PyContext * ctx) PyContext_Copy(PyObject * octx)
{ {
return context_new_from_vars(ctx->ctx_vars); ENSURE_Context(octx, NULL)
PyContext *ctx = (PyContext *)octx;
return (PyObject *)context_new_from_vars(ctx->ctx_vars);
} }
PyContext * PyObject *
PyContext_CopyCurrent(void) PyContext_CopyCurrent(void)
{ {
PyContext *ctx = context_get(); PyContext *ctx = context_get();
...@@ -72,13 +96,16 @@ PyContext_CopyCurrent(void) ...@@ -72,13 +96,16 @@ PyContext_CopyCurrent(void)
return NULL; return NULL;
} }
return context_new_from_vars(ctx->ctx_vars); return (PyObject *)context_new_from_vars(ctx->ctx_vars);
} }
int int
PyContext_Enter(PyContext *ctx) PyContext_Enter(PyObject *octx)
{ {
ENSURE_Context(octx, -1)
PyContext *ctx = (PyContext *)octx;
if (ctx->ctx_entered) { if (ctx->ctx_entered) {
PyErr_Format(PyExc_RuntimeError, PyErr_Format(PyExc_RuntimeError,
"cannot enter context: %R is already entered", ctx); "cannot enter context: %R is already entered", ctx);
...@@ -100,8 +127,11 @@ PyContext_Enter(PyContext *ctx) ...@@ -100,8 +127,11 @@ PyContext_Enter(PyContext *ctx)
int int
PyContext_Exit(PyContext *ctx) PyContext_Exit(PyObject *octx)
{ {
ENSURE_Context(octx, -1)
PyContext *ctx = (PyContext *)octx;
if (!ctx->ctx_entered) { if (!ctx->ctx_entered) {
PyErr_Format(PyExc_RuntimeError, PyErr_Format(PyExc_RuntimeError,
"cannot exit context: %R has not been entered", ctx); "cannot exit context: %R has not been entered", ctx);
...@@ -129,7 +159,7 @@ PyContext_Exit(PyContext *ctx) ...@@ -129,7 +159,7 @@ PyContext_Exit(PyContext *ctx)
} }
PyContextVar * PyObject *
PyContextVar_New(const char *name, PyObject *def) PyContextVar_New(const char *name, PyObject *def)
{ {
PyObject *pyname = PyUnicode_FromString(name); PyObject *pyname = PyUnicode_FromString(name);
...@@ -138,14 +168,15 @@ PyContextVar_New(const char *name, PyObject *def) ...@@ -138,14 +168,15 @@ PyContextVar_New(const char *name, PyObject *def)
} }
PyContextVar *var = contextvar_new(pyname, def); PyContextVar *var = contextvar_new(pyname, def);
Py_DECREF(pyname); Py_DECREF(pyname);
return var; return (PyObject *)var;
} }
int int
PyContextVar_Get(PyContextVar *var, PyObject *def, PyObject **val) PyContextVar_Get(PyObject *ovar, PyObject *def, PyObject **val)
{ {
assert(PyContextVar_CheckExact(var)); ENSURE_ContextVar(ovar, -1)
PyContextVar *var = (PyContextVar *)ovar;
PyThreadState *ts = PyThreadState_GET(); PyThreadState *ts = PyThreadState_GET();
assert(ts != NULL); assert(ts != NULL);
...@@ -204,9 +235,12 @@ error: ...@@ -204,9 +235,12 @@ error:
} }
PyContextToken * PyObject *
PyContextVar_Set(PyContextVar *var, PyObject *val) PyContextVar_Set(PyObject *ovar, PyObject *val)
{ {
ENSURE_ContextVar(ovar, NULL)
PyContextVar *var = (PyContextVar *)ovar;
if (!PyContextVar_CheckExact(var)) { if (!PyContextVar_CheckExact(var)) {
PyErr_SetString( PyErr_SetString(
PyExc_TypeError, "an instance of ContextVar was expected"); PyExc_TypeError, "an instance of ContextVar was expected");
...@@ -233,13 +267,18 @@ PyContextVar_Set(PyContextVar *var, PyObject *val) ...@@ -233,13 +267,18 @@ PyContextVar_Set(PyContextVar *var, PyObject *val)
return NULL; return NULL;
} }
return tok; return (PyObject *)tok;
} }
int int
PyContextVar_Reset(PyContextVar *var, PyContextToken *tok) PyContextVar_Reset(PyObject *ovar, PyObject *otok)
{ {
ENSURE_ContextVar(ovar, -1)
ENSURE_ContextToken(otok, -1)
PyContextVar *var = (PyContextVar *)ovar;
PyContextToken *tok = (PyContextToken *)otok;
if (tok->tok_used) { if (tok->tok_used) {
PyErr_Format(PyExc_RuntimeError, PyErr_Format(PyExc_RuntimeError,
"%R has already been used once", tok); "%R has already been used once", tok);
...@@ -376,7 +415,7 @@ context_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) ...@@ -376,7 +415,7 @@ context_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyExc_TypeError, "Context() does not accept any arguments"); PyExc_TypeError, "Context() does not accept any arguments");
return NULL; return NULL;
} }
return (PyObject *)PyContext_New(); return PyContext_New();
} }
static int static int
...@@ -587,14 +626,14 @@ context_run(PyContext *self, PyObject *const *args, ...@@ -587,14 +626,14 @@ context_run(PyContext *self, PyObject *const *args,
return NULL; return NULL;
} }
if (PyContext_Enter(self)) { if (PyContext_Enter((PyObject *)self)) {
return NULL; return NULL;
} }
PyObject *call_result = _PyObject_FastCallKeywords( PyObject *call_result = _PyObject_FastCallKeywords(
args[0], args + 1, nargs - 1, kwnames); args[0], args + 1, nargs - 1, kwnames);
if (PyContext_Exit(self)) { if (PyContext_Exit((PyObject *)self)) {
return NULL; return NULL;
} }
...@@ -908,7 +947,7 @@ _contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value) ...@@ -908,7 +947,7 @@ _contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value)
} }
PyObject *val; PyObject *val;
if (PyContextVar_Get(self, default_value, &val) < 0) { if (PyContextVar_Get((PyObject *)self, default_value, &val) < 0) {
return NULL; return NULL;
} }
...@@ -937,7 +976,7 @@ static PyObject * ...@@ -937,7 +976,7 @@ static PyObject *
_contextvars_ContextVar_set(PyContextVar *self, PyObject *value) _contextvars_ContextVar_set(PyContextVar *self, PyObject *value)
/*[clinic end generated code: output=446ed5e820d6d60b input=c0a6887154227453]*/ /*[clinic end generated code: output=446ed5e820d6d60b input=c0a6887154227453]*/
{ {
return (PyObject *)PyContextVar_Set(self, value); return PyContextVar_Set((PyObject *)self, value);
} }
/*[clinic input] /*[clinic input]
...@@ -961,7 +1000,7 @@ _contextvars_ContextVar_reset(PyContextVar *self, PyObject *token) ...@@ -961,7 +1000,7 @@ _contextvars_ContextVar_reset(PyContextVar *self, PyObject *token)
return NULL; return NULL;
} }
if (PyContextVar_Reset(self, (PyContextToken *)token)) { if (PyContextVar_Reset((PyObject *)self, token)) {
return NULL; return NULL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment