Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.7] bpo-34762: Fix contextvars C API to use PyObject* pointer types. (GH-9473) #9478

Merged
merged 1 commit into from
Sep 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 29 additions & 10 deletions Doc/c-api/contextvars.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@
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

This section details the public C API for the :mod:`contextvars` module.
Expand Down Expand Up @@ -56,27 +75,27 @@ Type-check macros:

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
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.
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.
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.
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
current context for the current thread. Returns ``0`` on success,
Expand All @@ -90,14 +109,14 @@ Context object management 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
for introspection and debug purposes. The *def* parameter may optionally
specify the default value for the context variable. If an error has
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
occurred during lookup, and ``0`` if no error occurred, whether or not
Expand All @@ -112,13 +131,13 @@ Context variable functions:

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
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.

.. 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
:c:func:`PyContextVar_Set` that returned the *token* was called.
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
this behavior is unwanted, set :c:data:`Py_IgnoreEnvironmentFlag` to 1 before
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`.
20 changes: 9 additions & 11 deletions Include/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@ typedef struct _pycontexttokenobject PyContextToken;
#define PyContextToken_CheckExact(o) (Py_TYPE(o) == &PyContextToken_Type)


PyAPI_FUNC(PyContext *) PyContext_New(void);
PyAPI_FUNC(PyContext *) PyContext_Copy(PyContext *);
PyAPI_FUNC(PyContext *) PyContext_CopyCurrent(void);
PyAPI_FUNC(PyObject *) PyContext_New(void);
PyAPI_FUNC(PyObject *) PyContext_Copy(PyObject *);
PyAPI_FUNC(PyObject *) PyContext_CopyCurrent(void);

PyAPI_FUNC(int) PyContext_Enter(PyContext *);
PyAPI_FUNC(int) PyContext_Exit(PyContext *);
PyAPI_FUNC(int) PyContext_Enter(PyObject *);
PyAPI_FUNC(int) PyContext_Exit(PyObject *);


/* Create a new context variable.

default_value can be NULL.
*/
PyAPI_FUNC(PyContextVar *) PyContextVar_New(
PyAPI_FUNC(PyObject *) PyContextVar_New(
const char *name, PyObject *default_value);


Expand All @@ -54,21 +54,19 @@ PyAPI_FUNC(PyContextVar *) PyContextVar_New(
'*value' will be a new ref, if not NULL.
*/
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.
Returns NULL if an error occurs.
*/
PyAPI_FUNC(PyContextToken *) PyContextVar_Set(
PyContextVar *var, PyObject *value);
PyAPI_FUNC(PyObject *) PyContextVar_Set(PyObject *var, PyObject *value);


/* Reset a variable to its previous value.
Returns 0 on success, -1 on error.
*/
PyAPI_FUNC(int) PyContextVar_Reset(
PyContextVar *var, PyContextToken *token);
PyAPI_FUNC(int) PyContextVar_Reset(PyObject *var, PyObject *token);


/* This method is exposed only for CPython tests. Don not use it. */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix contextvars C API to use PyObject* pointer types.
2 changes: 1 addition & 1 deletion Modules/_contextvarsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ static PyObject *
_contextvars_copy_context_impl(PyObject *module)
/*[clinic end generated code: output=1fcd5da7225c4fa9 input=89bb9ae485888440]*/
{
return (PyObject *)PyContext_CopyCurrent();
return PyContext_CopyCurrent();
}


Expand Down
85 changes: 62 additions & 23 deletions Python/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,28 @@ module _contextvars
/*[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


Expand Down Expand Up @@ -50,35 +72,40 @@ _PyContext_NewHamtForTests(void)
}


PyContext *
PyObject *
PyContext_New(void)
{
return context_new_empty();
return (PyObject *)context_new_empty();
}


PyContext *
PyContext_Copy(PyContext * ctx)
PyObject *
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 *ctx = context_get();
if (ctx == NULL) {
return NULL;
}

return context_new_from_vars(ctx->ctx_vars);
return (PyObject *)context_new_from_vars(ctx->ctx_vars);
}


int
PyContext_Enter(PyContext *ctx)
PyContext_Enter(PyObject *octx)
{
ENSURE_Context(octx, -1)
PyContext *ctx = (PyContext *)octx;

if (ctx->ctx_entered) {
PyErr_Format(PyExc_RuntimeError,
"cannot enter context: %R is already entered", ctx);
Expand All @@ -100,8 +127,11 @@ PyContext_Enter(PyContext *ctx)


int
PyContext_Exit(PyContext *ctx)
PyContext_Exit(PyObject *octx)
{
ENSURE_Context(octx, -1)
PyContext *ctx = (PyContext *)octx;

if (!ctx->ctx_entered) {
PyErr_Format(PyExc_RuntimeError,
"cannot exit context: %R has not been entered", ctx);
Expand Down Expand Up @@ -129,7 +159,7 @@ PyContext_Exit(PyContext *ctx)
}


PyContextVar *
PyObject *
PyContextVar_New(const char *name, PyObject *def)
{
PyObject *pyname = PyUnicode_FromString(name);
Expand All @@ -138,14 +168,15 @@ PyContextVar_New(const char *name, PyObject *def)
}
PyContextVar *var = contextvar_new(pyname, def);
Py_DECREF(pyname);
return var;
return (PyObject *)var;
}


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();
assert(ts != NULL);
Expand Down Expand Up @@ -204,9 +235,12 @@ PyContextVar_Get(PyContextVar *var, PyObject *def, PyObject **val)
}


PyContextToken *
PyContextVar_Set(PyContextVar *var, PyObject *val)
PyObject *
PyContextVar_Set(PyObject *ovar, PyObject *val)
{
ENSURE_ContextVar(ovar, NULL)
PyContextVar *var = (PyContextVar *)ovar;

if (!PyContextVar_CheckExact(var)) {
PyErr_SetString(
PyExc_TypeError, "an instance of ContextVar was expected");
Expand All @@ -233,13 +267,18 @@ PyContextVar_Set(PyContextVar *var, PyObject *val)
return NULL;
}

return tok;
return (PyObject *)tok;
}


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) {
PyErr_Format(PyExc_RuntimeError,
"%R has already been used once", tok);
Expand Down Expand Up @@ -376,7 +415,7 @@ context_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyExc_TypeError, "Context() does not accept any arguments");
return NULL;
}
return (PyObject *)PyContext_New();
return PyContext_New();
}

static int
Expand Down Expand Up @@ -572,14 +611,14 @@ context_run(PyContext *self, PyObject *const *args,
return NULL;
}

if (PyContext_Enter(self)) {
if (PyContext_Enter((PyObject *)self)) {
return NULL;
}

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

if (PyContext_Exit(self)) {
if (PyContext_Exit((PyObject *)self)) {
return NULL;
}

Expand Down Expand Up @@ -885,7 +924,7 @@ _contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value)
}

PyObject *val;
if (PyContextVar_Get(self, default_value, &val) < 0) {
if (PyContextVar_Get((PyObject *)self, default_value, &val) < 0) {
return NULL;
}

Expand All @@ -907,7 +946,7 @@ static PyObject *
_contextvars_ContextVar_set(PyContextVar *self, PyObject *value)
/*[clinic end generated code: output=446ed5e820d6d60b input=a2d88f57c6d86f7c]*/
{
return (PyObject *)PyContextVar_Set(self, value);
return PyContextVar_Set((PyObject *)self, value);
}

/*[clinic input]
Expand All @@ -926,7 +965,7 @@ _contextvars_ContextVar_reset(PyContextVar *self, PyObject *token)
return NULL;
}

if (PyContextVar_Reset(self, (PyContextToken *)token)) {
if (PyContextVar_Reset((PyObject *)self, token)) {
return NULL;
}

Expand Down