bpo-32436: Implement PEP 567 (#5027)

This commit is contained in:
Yury Selivanov 2018-01-22 19:11:18 -05:00 committed by GitHub
parent 9089a26591
commit f23746a934
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 6269 additions and 120 deletions

View File

@ -109,6 +109,7 @@
#include "pyerrors.h" #include "pyerrors.h"
#include "pystate.h" #include "pystate.h"
#include "context.h"
#include "pyarena.h" #include "pyarena.h"
#include "modsupport.h" #include "modsupport.h"

86
Include/context.h Normal file
View File

@ -0,0 +1,86 @@
#ifndef Py_CONTEXT_H
#define Py_CONTEXT_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef Py_LIMITED_API
PyAPI_DATA(PyTypeObject) PyContext_Type;
typedef struct _pycontextobject PyContext;
PyAPI_DATA(PyTypeObject) PyContextVar_Type;
typedef struct _pycontextvarobject PyContextVar;
PyAPI_DATA(PyTypeObject) PyContextToken_Type;
typedef struct _pycontexttokenobject PyContextToken;
#define PyContext_CheckExact(o) (Py_TYPE(o) == &PyContext_Type)
#define PyContextVar_CheckExact(o) (Py_TYPE(o) == &PyContextVar_Type)
#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(int) PyContext_Enter(PyContext *);
PyAPI_FUNC(int) PyContext_Exit(PyContext *);
/* Create a new context variable.
default_value can be NULL.
*/
PyAPI_FUNC(PyContextVar *) PyContextVar_New(
const char *name, PyObject *default_value);
/* Get a value for the variable.
Returns -1 if an error occurred during lookup.
Returns 0 if value either was or was not found.
If value was found, *value will point to it.
If not, it will point to:
- default_value, if not NULL;
- the default value of "var", if not NULL;
- NULL.
'*value' will be a new ref, if not NULL.
*/
PyAPI_FUNC(int) PyContextVar_Get(
PyContextVar *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);
/* Reset a variable to its previous value.
Returns 0 on sucess, -1 on error.
*/
PyAPI_FUNC(int) PyContextVar_Reset(
PyContextVar *var, PyContextToken *token);
/* This method is exposed only for CPython tests. Don not use it. */
PyAPI_FUNC(PyObject *) _PyContext_NewHamtForTests(void);
PyAPI_FUNC(int) PyContext_ClearFreeList(void);
#endif /* !Py_LIMITED_API */
#ifdef __cplusplus
}
#endif
#endif /* !Py_CONTEXT_H */

View File

@ -0,0 +1,41 @@
#ifndef Py_INTERNAL_CONTEXT_H
#define Py_INTERNAL_CONTEXT_H
#include "internal/hamt.h"
struct _pycontextobject {
PyObject_HEAD
PyContext *ctx_prev;
PyHamtObject *ctx_vars;
PyObject *ctx_weakreflist;
int ctx_entered;
};
struct _pycontextvarobject {
PyObject_HEAD
PyObject *var_name;
PyObject *var_default;
PyObject *var_cached;
uint64_t var_cached_tsid;
uint64_t var_cached_tsver;
Py_hash_t var_hash;
};
struct _pycontexttokenobject {
PyObject_HEAD
PyContext *tok_ctx;
PyContextVar *tok_var;
PyObject *tok_oldval;
int tok_used;
};
int _PyContext_Init(void);
void _PyContext_Fini(void);
#endif /* !Py_INTERNAL_CONTEXT_H */

113
Include/internal/hamt.h Normal file
View File

@ -0,0 +1,113 @@
#ifndef Py_INTERNAL_HAMT_H
#define Py_INTERNAL_HAMT_H
#define _Py_HAMT_MAX_TREE_DEPTH 7
#define PyHamt_Check(o) (Py_TYPE(o) == &_PyHamt_Type)
/* Abstract tree node. */
typedef struct {
PyObject_HEAD
} PyHamtNode;
/* An HAMT immutable mapping collection. */
typedef struct {
PyObject_HEAD
PyHamtNode *h_root;
PyObject *h_weakreflist;
Py_ssize_t h_count;
} PyHamtObject;
/* A struct to hold the state of depth-first traverse of the tree.
HAMT is an immutable collection. Iterators will hold a strong reference
to it, and every node in the HAMT has strong references to its children.
So for iterators, we can implement zero allocations and zero reference
inc/dec depth-first iteration.
- i_nodes: an array of seven pointers to tree nodes
- i_level: the current node in i_nodes
- i_pos: an array of positions within nodes in i_nodes.
*/
typedef struct {
PyHamtNode *i_nodes[_Py_HAMT_MAX_TREE_DEPTH];
Py_ssize_t i_pos[_Py_HAMT_MAX_TREE_DEPTH];
int8_t i_level;
} PyHamtIteratorState;
/* Base iterator object.
Contains the iteration state, a pointer to the HAMT tree,
and a pointer to the 'yield function'. The latter is a simple
function that returns a key/value tuple for the 'Items' iterator,
just a key for the 'Keys' iterator, and a value for the 'Values'
iterator.
*/
typedef struct {
PyObject_HEAD
PyHamtObject *hi_obj;
PyHamtIteratorState hi_iter;
binaryfunc hi_yield;
} PyHamtIterator;
PyAPI_DATA(PyTypeObject) _PyHamt_Type;
PyAPI_DATA(PyTypeObject) _PyHamt_ArrayNode_Type;
PyAPI_DATA(PyTypeObject) _PyHamt_BitmapNode_Type;
PyAPI_DATA(PyTypeObject) _PyHamt_CollisionNode_Type;
PyAPI_DATA(PyTypeObject) _PyHamtKeys_Type;
PyAPI_DATA(PyTypeObject) _PyHamtValues_Type;
PyAPI_DATA(PyTypeObject) _PyHamtItems_Type;
/* Create a new HAMT immutable mapping. */
PyHamtObject * _PyHamt_New(void);
/* Return a new collection based on "o", but with an additional
key/val pair. */
PyHamtObject * _PyHamt_Assoc(PyHamtObject *o, PyObject *key, PyObject *val);
/* Return a new collection based on "o", but without "key". */
PyHamtObject * _PyHamt_Without(PyHamtObject *o, PyObject *key);
/* Find "key" in the "o" collection.
Return:
- -1: An error ocurred.
- 0: "key" wasn't found in "o".
- 1: "key" is in "o"; "*val" is set to its value (a borrowed ref).
*/
int _PyHamt_Find(PyHamtObject *o, PyObject *key, PyObject **val);
/* Check if "v" is equal to "w".
Return:
- 0: v != w
- 1: v == w
- -1: An error occurred.
*/
int _PyHamt_Eq(PyHamtObject *v, PyHamtObject *w);
/* Return the size of "o"; equivalent of "len(o)". */
Py_ssize_t _PyHamt_Len(PyHamtObject *o);
/* Return a Keys iterator over "o". */
PyObject * _PyHamt_NewIterKeys(PyHamtObject *o);
/* Return a Values iterator over "o". */
PyObject * _PyHamt_NewIterValues(PyHamtObject *o);
/* Return a Items iterator over "o". */
PyObject * _PyHamt_NewIterItems(PyHamtObject *o);
int _PyHamt_Init(void);
void _PyHamt_Fini(void);
#endif /* !Py_INTERNAL_HAMT_H */

View File

@ -143,6 +143,8 @@ typedef struct _is {
/* AtExit module */ /* AtExit module */
void (*pyexitfunc)(PyObject *); void (*pyexitfunc)(PyObject *);
PyObject *pyexitmodule; PyObject *pyexitmodule;
uint64_t tstate_next_unique_id;
} PyInterpreterState; } PyInterpreterState;
#endif /* !Py_LIMITED_API */ #endif /* !Py_LIMITED_API */
@ -270,6 +272,12 @@ typedef struct _ts {
PyObject *async_gen_firstiter; PyObject *async_gen_firstiter;
PyObject *async_gen_finalizer; PyObject *async_gen_finalizer;
PyObject *context;
uint64_t context_ver;
/* Unique thread state id. */
uint64_t id;
/* XXX signal handlers should also be here */ /* XXX signal handlers should also be here */
} PyThreadState; } PyThreadState;

View File

@ -489,7 +489,7 @@ class BaseEventLoop(events.AbstractEventLoop):
""" """
return time.monotonic() return time.monotonic()
def call_later(self, delay, callback, *args): def call_later(self, delay, callback, *args, context=None):
"""Arrange for a callback to be called at a given time. """Arrange for a callback to be called at a given time.
Return a Handle: an opaque object with a cancel() method that Return a Handle: an opaque object with a cancel() method that
@ -505,12 +505,13 @@ class BaseEventLoop(events.AbstractEventLoop):
Any positional arguments after the callback will be passed to Any positional arguments after the callback will be passed to
the callback when it is called. the callback when it is called.
""" """
timer = self.call_at(self.time() + delay, callback, *args) timer = self.call_at(self.time() + delay, callback, *args,
context=context)
if timer._source_traceback: if timer._source_traceback:
del timer._source_traceback[-1] del timer._source_traceback[-1]
return timer return timer
def call_at(self, when, callback, *args): def call_at(self, when, callback, *args, context=None):
"""Like call_later(), but uses an absolute time. """Like call_later(), but uses an absolute time.
Absolute time corresponds to the event loop's time() method. Absolute time corresponds to the event loop's time() method.
@ -519,14 +520,14 @@ class BaseEventLoop(events.AbstractEventLoop):
if self._debug: if self._debug:
self._check_thread() self._check_thread()
self._check_callback(callback, 'call_at') self._check_callback(callback, 'call_at')
timer = events.TimerHandle(when, callback, args, self) timer = events.TimerHandle(when, callback, args, self, context)
if timer._source_traceback: if timer._source_traceback:
del timer._source_traceback[-1] del timer._source_traceback[-1]
heapq.heappush(self._scheduled, timer) heapq.heappush(self._scheduled, timer)
timer._scheduled = True timer._scheduled = True
return timer return timer
def call_soon(self, callback, *args): def call_soon(self, callback, *args, context=None):
"""Arrange for a callback to be called as soon as possible. """Arrange for a callback to be called as soon as possible.
This operates as a FIFO queue: callbacks are called in the This operates as a FIFO queue: callbacks are called in the
@ -540,7 +541,7 @@ class BaseEventLoop(events.AbstractEventLoop):
if self._debug: if self._debug:
self._check_thread() self._check_thread()
self._check_callback(callback, 'call_soon') self._check_callback(callback, 'call_soon')
handle = self._call_soon(callback, args) handle = self._call_soon(callback, args, context)
if handle._source_traceback: if handle._source_traceback:
del handle._source_traceback[-1] del handle._source_traceback[-1]
return handle return handle
@ -555,8 +556,8 @@ class BaseEventLoop(events.AbstractEventLoop):
f'a callable object was expected by {method}(), ' f'a callable object was expected by {method}(), '
f'got {callback!r}') f'got {callback!r}')
def _call_soon(self, callback, args): def _call_soon(self, callback, args, context):
handle = events.Handle(callback, args, self) handle = events.Handle(callback, args, self, context)
if handle._source_traceback: if handle._source_traceback:
del handle._source_traceback[-1] del handle._source_traceback[-1]
self._ready.append(handle) self._ready.append(handle)
@ -579,12 +580,12 @@ class BaseEventLoop(events.AbstractEventLoop):
"Non-thread-safe operation invoked on an event loop other " "Non-thread-safe operation invoked on an event loop other "
"than the current one") "than the current one")
def call_soon_threadsafe(self, callback, *args): def call_soon_threadsafe(self, callback, *args, context=None):
"""Like call_soon(), but thread-safe.""" """Like call_soon(), but thread-safe."""
self._check_closed() self._check_closed()
if self._debug: if self._debug:
self._check_callback(callback, 'call_soon_threadsafe') self._check_callback(callback, 'call_soon_threadsafe')
handle = self._call_soon(callback, args) handle = self._call_soon(callback, args, context)
if handle._source_traceback: if handle._source_traceback:
del handle._source_traceback[-1] del handle._source_traceback[-1]
self._write_to_self() self._write_to_self()

View File

@ -41,13 +41,13 @@ def _format_callbacks(cb):
return format_helpers._format_callback_source(callback, ()) return format_helpers._format_callback_source(callback, ())
if size == 1: if size == 1:
cb = format_cb(cb[0]) cb = format_cb(cb[0][0])
elif size == 2: elif size == 2:
cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1])) cb = '{}, {}'.format(format_cb(cb[0][0]), format_cb(cb[1][0]))
elif size > 2: elif size > 2:
cb = '{}, <{} more>, {}'.format(format_cb(cb[0]), cb = '{}, <{} more>, {}'.format(format_cb(cb[0][0]),
size - 2, size - 2,
format_cb(cb[-1])) format_cb(cb[-1][0]))
return f'cb=[{cb}]' return f'cb=[{cb}]'

View File

@ -11,6 +11,7 @@ __all__ = (
'_get_running_loop', '_get_running_loop',
) )
import contextvars
import os import os
import socket import socket
import subprocess import subprocess
@ -32,9 +33,13 @@ class Handle:
"""Object returned by callback registration methods.""" """Object returned by callback registration methods."""
__slots__ = ('_callback', '_args', '_cancelled', '_loop', __slots__ = ('_callback', '_args', '_cancelled', '_loop',
'_source_traceback', '_repr', '__weakref__') '_source_traceback', '_repr', '__weakref__',
'_context')
def __init__(self, callback, args, loop): def __init__(self, callback, args, loop, context=None):
if context is None:
context = contextvars.copy_context()
self._context = context
self._loop = loop self._loop = loop
self._callback = callback self._callback = callback
self._args = args self._args = args
@ -80,7 +85,7 @@ class Handle:
def _run(self): def _run(self):
try: try:
self._callback(*self._args) self._context.run(self._callback, *self._args)
except Exception as exc: except Exception as exc:
cb = format_helpers._format_callback_source( cb = format_helpers._format_callback_source(
self._callback, self._args) self._callback, self._args)
@ -101,9 +106,9 @@ class TimerHandle(Handle):
__slots__ = ['_scheduled', '_when'] __slots__ = ['_scheduled', '_when']
def __init__(self, when, callback, args, loop): def __init__(self, when, callback, args, loop, context=None):
assert when is not None assert when is not None
super().__init__(callback, args, loop) super().__init__(callback, args, loop, context)
if self._source_traceback: if self._source_traceback:
del self._source_traceback[-1] del self._source_traceback[-1]
self._when = when self._when = when

View File

@ -6,6 +6,7 @@ __all__ = (
) )
import concurrent.futures import concurrent.futures
import contextvars
import logging import logging
import sys import sys
@ -144,8 +145,8 @@ class Future:
return return
self._callbacks[:] = [] self._callbacks[:] = []
for callback in callbacks: for callback, ctx in callbacks:
self._loop.call_soon(callback, self) self._loop.call_soon(callback, self, context=ctx)
def cancelled(self): def cancelled(self):
"""Return True if the future was cancelled.""" """Return True if the future was cancelled."""
@ -192,7 +193,7 @@ class Future:
self.__log_traceback = False self.__log_traceback = False
return self._exception return self._exception
def add_done_callback(self, fn): def add_done_callback(self, fn, *, context=None):
"""Add a callback to be run when the future becomes done. """Add a callback to be run when the future becomes done.
The callback is called with a single argument - the future object. If The callback is called with a single argument - the future object. If
@ -200,9 +201,11 @@ class Future:
scheduled with call_soon. scheduled with call_soon.
""" """
if self._state != _PENDING: if self._state != _PENDING:
self._loop.call_soon(fn, self) self._loop.call_soon(fn, self, context=context)
else: else:
self._callbacks.append(fn) if context is None:
context = contextvars.copy_context()
self._callbacks.append((fn, context))
# New method not in PEP 3148. # New method not in PEP 3148.
@ -211,7 +214,9 @@ class Future:
Returns the number of callbacks removed. Returns the number of callbacks removed.
""" """
filtered_callbacks = [f for f in self._callbacks if f != fn] filtered_callbacks = [(f, ctx)
for (f, ctx) in self._callbacks
if f != fn]
removed_count = len(self._callbacks) - len(filtered_callbacks) removed_count = len(self._callbacks) - len(filtered_callbacks)
if removed_count: if removed_count:
self._callbacks[:] = filtered_callbacks self._callbacks[:] = filtered_callbacks

View File

@ -256,7 +256,7 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
def _add_reader(self, fd, callback, *args): def _add_reader(self, fd, callback, *args):
self._check_closed() self._check_closed()
handle = events.Handle(callback, args, self) handle = events.Handle(callback, args, self, None)
try: try:
key = self._selector.get_key(fd) key = self._selector.get_key(fd)
except KeyError: except KeyError:
@ -292,7 +292,7 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
def _add_writer(self, fd, callback, *args): def _add_writer(self, fd, callback, *args):
self._check_closed() self._check_closed()
handle = events.Handle(callback, args, self) handle = events.Handle(callback, args, self, None)
try: try:
key = self._selector.get_key(fd) key = self._selector.get_key(fd)
except KeyError: except KeyError:

View File

@ -10,6 +10,7 @@ __all__ = (
) )
import concurrent.futures import concurrent.futures
import contextvars
import functools import functools
import inspect import inspect
import types import types
@ -96,8 +97,9 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
self._must_cancel = False self._must_cancel = False
self._fut_waiter = None self._fut_waiter = None
self._coro = coro self._coro = coro
self._context = contextvars.copy_context()
self._loop.call_soon(self._step) self._loop.call_soon(self._step, context=self._context)
_register_task(self) _register_task(self)
def __del__(self): def __del__(self):
@ -229,15 +231,18 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
new_exc = RuntimeError( new_exc = RuntimeError(
f'Task {self!r} got Future ' f'Task {self!r} got Future '
f'{result!r} attached to a different loop') f'{result!r} attached to a different loop')
self._loop.call_soon(self._step, new_exc) self._loop.call_soon(
self._step, new_exc, context=self._context)
elif blocking: elif blocking:
if result is self: if result is self:
new_exc = RuntimeError( new_exc = RuntimeError(
f'Task cannot await on itself: {self!r}') f'Task cannot await on itself: {self!r}')
self._loop.call_soon(self._step, new_exc) self._loop.call_soon(
self._step, new_exc, context=self._context)
else: else:
result._asyncio_future_blocking = False result._asyncio_future_blocking = False
result.add_done_callback(self._wakeup) result.add_done_callback(
self._wakeup, context=self._context)
self._fut_waiter = result self._fut_waiter = result
if self._must_cancel: if self._must_cancel:
if self._fut_waiter.cancel(): if self._fut_waiter.cancel():
@ -246,21 +251,24 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
new_exc = RuntimeError( new_exc = RuntimeError(
f'yield was used instead of yield from ' f'yield was used instead of yield from '
f'in task {self!r} with {result!r}') f'in task {self!r} with {result!r}')
self._loop.call_soon(self._step, new_exc) self._loop.call_soon(
self._step, new_exc, context=self._context)
elif result is None: elif result is None:
# Bare yield relinquishes control for one event loop iteration. # Bare yield relinquishes control for one event loop iteration.
self._loop.call_soon(self._step) self._loop.call_soon(self._step, context=self._context)
elif inspect.isgenerator(result): elif inspect.isgenerator(result):
# Yielding a generator is just wrong. # Yielding a generator is just wrong.
new_exc = RuntimeError( new_exc = RuntimeError(
f'yield was used instead of yield from for ' f'yield was used instead of yield from for '
f'generator in task {self!r} with {result}') f'generator in task {self!r} with {result}')
self._loop.call_soon(self._step, new_exc) self._loop.call_soon(
self._step, new_exc, context=self._context)
else: else:
# Yielding something else is an error. # Yielding something else is an error.
new_exc = RuntimeError(f'Task got bad yield: {result!r}') new_exc = RuntimeError(f'Task got bad yield: {result!r}')
self._loop.call_soon(self._step, new_exc) self._loop.call_soon(
self._step, new_exc, context=self._context)
finally: finally:
_leave_task(self._loop, self) _leave_task(self._loop, self)
self = None # Needed to break cycles when an exception occurs. self = None # Needed to break cycles when an exception occurs.

View File

@ -92,7 +92,7 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
except (ValueError, OSError) as exc: except (ValueError, OSError) as exc:
raise RuntimeError(str(exc)) raise RuntimeError(str(exc))
handle = events.Handle(callback, args, self) handle = events.Handle(callback, args, self, None)
self._signal_handlers[sig] = handle self._signal_handlers[sig] = handle
try: try:

4
Lib/contextvars.py Normal file
View File

@ -0,0 +1,4 @@
from _contextvars import Context, ContextVar, Token, copy_context
__all__ = ('Context', 'ContextVar', 'Token', 'copy_context')

View File

@ -192,14 +192,14 @@ class BaseEventLoopTests(test_utils.TestCase):
self.assertRaises(RuntimeError, self.loop.run_until_complete, f) self.assertRaises(RuntimeError, self.loop.run_until_complete, f)
def test__add_callback_handle(self): def test__add_callback_handle(self):
h = asyncio.Handle(lambda: False, (), self.loop) h = asyncio.Handle(lambda: False, (), self.loop, None)
self.loop._add_callback(h) self.loop._add_callback(h)
self.assertFalse(self.loop._scheduled) self.assertFalse(self.loop._scheduled)
self.assertIn(h, self.loop._ready) self.assertIn(h, self.loop._ready)
def test__add_callback_cancelled_handle(self): def test__add_callback_cancelled_handle(self):
h = asyncio.Handle(lambda: False, (), self.loop) h = asyncio.Handle(lambda: False, (), self.loop, None)
h.cancel() h.cancel()
self.loop._add_callback(h) self.loop._add_callback(h)
@ -333,9 +333,9 @@ class BaseEventLoopTests(test_utils.TestCase):
def test__run_once(self): def test__run_once(self):
h1 = asyncio.TimerHandle(time.monotonic() + 5.0, lambda: True, (), h1 = asyncio.TimerHandle(time.monotonic() + 5.0, lambda: True, (),
self.loop) self.loop, None)
h2 = asyncio.TimerHandle(time.monotonic() + 10.0, lambda: True, (), h2 = asyncio.TimerHandle(time.monotonic() + 10.0, lambda: True, (),
self.loop) self.loop, None)
h1.cancel() h1.cancel()
@ -390,7 +390,7 @@ class BaseEventLoopTests(test_utils.TestCase):
handle = loop.call_soon(lambda: True) handle = loop.call_soon(lambda: True)
h = asyncio.TimerHandle(time.monotonic() - 1, cb, (self.loop,), h = asyncio.TimerHandle(time.monotonic() - 1, cb, (self.loop,),
self.loop) self.loop, None)
self.loop._process_events = mock.Mock() self.loop._process_events = mock.Mock()
self.loop._scheduled.append(h) self.loop._scheduled.append(h)

View File

@ -565,16 +565,22 @@ class BaseFutureTests:
@unittest.skipUnless(hasattr(futures, '_CFuture'), @unittest.skipUnless(hasattr(futures, '_CFuture'),
'requires the C _asyncio module') 'requires the C _asyncio module')
class CFutureTests(BaseFutureTests, test_utils.TestCase): class CFutureTests(BaseFutureTests, test_utils.TestCase):
cls = futures._CFuture try:
cls = futures._CFuture
except AttributeError:
cls = None
@unittest.skipUnless(hasattr(futures, '_CFuture'), @unittest.skipUnless(hasattr(futures, '_CFuture'),
'requires the C _asyncio module') 'requires the C _asyncio module')
class CSubFutureTests(BaseFutureTests, test_utils.TestCase): class CSubFutureTests(BaseFutureTests, test_utils.TestCase):
class CSubFuture(futures._CFuture): try:
pass class CSubFuture(futures._CFuture):
pass
cls = CSubFuture cls = CSubFuture
except AttributeError:
cls = None
class PyFutureTests(BaseFutureTests, test_utils.TestCase): class PyFutureTests(BaseFutureTests, test_utils.TestCase):

View File

@ -2,10 +2,11 @@
import collections import collections
import contextlib import contextlib
import contextvars
import functools import functools
import gc import gc
import io import io
import os import random
import re import re
import sys import sys
import types import types
@ -1377,9 +1378,9 @@ class BaseTaskTests:
self.cb_added = False self.cb_added = False
super().__init__(*args, **kwds) super().__init__(*args, **kwds)
def add_done_callback(self, fn): def add_done_callback(self, *args, **kwargs):
self.cb_added = True self.cb_added = True
super().add_done_callback(fn) super().add_done_callback(*args, **kwargs)
fut = Fut(loop=self.loop) fut = Fut(loop=self.loop)
result = None result = None
@ -2091,7 +2092,7 @@ class BaseTaskTests:
@mock.patch('asyncio.base_events.logger') @mock.patch('asyncio.base_events.logger')
def test_error_in_call_soon(self, m_log): def test_error_in_call_soon(self, m_log):
def call_soon(callback, *args): def call_soon(callback, *args, **kwargs):
raise ValueError raise ValueError
self.loop.call_soon = call_soon self.loop.call_soon = call_soon
@ -2176,6 +2177,91 @@ class BaseTaskTests:
self.loop.run_until_complete(coro()) self.loop.run_until_complete(coro())
def test_context_1(self):
cvar = contextvars.ContextVar('cvar', default='nope')
async def sub():
await asyncio.sleep(0.01, loop=loop)
self.assertEqual(cvar.get(), 'nope')
cvar.set('something else')
async def main():
self.assertEqual(cvar.get(), 'nope')
subtask = self.new_task(loop, sub())
cvar.set('yes')
self.assertEqual(cvar.get(), 'yes')
await subtask
self.assertEqual(cvar.get(), 'yes')
loop = asyncio.new_event_loop()
try:
task = self.new_task(loop, main())
loop.run_until_complete(task)
finally:
loop.close()
def test_context_2(self):
cvar = contextvars.ContextVar('cvar', default='nope')
async def main():
def fut_on_done(fut):
# This change must not pollute the context
# of the "main()" task.
cvar.set('something else')
self.assertEqual(cvar.get(), 'nope')
for j in range(2):
fut = self.new_future(loop)
fut.add_done_callback(fut_on_done)
cvar.set(f'yes{j}')
loop.call_soon(fut.set_result, None)
await fut
self.assertEqual(cvar.get(), f'yes{j}')
for i in range(3):
# Test that task passed its context to add_done_callback:
cvar.set(f'yes{i}-{j}')
await asyncio.sleep(0.001, loop=loop)
self.assertEqual(cvar.get(), f'yes{i}-{j}')
loop = asyncio.new_event_loop()
try:
task = self.new_task(loop, main())
loop.run_until_complete(task)
finally:
loop.close()
self.assertEqual(cvar.get(), 'nope')
def test_context_3(self):
# Run 100 Tasks in parallel, each modifying cvar.
cvar = contextvars.ContextVar('cvar', default=-1)
async def sub(num):
for i in range(10):
cvar.set(num + i)
await asyncio.sleep(
random.uniform(0.001, 0.05), loop=loop)
self.assertEqual(cvar.get(), num + i)
async def main():
tasks = []
for i in range(100):
task = loop.create_task(sub(random.randint(0, 10)))
tasks.append(task)
await asyncio.gather(*tasks, loop=loop)
loop = asyncio.new_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
self.assertEqual(cvar.get(), -1)
def add_subclass_tests(cls): def add_subclass_tests(cls):
BaseTask = cls.Task BaseTask = cls.Task
@ -2193,9 +2279,9 @@ def add_subclass_tests(cls):
self.calls['_schedule_callbacks'] += 1 self.calls['_schedule_callbacks'] += 1
return super()._schedule_callbacks() return super()._schedule_callbacks()
def add_done_callback(self, *args): def add_done_callback(self, *args, **kwargs):
self.calls['add_done_callback'] += 1 self.calls['add_done_callback'] += 1
return super().add_done_callback(*args) return super().add_done_callback(*args, **kwargs)
class Task(CommonFuture, BaseTask): class Task(CommonFuture, BaseTask):
def _step(self, *args): def _step(self, *args):
@ -2486,10 +2572,13 @@ class PyIntrospectionTests(unittest.TestCase, BaseTaskIntrospectionTests):
@unittest.skipUnless(hasattr(tasks, '_c_register_task'), @unittest.skipUnless(hasattr(tasks, '_c_register_task'),
'requires the C _asyncio module') 'requires the C _asyncio module')
class CIntrospectionTests(unittest.TestCase, BaseTaskIntrospectionTests): class CIntrospectionTests(unittest.TestCase, BaseTaskIntrospectionTests):
_register_task = staticmethod(tasks._c_register_task) if hasattr(tasks, '_c_register_task'):
_unregister_task = staticmethod(tasks._c_unregister_task) _register_task = staticmethod(tasks._c_register_task)
_enter_task = staticmethod(tasks._c_enter_task) _unregister_task = staticmethod(tasks._c_unregister_task)
_leave_task = staticmethod(tasks._c_leave_task) _enter_task = staticmethod(tasks._c_enter_task)
_leave_task = staticmethod(tasks._c_leave_task)
else:
_register_task = _unregister_task = _enter_task = _leave_task = None
class BaseCurrentLoopTests: class BaseCurrentLoopTests:

View File

@ -365,7 +365,7 @@ class TestLoop(base_events.BaseEventLoop):
raise AssertionError("Time generator is not finished") raise AssertionError("Time generator is not finished")
def _add_reader(self, fd, callback, *args): def _add_reader(self, fd, callback, *args):
self.readers[fd] = events.Handle(callback, args, self) self.readers[fd] = events.Handle(callback, args, self, None)
def _remove_reader(self, fd): def _remove_reader(self, fd):
self.remove_reader_count[fd] += 1 self.remove_reader_count[fd] += 1
@ -391,7 +391,7 @@ class TestLoop(base_events.BaseEventLoop):
raise AssertionError(f'fd {fd} is registered') raise AssertionError(f'fd {fd} is registered')
def _add_writer(self, fd, callback, *args): def _add_writer(self, fd, callback, *args):
self.writers[fd] = events.Handle(callback, args, self) self.writers[fd] = events.Handle(callback, args, self, None)
def _remove_writer(self, fd): def _remove_writer(self, fd):
self.remove_writer_count[fd] += 1 self.remove_writer_count[fd] += 1
@ -457,9 +457,9 @@ class TestLoop(base_events.BaseEventLoop):
self.advance_time(advance) self.advance_time(advance)
self._timers = [] self._timers = []
def call_at(self, when, callback, *args): def call_at(self, when, callback, *args, context=None):
self._timers.append(when) self._timers.append(when)
return super().call_at(when, callback, *args) return super().call_at(when, callback, *args, context=context)
def _process_events(self, event_list): def _process_events(self, event_list):
return return

1064
Lib/test/test_context.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -354,6 +354,8 @@ PYTHON_OBJS= \
Python/pylifecycle.o \ Python/pylifecycle.o \
Python/pymath.o \ Python/pymath.o \
Python/pystate.o \ Python/pystate.o \
Python/context.o \
Python/hamt.o \
Python/pythonrun.o \ Python/pythonrun.o \
Python/pytime.o \ Python/pytime.o \
Python/bootstrap_hash.o \ Python/bootstrap_hash.o \
@ -996,6 +998,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/pymem.h \ $(srcdir)/Include/pymem.h \
$(srcdir)/Include/pyport.h \ $(srcdir)/Include/pyport.h \
$(srcdir)/Include/pystate.h \ $(srcdir)/Include/pystate.h \
$(srcdir)/Include/context.h \
$(srcdir)/Include/pystrcmp.h \ $(srcdir)/Include/pystrcmp.h \
$(srcdir)/Include/pystrtod.h \ $(srcdir)/Include/pystrtod.h \
$(srcdir)/Include/pystrhex.h \ $(srcdir)/Include/pystrhex.h \
@ -1023,6 +1026,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/mem.h \ $(srcdir)/Include/internal/mem.h \
$(srcdir)/Include/internal/pygetopt.h \ $(srcdir)/Include/internal/pygetopt.h \
$(srcdir)/Include/internal/pystate.h \ $(srcdir)/Include/internal/pystate.h \
$(srcdir)/Include/internal/context.h \
$(srcdir)/Include/internal/warnings.h \ $(srcdir)/Include/internal/warnings.h \
$(DTRACE_HEADERS) $(DTRACE_HEADERS)

View File

@ -0,0 +1 @@
Implement PEP 567

View File

@ -176,6 +176,7 @@ _symtable symtablemodule.c
#array arraymodule.c # array objects #array arraymodule.c # array objects
#cmath cmathmodule.c _math.c # -lm # complex math library functions #cmath cmathmodule.c _math.c # -lm # complex math library functions
#math mathmodule.c _math.c # -lm # math library functions, e.g. sin() #math mathmodule.c _math.c # -lm # math library functions, e.g. sin()
#_contextvars _contextvarsmodule.c # Context Variables
#_struct _struct.c # binary structure packing/unpacking #_struct _struct.c # binary structure packing/unpacking
#_weakref _weakref.c # basic weak reference support #_weakref _weakref.c # basic weak reference support
#_testcapi _testcapimodule.c # Python C API test module #_testcapi _testcapimodule.c # Python C API test module

View File

@ -36,6 +36,7 @@ static PyObject *asyncio_task_print_stack_func;
static PyObject *asyncio_task_repr_info_func; static PyObject *asyncio_task_repr_info_func;
static PyObject *asyncio_InvalidStateError; static PyObject *asyncio_InvalidStateError;
static PyObject *asyncio_CancelledError; static PyObject *asyncio_CancelledError;
static PyObject *context_kwname;
/* WeakSet containing all alive tasks. */ /* WeakSet containing all alive tasks. */
@ -59,6 +60,7 @@ typedef enum {
PyObject_HEAD \ PyObject_HEAD \
PyObject *prefix##_loop; \ PyObject *prefix##_loop; \
PyObject *prefix##_callback0; \ PyObject *prefix##_callback0; \
PyContext *prefix##_context0; \
PyObject *prefix##_callbacks; \ PyObject *prefix##_callbacks; \
PyObject *prefix##_exception; \ PyObject *prefix##_exception; \
PyObject *prefix##_result; \ PyObject *prefix##_result; \
@ -77,6 +79,7 @@ typedef struct {
FutureObj_HEAD(task) FutureObj_HEAD(task)
PyObject *task_fut_waiter; PyObject *task_fut_waiter;
PyObject *task_coro; PyObject *task_coro;
PyContext *task_context;
int task_must_cancel; int task_must_cancel;
int task_log_destroy_pending; int task_log_destroy_pending;
} TaskObj; } TaskObj;
@ -336,11 +339,38 @@ get_event_loop(void)
static int static int
call_soon(PyObject *loop, PyObject *func, PyObject *arg) call_soon(PyObject *loop, PyObject *func, PyObject *arg, PyContext *ctx)
{ {
PyObject *handle; PyObject *handle;
handle = _PyObject_CallMethodIdObjArgs( PyObject *stack[3];
loop, &PyId_call_soon, func, arg, NULL); Py_ssize_t nargs;
if (ctx == NULL) {
handle = _PyObject_CallMethodIdObjArgs(
loop, &PyId_call_soon, func, arg, NULL);
}
else {
/* Use FASTCALL to pass a keyword-only argument to call_soon */
PyObject *callable = _PyObject_GetAttrId(loop, &PyId_call_soon);
if (callable == NULL) {
return -1;
}
/* All refs in 'stack' are borrowed. */
nargs = 1;
stack[0] = func;
if (arg != NULL) {
stack[1] = arg;
nargs++;
}
stack[nargs] = (PyObject *)ctx;
handle = _PyObject_FastCallKeywords(
callable, stack, nargs, context_kwname);
Py_DECREF(callable);
}
if (handle == NULL) { if (handle == NULL) {
return -1; return -1;
} }
@ -387,8 +417,11 @@ future_schedule_callbacks(FutureObj *fut)
/* There's a 1st callback */ /* There's a 1st callback */
int ret = call_soon( int ret = call_soon(
fut->fut_loop, fut->fut_callback0, (PyObject *)fut); fut->fut_loop, fut->fut_callback0,
(PyObject *)fut, fut->fut_context0);
Py_CLEAR(fut->fut_callback0); Py_CLEAR(fut->fut_callback0);
Py_CLEAR(fut->fut_context0);
if (ret) { if (ret) {
/* If an error occurs in pure-Python implementation, /* If an error occurs in pure-Python implementation,
all callbacks are cleared. */ all callbacks are cleared. */
@ -413,9 +446,11 @@ future_schedule_callbacks(FutureObj *fut)
} }
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
PyObject *cb = PyList_GET_ITEM(fut->fut_callbacks, i); PyObject *cb_tup = PyList_GET_ITEM(fut->fut_callbacks, i);
PyObject *cb = PyTuple_GET_ITEM(cb_tup, 0);
PyObject *ctx = PyTuple_GET_ITEM(cb_tup, 1);
if (call_soon(fut->fut_loop, cb, (PyObject *)fut)) { if (call_soon(fut->fut_loop, cb, (PyObject *)fut, (PyContext *)ctx)) {
/* If an error occurs in pure-Python implementation, /* If an error occurs in pure-Python implementation,
all callbacks are cleared. */ all callbacks are cleared. */
Py_CLEAR(fut->fut_callbacks); Py_CLEAR(fut->fut_callbacks);
@ -462,6 +497,7 @@ future_init(FutureObj *fut, PyObject *loop)
} }
fut->fut_callback0 = NULL; fut->fut_callback0 = NULL;
fut->fut_context0 = NULL;
fut->fut_callbacks = NULL; fut->fut_callbacks = NULL;
return 0; return 0;
@ -566,7 +602,7 @@ future_get_result(FutureObj *fut, PyObject **result)
} }
static PyObject * static PyObject *
future_add_done_callback(FutureObj *fut, PyObject *arg) future_add_done_callback(FutureObj *fut, PyObject *arg, PyContext *ctx)
{ {
if (!future_is_alive(fut)) { if (!future_is_alive(fut)) {
PyErr_SetString(PyExc_RuntimeError, "uninitialized Future object"); PyErr_SetString(PyExc_RuntimeError, "uninitialized Future object");
@ -576,7 +612,7 @@ future_add_done_callback(FutureObj *fut, PyObject *arg)
if (fut->fut_state != STATE_PENDING) { if (fut->fut_state != STATE_PENDING) {
/* The future is done/cancelled, so schedule the callback /* The future is done/cancelled, so schedule the callback
right away. */ right away. */
if (call_soon(fut->fut_loop, arg, (PyObject*) fut)) { if (call_soon(fut->fut_loop, arg, (PyObject*) fut, ctx)) {
return NULL; return NULL;
} }
} }
@ -602,24 +638,38 @@ future_add_done_callback(FutureObj *fut, PyObject *arg)
with a new list and add the new callback to it. with a new list and add the new callback to it.
*/ */
if (fut->fut_callbacks != NULL) { if (fut->fut_callbacks == NULL && fut->fut_callback0 == NULL) {
int err = PyList_Append(fut->fut_callbacks, arg);
if (err != 0) {
return NULL;
}
}
else if (fut->fut_callback0 == NULL) {
Py_INCREF(arg); Py_INCREF(arg);
fut->fut_callback0 = arg; fut->fut_callback0 = arg;
Py_INCREF(ctx);
fut->fut_context0 = ctx;
} }
else { else {
fut->fut_callbacks = PyList_New(1); PyObject *tup = PyTuple_New(2);
if (fut->fut_callbacks == NULL) { if (tup == NULL) {
return NULL; return NULL;
} }
Py_INCREF(arg); Py_INCREF(arg);
PyList_SET_ITEM(fut->fut_callbacks, 0, arg); PyTuple_SET_ITEM(tup, 0, arg);
Py_INCREF(ctx);
PyTuple_SET_ITEM(tup, 1, (PyObject *)ctx);
if (fut->fut_callbacks != NULL) {
int err = PyList_Append(fut->fut_callbacks, tup);
if (err) {
Py_DECREF(tup);
return NULL;
}
Py_DECREF(tup);
}
else {
fut->fut_callbacks = PyList_New(1);
if (fut->fut_callbacks == NULL) {
return NULL;
}
PyList_SET_ITEM(fut->fut_callbacks, 0, tup); /* borrow */
}
} }
} }
@ -676,6 +726,7 @@ FutureObj_clear(FutureObj *fut)
{ {
Py_CLEAR(fut->fut_loop); Py_CLEAR(fut->fut_loop);
Py_CLEAR(fut->fut_callback0); Py_CLEAR(fut->fut_callback0);
Py_CLEAR(fut->fut_context0);
Py_CLEAR(fut->fut_callbacks); Py_CLEAR(fut->fut_callbacks);
Py_CLEAR(fut->fut_result); Py_CLEAR(fut->fut_result);
Py_CLEAR(fut->fut_exception); Py_CLEAR(fut->fut_exception);
@ -689,6 +740,7 @@ FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg)
{ {
Py_VISIT(fut->fut_loop); Py_VISIT(fut->fut_loop);
Py_VISIT(fut->fut_callback0); Py_VISIT(fut->fut_callback0);
Py_VISIT(fut->fut_context0);
Py_VISIT(fut->fut_callbacks); Py_VISIT(fut->fut_callbacks);
Py_VISIT(fut->fut_result); Py_VISIT(fut->fut_result);
Py_VISIT(fut->fut_exception); Py_VISIT(fut->fut_exception);
@ -821,6 +873,8 @@ _asyncio.Future.add_done_callback
fn: object fn: object
/ /
*
context: object = NULL
Add a callback to be run when the future becomes done. Add a callback to be run when the future becomes done.
@ -830,10 +884,21 @@ scheduled with call_soon.
[clinic start generated code]*/ [clinic start generated code]*/
static PyObject * static PyObject *
_asyncio_Future_add_done_callback(FutureObj *self, PyObject *fn) _asyncio_Future_add_done_callback_impl(FutureObj *self, PyObject *fn,
/*[clinic end generated code: output=819e09629b2ec2b5 input=8f818b39990b027d]*/ PyObject *context)
/*[clinic end generated code: output=7ce635bbc9554c1e input=15ab0693a96e9533]*/
{ {
return future_add_done_callback(self, fn); if (context == NULL) {
context = (PyObject *)PyContext_CopyCurrent();
if (context == NULL) {
return NULL;
}
PyObject *res = future_add_done_callback(
self, fn, (PyContext *)context);
Py_DECREF(context);
return res;
}
return future_add_done_callback(self, fn, (PyContext *)context);
} }
/*[clinic input] /*[clinic input]
@ -865,6 +930,7 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
if (cmp == 1) { if (cmp == 1) {
/* callback0 == fn */ /* callback0 == fn */
Py_CLEAR(self->fut_callback0); Py_CLEAR(self->fut_callback0);
Py_CLEAR(self->fut_context0);
cleared_callback0 = 1; cleared_callback0 = 1;
} }
} }
@ -880,8 +946,9 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
} }
if (len == 1) { if (len == 1) {
PyObject *cb_tup = PyList_GET_ITEM(self->fut_callbacks, 0);
int cmp = PyObject_RichCompareBool( int cmp = PyObject_RichCompareBool(
fn, PyList_GET_ITEM(self->fut_callbacks, 0), Py_EQ); fn, PyTuple_GET_ITEM(cb_tup, 0), Py_EQ);
if (cmp == -1) { if (cmp == -1) {
return NULL; return NULL;
} }
@ -903,7 +970,7 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
int ret; int ret;
PyObject *item = PyList_GET_ITEM(self->fut_callbacks, i); PyObject *item = PyList_GET_ITEM(self->fut_callbacks, i);
Py_INCREF(item); Py_INCREF(item);
ret = PyObject_RichCompareBool(fn, item, Py_EQ); ret = PyObject_RichCompareBool(fn, PyTuple_GET_ITEM(item, 0), Py_EQ);
if (ret == 0) { if (ret == 0) {
if (j < len) { if (j < len) {
PyList_SET_ITEM(newlist, j, item); PyList_SET_ITEM(newlist, j, item);
@ -1081,47 +1148,49 @@ static PyObject *
FutureObj_get_callbacks(FutureObj *fut) FutureObj_get_callbacks(FutureObj *fut)
{ {
Py_ssize_t i; Py_ssize_t i;
Py_ssize_t len;
PyObject *new_list;
ENSURE_FUTURE_ALIVE(fut) ENSURE_FUTURE_ALIVE(fut)
if (fut->fut_callbacks == NULL) { if (fut->fut_callback0 == NULL) {
if (fut->fut_callback0 == NULL) { if (fut->fut_callbacks == NULL) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
else {
new_list = PyList_New(1);
if (new_list == NULL) {
return NULL;
}
Py_INCREF(fut->fut_callback0);
PyList_SET_ITEM(new_list, 0, fut->fut_callback0);
return new_list;
}
}
assert(fut->fut_callbacks != NULL);
if (fut->fut_callback0 == NULL) {
Py_INCREF(fut->fut_callbacks); Py_INCREF(fut->fut_callbacks);
return fut->fut_callbacks; return fut->fut_callbacks;
} }
assert(fut->fut_callback0 != NULL); Py_ssize_t len = 1;
if (fut->fut_callbacks != NULL) {
len += PyList_GET_SIZE(fut->fut_callbacks);
}
len = PyList_GET_SIZE(fut->fut_callbacks);
new_list = PyList_New(len + 1); PyObject *new_list = PyList_New(len);
if (new_list == NULL) { if (new_list == NULL) {
return NULL; return NULL;
} }
PyObject *tup0 = PyTuple_New(2);
if (tup0 == NULL) {
Py_DECREF(new_list);
return NULL;
}
Py_INCREF(fut->fut_callback0); Py_INCREF(fut->fut_callback0);
PyList_SET_ITEM(new_list, 0, fut->fut_callback0); PyTuple_SET_ITEM(tup0, 0, fut->fut_callback0);
for (i = 0; i < len; i++) { assert(fut->fut_context0 != NULL);
PyObject *cb = PyList_GET_ITEM(fut->fut_callbacks, i); Py_INCREF(fut->fut_context0);
Py_INCREF(cb); PyTuple_SET_ITEM(tup0, 1, (PyObject *)fut->fut_context0);
PyList_SET_ITEM(new_list, i + 1, cb);
PyList_SET_ITEM(new_list, 0, tup0);
if (fut->fut_callbacks != NULL) {
for (i = 0; i < PyList_GET_SIZE(fut->fut_callbacks); i++) {
PyObject *cb = PyList_GET_ITEM(fut->fut_callbacks, i);
Py_INCREF(cb);
PyList_SET_ITEM(new_list, i + 1, cb);
}
} }
return new_list; return new_list;
@ -1912,6 +1981,11 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop)
return -1; return -1;
} }
self->task_context = PyContext_CopyCurrent();
if (self->task_context == NULL) {
return -1;
}
self->task_fut_waiter = NULL; self->task_fut_waiter = NULL;
self->task_must_cancel = 0; self->task_must_cancel = 0;
self->task_log_destroy_pending = 1; self->task_log_destroy_pending = 1;
@ -1928,6 +2002,7 @@ static int
TaskObj_clear(TaskObj *task) TaskObj_clear(TaskObj *task)
{ {
(void)FutureObj_clear((FutureObj*) task); (void)FutureObj_clear((FutureObj*) task);
Py_CLEAR(task->task_context);
Py_CLEAR(task->task_coro); Py_CLEAR(task->task_coro);
Py_CLEAR(task->task_fut_waiter); Py_CLEAR(task->task_fut_waiter);
return 0; return 0;
@ -1936,6 +2011,7 @@ TaskObj_clear(TaskObj *task)
static int static int
TaskObj_traverse(TaskObj *task, visitproc visit, void *arg) TaskObj_traverse(TaskObj *task, visitproc visit, void *arg)
{ {
Py_VISIT(task->task_context);
Py_VISIT(task->task_coro); Py_VISIT(task->task_coro);
Py_VISIT(task->task_fut_waiter); Py_VISIT(task->task_fut_waiter);
(void)FutureObj_traverse((FutureObj*) task, visit, arg); (void)FutureObj_traverse((FutureObj*) task, visit, arg);
@ -2451,7 +2527,7 @@ task_call_step_soon(TaskObj *task, PyObject *arg)
return -1; return -1;
} }
int ret = call_soon(task->task_loop, cb, NULL); int ret = call_soon(task->task_loop, cb, NULL, task->task_context);
Py_DECREF(cb); Py_DECREF(cb);
return ret; return ret;
} }
@ -2650,7 +2726,8 @@ set_exception:
if (wrapper == NULL) { if (wrapper == NULL) {
goto fail; goto fail;
} }
res = future_add_done_callback((FutureObj*)result, wrapper); res = future_add_done_callback(
(FutureObj*)result, wrapper, task->task_context);
Py_DECREF(wrapper); Py_DECREF(wrapper);
if (res == NULL) { if (res == NULL) {
goto fail; goto fail;
@ -2724,14 +2801,23 @@ set_exception:
goto fail; goto fail;
} }
/* result.add_done_callback(task._wakeup) */
wrapper = TaskWakeupMethWrapper_new(task); wrapper = TaskWakeupMethWrapper_new(task);
if (wrapper == NULL) { if (wrapper == NULL) {
goto fail; goto fail;
} }
res = _PyObject_CallMethodIdObjArgs(result,
&PyId_add_done_callback, /* result.add_done_callback(task._wakeup) */
wrapper, NULL); PyObject *add_cb = _PyObject_GetAttrId(
result, &PyId_add_done_callback);
if (add_cb == NULL) {
goto fail;
}
PyObject *stack[2];
stack[0] = wrapper;
stack[1] = (PyObject *)task->task_context;
res = _PyObject_FastCallKeywords(
add_cb, stack, 1, context_kwname);
Py_DECREF(add_cb);
Py_DECREF(wrapper); Py_DECREF(wrapper);
if (res == NULL) { if (res == NULL) {
goto fail; goto fail;
@ -3141,6 +3227,8 @@ module_free(void *m)
Py_CLEAR(current_tasks); Py_CLEAR(current_tasks);
Py_CLEAR(iscoroutine_typecache); Py_CLEAR(iscoroutine_typecache);
Py_CLEAR(context_kwname);
module_free_freelists(); module_free_freelists();
} }
@ -3164,6 +3252,17 @@ module_init(void)
goto fail; goto fail;
} }
context_kwname = PyTuple_New(1);
if (context_kwname == NULL) {
goto fail;
}
PyObject *context_str = PyUnicode_FromString("context");
if (context_str == NULL) {
goto fail;
}
PyTuple_SET_ITEM(context_kwname, 0, context_str);
#define WITH_MOD(NAME) \ #define WITH_MOD(NAME) \
Py_CLEAR(module); \ Py_CLEAR(module); \
module = PyImport_ImportModule(NAME); \ module = PyImport_ImportModule(NAME); \

View File

@ -0,0 +1,75 @@
#include "Python.h"
#include "clinic/_contextvarsmodule.c.h"
/*[clinic input]
module _contextvars
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a0955718c8b8cea6]*/
/*[clinic input]
_contextvars.copy_context
[clinic start generated code]*/
static PyObject *
_contextvars_copy_context_impl(PyObject *module)
/*[clinic end generated code: output=1fcd5da7225c4fa9 input=89bb9ae485888440]*/
{
return (PyObject *)PyContext_CopyCurrent();
}
PyDoc_STRVAR(module_doc, "Context Variables");
static PyMethodDef _contextvars_methods[] = {
_CONTEXTVARS_COPY_CONTEXT_METHODDEF
{NULL, NULL}
};
static struct PyModuleDef _contextvarsmodule = {
PyModuleDef_HEAD_INIT, /* m_base */
"_contextvars", /* m_name */
module_doc, /* m_doc */
-1, /* m_size */
_contextvars_methods, /* m_methods */
NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
PyMODINIT_FUNC
PyInit__contextvars(void)
{
PyObject *m = PyModule_Create(&_contextvarsmodule);
if (m == NULL) {
return NULL;
}
Py_INCREF(&PyContext_Type);
if (PyModule_AddObject(m, "Context",
(PyObject *)&PyContext_Type) < 0)
{
Py_DECREF(&PyContext_Type);
return NULL;
}
Py_INCREF(&PyContextVar_Type);
if (PyModule_AddObject(m, "ContextVar",
(PyObject *)&PyContextVar_Type) < 0)
{
Py_DECREF(&PyContextVar_Type);
return NULL;
}
Py_INCREF(&PyContextToken_Type);
if (PyModule_AddObject(m, "Token",
(PyObject *)&PyContextToken_Type) < 0)
{
Py_DECREF(&PyContextToken_Type);
return NULL;
}
return m;
}

View File

@ -4438,6 +4438,13 @@ test_pythread_tss_key_state(PyObject *self, PyObject *args)
} }
static PyObject*
new_hamt(PyObject *self, PyObject *args)
{
return _PyContext_NewHamtForTests();
}
static PyMethodDef TestMethods[] = { static PyMethodDef TestMethods[] = {
{"raise_exception", raise_exception, METH_VARARGS}, {"raise_exception", raise_exception, METH_VARARGS},
{"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS}, {"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS},
@ -4655,6 +4662,7 @@ static PyMethodDef TestMethods[] = {
{"get_mapping_values", get_mapping_values, METH_O}, {"get_mapping_values", get_mapping_values, METH_O},
{"get_mapping_items", get_mapping_items, METH_O}, {"get_mapping_items", get_mapping_items, METH_O},
{"test_pythread_tss_key_state", test_pythread_tss_key_state, METH_VARARGS}, {"test_pythread_tss_key_state", test_pythread_tss_key_state, METH_VARARGS},
{"hamt", new_hamt, METH_NOARGS},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };

View File

@ -110,7 +110,7 @@ PyDoc_STRVAR(_asyncio_Future_set_exception__doc__,
{"set_exception", (PyCFunction)_asyncio_Future_set_exception, METH_O, _asyncio_Future_set_exception__doc__}, {"set_exception", (PyCFunction)_asyncio_Future_set_exception, METH_O, _asyncio_Future_set_exception__doc__},
PyDoc_STRVAR(_asyncio_Future_add_done_callback__doc__, PyDoc_STRVAR(_asyncio_Future_add_done_callback__doc__,
"add_done_callback($self, fn, /)\n" "add_done_callback($self, fn, /, *, context=None)\n"
"--\n" "--\n"
"\n" "\n"
"Add a callback to be run when the future becomes done.\n" "Add a callback to be run when the future becomes done.\n"
@ -120,7 +120,30 @@ PyDoc_STRVAR(_asyncio_Future_add_done_callback__doc__,
"scheduled with call_soon."); "scheduled with call_soon.");
#define _ASYNCIO_FUTURE_ADD_DONE_CALLBACK_METHODDEF \ #define _ASYNCIO_FUTURE_ADD_DONE_CALLBACK_METHODDEF \
{"add_done_callback", (PyCFunction)_asyncio_Future_add_done_callback, METH_O, _asyncio_Future_add_done_callback__doc__}, {"add_done_callback", (PyCFunction)_asyncio_Future_add_done_callback, METH_FASTCALL|METH_KEYWORDS, _asyncio_Future_add_done_callback__doc__},
static PyObject *
_asyncio_Future_add_done_callback_impl(FutureObj *self, PyObject *fn,
PyObject *context);
static PyObject *
_asyncio_Future_add_done_callback(FutureObj *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
static const char * const _keywords[] = {"", "context", NULL};
static _PyArg_Parser _parser = {"O|$O:add_done_callback", _keywords, 0};
PyObject *fn;
PyObject *context = NULL;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&fn, &context)) {
goto exit;
}
return_value = _asyncio_Future_add_done_callback_impl(self, fn, context);
exit:
return return_value;
}
PyDoc_STRVAR(_asyncio_Future_remove_done_callback__doc__, PyDoc_STRVAR(_asyncio_Future_remove_done_callback__doc__,
"remove_done_callback($self, fn, /)\n" "remove_done_callback($self, fn, /)\n"
@ -763,4 +786,4 @@ _asyncio__leave_task(PyObject *module, PyObject *const *args, Py_ssize_t nargs,
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=616e814431893dcc input=a9049054013a1b77]*/ /*[clinic end generated code: output=bcbaf1b2480f4aa9 input=a9049054013a1b77]*/

View File

@ -0,0 +1,21 @@
/*[clinic input]
preserve
[clinic start generated code]*/
PyDoc_STRVAR(_contextvars_copy_context__doc__,
"copy_context($module, /)\n"
"--\n"
"\n");
#define _CONTEXTVARS_COPY_CONTEXT_METHODDEF \
{"copy_context", (PyCFunction)_contextvars_copy_context, METH_NOARGS, _contextvars_copy_context__doc__},
static PyObject *
_contextvars_copy_context_impl(PyObject *module);
static PyObject *
_contextvars_copy_context(PyObject *module, PyObject *Py_UNUSED(ignored))
{
return _contextvars_copy_context_impl(module);
}
/*[clinic end generated code: output=26e07024451baf52 input=a9049054013a1b77]*/

View File

@ -24,6 +24,7 @@
*/ */
#include "Python.h" #include "Python.h"
#include "internal/context.h"
#include "internal/mem.h" #include "internal/mem.h"
#include "internal/pystate.h" #include "internal/pystate.h"
#include "frameobject.h" /* for PyFrame_ClearFreeList */ #include "frameobject.h" /* for PyFrame_ClearFreeList */
@ -790,6 +791,7 @@ clear_freelists(void)
(void)PyDict_ClearFreeList(); (void)PyDict_ClearFreeList();
(void)PySet_ClearFreeList(); (void)PySet_ClearFreeList();
(void)PyAsyncGen_ClearFreeLists(); (void)PyAsyncGen_ClearFreeLists();
(void)PyContext_ClearFreeList();
} }
/* This is the main function. Read this to understand how the /* This is the main function. Read this to understand how the

View File

@ -3,6 +3,7 @@
#include "Python.h" #include "Python.h"
#include "internal/pystate.h" #include "internal/pystate.h"
#include "internal/context.h"
#include "frameobject.h" #include "frameobject.h"
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGInstrument|Win32">
<Configuration>PGInstrument</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGInstrument|x64">
<Configuration>PGInstrument</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGUpdate|Win32">
<Configuration>PGUpdate</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="PGUpdate|x64">
<Configuration>PGUpdate</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{B8BF1D81-09DC-42D4-B406-4F868B33A89E}</ProjectGuid>
<RootNamespace>_contextvars</RootNamespace>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="python.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<PropertyGroup>
<TargetExt>.pyd</TargetExt>
</PropertyGroup>
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="pyproject.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\Modules\_contextvarsmodule.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="pythoncore.vcxproj">
<Project>{cf7ac3d1-e2df-41d2-bea6-1e2556cdea26}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc" />
</ItemGroup>
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{7CBD8910-233D-4E9A-9164-9BA66C1F0E6D}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\Modules\_contextvarsmodule.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -121,4 +121,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>
</Project> </Project>

View File

@ -49,7 +49,7 @@
<!-- pyshellext.dll --> <!-- pyshellext.dll -->
<Projects Include="pyshellext.vcxproj" /> <Projects Include="pyshellext.vcxproj" />
<!-- Extension modules --> <!-- Extension modules -->
<ExtensionModules Include="_asyncio;_ctypes;_decimal;_distutils_findvs;_elementtree;_msi;_multiprocessing;_overlapped;pyexpat;_queue;select;unicodedata;winsound" /> <ExtensionModules Include="_asyncio;_contextvars;_ctypes;_decimal;_distutils_findvs;_elementtree;_msi;_multiprocessing;_overlapped;pyexpat;_queue;select;unicodedata;winsound" />
<!-- Extension modules that require external sources --> <!-- Extension modules that require external sources -->
<ExternalModules Include="_bz2;_lzma;_sqlite3" /> <ExternalModules Include="_bz2;_lzma;_sqlite3" />
<!-- _ssl will build _socket as well, which may cause conflicts in parallel builds --> <!-- _ssl will build _socket as well, which may cause conflicts in parallel builds -->

View File

@ -94,6 +94,7 @@
<ClInclude Include="..\Include\codecs.h" /> <ClInclude Include="..\Include\codecs.h" />
<ClInclude Include="..\Include\compile.h" /> <ClInclude Include="..\Include\compile.h" />
<ClInclude Include="..\Include\complexobject.h" /> <ClInclude Include="..\Include\complexobject.h" />
<ClInclude Include="..\Include\context.h" />
<ClInclude Include="..\Include\datetime.h" /> <ClInclude Include="..\Include\datetime.h" />
<ClInclude Include="..\Include\descrobject.h" /> <ClInclude Include="..\Include\descrobject.h" />
<ClInclude Include="..\Include\dictobject.h" /> <ClInclude Include="..\Include\dictobject.h" />
@ -112,7 +113,9 @@
<ClInclude Include="..\Include\import.h" /> <ClInclude Include="..\Include\import.h" />
<ClInclude Include="..\Include\internal\ceval.h" /> <ClInclude Include="..\Include\internal\ceval.h" />
<ClInclude Include="..\Include\internal\condvar.h" /> <ClInclude Include="..\Include\internal\condvar.h" />
<ClInclude Include="..\Include\internal\context.h" />
<ClInclude Include="..\Include\internal\gil.h" /> <ClInclude Include="..\Include\internal\gil.h" />
<ClInclude Include="..\Include\internal\hamt.h" />
<ClInclude Include="..\Include\internal\mem.h" /> <ClInclude Include="..\Include\internal\mem.h" />
<ClInclude Include="..\Include\internal\pystate.h" /> <ClInclude Include="..\Include\internal\pystate.h" />
<ClInclude Include="..\Include\internal\warnings.h" /> <ClInclude Include="..\Include\internal\warnings.h" />
@ -232,6 +235,7 @@
<ClCompile Include="..\Modules\_blake2\blake2s_impl.c" /> <ClCompile Include="..\Modules\_blake2\blake2s_impl.c" />
<ClCompile Include="..\Modules\_codecsmodule.c" /> <ClCompile Include="..\Modules\_codecsmodule.c" />
<ClCompile Include="..\Modules\_collectionsmodule.c" /> <ClCompile Include="..\Modules\_collectionsmodule.c" />
<ClCompile Include="..\Modules\_contextvarsmodule.c" />
<ClCompile Include="..\Modules\_csv.c" /> <ClCompile Include="..\Modules\_csv.c" />
<ClCompile Include="..\Modules\_functoolsmodule.c" /> <ClCompile Include="..\Modules\_functoolsmodule.c" />
<ClCompile Include="..\Modules\_heapqmodule.c" /> <ClCompile Include="..\Modules\_heapqmodule.c" />
@ -359,6 +363,7 @@
<ClCompile Include="..\Python\ceval.c" /> <ClCompile Include="..\Python\ceval.c" />
<ClCompile Include="..\Python\codecs.c" /> <ClCompile Include="..\Python\codecs.c" />
<ClCompile Include="..\Python\compile.c" /> <ClCompile Include="..\Python\compile.c" />
<ClCompile Include="..\Python\context.c" />
<ClCompile Include="..\Python\dynamic_annotations.c" /> <ClCompile Include="..\Python\dynamic_annotations.c" />
<ClCompile Include="..\Python\dynload_win.c" /> <ClCompile Include="..\Python\dynload_win.c" />
<ClCompile Include="..\Python\errors.c" /> <ClCompile Include="..\Python\errors.c" />
@ -373,6 +378,7 @@
<ClCompile Include="..\Python\getplatform.c" /> <ClCompile Include="..\Python\getplatform.c" />
<ClCompile Include="..\Python\getversion.c" /> <ClCompile Include="..\Python\getversion.c" />
<ClCompile Include="..\Python\graminit.c" /> <ClCompile Include="..\Python\graminit.c" />
<ClCompile Include="..\Python\hamt.c" />
<ClCompile Include="..\Python\import.c" /> <ClCompile Include="..\Python\import.c" />
<ClCompile Include="..\Python\importdl.c" /> <ClCompile Include="..\Python\importdl.c" />
<ClCompile Include="..\Python\marshal.c" /> <ClCompile Include="..\Python\marshal.c" />

View File

@ -81,6 +81,9 @@
<ClInclude Include="..\Include\complexobject.h"> <ClInclude Include="..\Include\complexobject.h">
<Filter>Include</Filter> <Filter>Include</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\Include\context.h">
<Filter>Include</Filter>
</ClInclude>
<ClInclude Include="..\Include\datetime.h"> <ClInclude Include="..\Include\datetime.h">
<Filter>Include</Filter> <Filter>Include</Filter>
</ClInclude> </ClInclude>
@ -135,9 +138,15 @@
<ClInclude Include="..\Include\internal\condvar.h"> <ClInclude Include="..\Include\internal\condvar.h">
<Filter>Include</Filter> <Filter>Include</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\Include\internal\context.h">
<Filter>Include</Filter>
</ClInclude>
<ClInclude Include="..\Include\internal\gil.h"> <ClInclude Include="..\Include\internal\gil.h">
<Filter>Include</Filter> <Filter>Include</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\Include\internal\hamt.h">
<Filter>Include</Filter>
</ClInclude>
<ClInclude Include="..\Include\internal\mem.h"> <ClInclude Include="..\Include\internal\mem.h">
<Filter>Include</Filter> <Filter>Include</Filter>
</ClInclude> </ClInclude>
@ -842,6 +851,9 @@
<ClCompile Include="..\Python\compile.c"> <ClCompile Include="..\Python\compile.c">
<Filter>Python</Filter> <Filter>Python</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\Python\context.h">
<Filter>Python</Filter>
</ClCompile>
<ClCompile Include="..\Python\dynamic_annotations.c"> <ClCompile Include="..\Python\dynamic_annotations.c">
<Filter>Python</Filter> <Filter>Python</Filter>
</ClCompile> </ClCompile>
@ -884,6 +896,9 @@
<ClCompile Include="..\Python\graminit.c"> <ClCompile Include="..\Python\graminit.c">
<Filter>Python</Filter> <Filter>Python</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\Python\hamt.h">
<Filter>Python</Filter>
</ClCompile>
<ClCompile Include="..\Python\import.c"> <ClCompile Include="..\Python\import.c">
<Filter>Python</Filter> <Filter>Python</Filter>
</ClCompile> </ClCompile>
@ -998,6 +1013,9 @@
<ClCompile Include="..\Modules\_asynciomodule.c"> <ClCompile Include="..\Modules\_asynciomodule.c">
<Filter>Modules</Filter> <Filter>Modules</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\Modules\_contextvarsmodule.c">
<Filter>Modules</Filter>
</ClCompile>
<ClCompile Include="$(zlibDir)\adler32.c"> <ClCompile Include="$(zlibDir)\adler32.c">
<Filter>Modules\zlib</Filter> <Filter>Modules\zlib</Filter>
</ClCompile> </ClCompile>

146
Python/clinic/context.c.h Normal file
View File

@ -0,0 +1,146 @@
/*[clinic input]
preserve
[clinic start generated code]*/
PyDoc_STRVAR(_contextvars_Context_get__doc__,
"get($self, key, default=None, /)\n"
"--\n"
"\n");
#define _CONTEXTVARS_CONTEXT_GET_METHODDEF \
{"get", (PyCFunction)_contextvars_Context_get, METH_FASTCALL, _contextvars_Context_get__doc__},
static PyObject *
_contextvars_Context_get_impl(PyContext *self, PyObject *key,
PyObject *default_value);
static PyObject *
_contextvars_Context_get(PyContext *self, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
PyObject *key;
PyObject *default_value = Py_None;
if (!_PyArg_UnpackStack(args, nargs, "get",
1, 2,
&key, &default_value)) {
goto exit;
}
return_value = _contextvars_Context_get_impl(self, key, default_value);
exit:
return return_value;
}
PyDoc_STRVAR(_contextvars_Context_items__doc__,
"items($self, /)\n"
"--\n"
"\n");
#define _CONTEXTVARS_CONTEXT_ITEMS_METHODDEF \
{"items", (PyCFunction)_contextvars_Context_items, METH_NOARGS, _contextvars_Context_items__doc__},
static PyObject *
_contextvars_Context_items_impl(PyContext *self);
static PyObject *
_contextvars_Context_items(PyContext *self, PyObject *Py_UNUSED(ignored))
{
return _contextvars_Context_items_impl(self);
}
PyDoc_STRVAR(_contextvars_Context_keys__doc__,
"keys($self, /)\n"
"--\n"
"\n");
#define _CONTEXTVARS_CONTEXT_KEYS_METHODDEF \
{"keys", (PyCFunction)_contextvars_Context_keys, METH_NOARGS, _contextvars_Context_keys__doc__},
static PyObject *
_contextvars_Context_keys_impl(PyContext *self);
static PyObject *
_contextvars_Context_keys(PyContext *self, PyObject *Py_UNUSED(ignored))
{
return _contextvars_Context_keys_impl(self);
}
PyDoc_STRVAR(_contextvars_Context_values__doc__,
"values($self, /)\n"
"--\n"
"\n");
#define _CONTEXTVARS_CONTEXT_VALUES_METHODDEF \
{"values", (PyCFunction)_contextvars_Context_values, METH_NOARGS, _contextvars_Context_values__doc__},
static PyObject *
_contextvars_Context_values_impl(PyContext *self);
static PyObject *
_contextvars_Context_values(PyContext *self, PyObject *Py_UNUSED(ignored))
{
return _contextvars_Context_values_impl(self);
}
PyDoc_STRVAR(_contextvars_Context_copy__doc__,
"copy($self, /)\n"
"--\n"
"\n");
#define _CONTEXTVARS_CONTEXT_COPY_METHODDEF \
{"copy", (PyCFunction)_contextvars_Context_copy, METH_NOARGS, _contextvars_Context_copy__doc__},
static PyObject *
_contextvars_Context_copy_impl(PyContext *self);
static PyObject *
_contextvars_Context_copy(PyContext *self, PyObject *Py_UNUSED(ignored))
{
return _contextvars_Context_copy_impl(self);
}
PyDoc_STRVAR(_contextvars_ContextVar_get__doc__,
"get($self, default=None, /)\n"
"--\n"
"\n");
#define _CONTEXTVARS_CONTEXTVAR_GET_METHODDEF \
{"get", (PyCFunction)_contextvars_ContextVar_get, METH_FASTCALL, _contextvars_ContextVar_get__doc__},
static PyObject *
_contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value);
static PyObject *
_contextvars_ContextVar_get(PyContextVar *self, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
PyObject *default_value = NULL;
if (!_PyArg_UnpackStack(args, nargs, "get",
0, 1,
&default_value)) {
goto exit;
}
return_value = _contextvars_ContextVar_get_impl(self, default_value);
exit:
return return_value;
}
PyDoc_STRVAR(_contextvars_ContextVar_set__doc__,
"set($self, value, /)\n"
"--\n"
"\n");
#define _CONTEXTVARS_CONTEXTVAR_SET_METHODDEF \
{"set", (PyCFunction)_contextvars_ContextVar_set, METH_O, _contextvars_ContextVar_set__doc__},
PyDoc_STRVAR(_contextvars_ContextVar_reset__doc__,
"reset($self, token, /)\n"
"--\n"
"\n");
#define _CONTEXTVARS_CONTEXTVAR_RESET_METHODDEF \
{"reset", (PyCFunction)_contextvars_ContextVar_reset, METH_O, _contextvars_ContextVar_reset__doc__},
/*[clinic end generated code: output=d9a675e3a52a14fc input=a9049054013a1b77]*/

1220
Python/context.c Normal file

File diff suppressed because it is too large Load Diff

2982
Python/hamt.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,8 @@
#include "Python-ast.h" #include "Python-ast.h"
#undef Yield /* undefine macro conflicting with winbase.h */ #undef Yield /* undefine macro conflicting with winbase.h */
#include "internal/context.h"
#include "internal/hamt.h"
#include "internal/pystate.h" #include "internal/pystate.h"
#include "grammar.h" #include "grammar.h"
#include "node.h" #include "node.h"
@ -758,6 +760,9 @@ _Py_InitializeCore(const _PyCoreConfig *core_config)
return _Py_INIT_ERR("can't initialize warnings"); return _Py_INIT_ERR("can't initialize warnings");
} }
if (!_PyContext_Init())
return _Py_INIT_ERR("can't init context");
/* This call sets up builtin and frozen import support */ /* This call sets up builtin and frozen import support */
if (!interp->core_config._disable_importlib) { if (!interp->core_config._disable_importlib) {
err = initimport(interp, sysmod); err = initimport(interp, sysmod);
@ -1176,6 +1181,7 @@ Py_FinalizeEx(void)
_Py_HashRandomization_Fini(); _Py_HashRandomization_Fini();
_PyArg_Fini(); _PyArg_Fini();
PyAsyncGen_Fini(); PyAsyncGen_Fini();
_PyContext_Fini();
/* Cleanup Unicode implementation */ /* Cleanup Unicode implementation */
_PyUnicode_Fini(); _PyUnicode_Fini();

View File

@ -173,6 +173,8 @@ PyInterpreterState_New(void)
} }
HEAD_UNLOCK(); HEAD_UNLOCK();
interp->tstate_next_unique_id = 0;
return interp; return interp;
} }
@ -313,6 +315,11 @@ new_threadstate(PyInterpreterState *interp, int init)
tstate->async_gen_firstiter = NULL; tstate->async_gen_firstiter = NULL;
tstate->async_gen_finalizer = NULL; tstate->async_gen_finalizer = NULL;
tstate->context = NULL;
tstate->context_ver = 1;
tstate->id = ++interp->tstate_next_unique_id;
if (init) if (init)
_PyThreadState_Init(tstate); _PyThreadState_Init(tstate);
@ -499,6 +506,8 @@ PyThreadState_Clear(PyThreadState *tstate)
Py_CLEAR(tstate->coroutine_wrapper); Py_CLEAR(tstate->coroutine_wrapper);
Py_CLEAR(tstate->async_gen_firstiter); Py_CLEAR(tstate->async_gen_firstiter);
Py_CLEAR(tstate->async_gen_finalizer); Py_CLEAR(tstate->async_gen_finalizer);
Py_CLEAR(tstate->context);
} }

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define exts=pyexpat;select;unicodedata;winsound;_bz2;_elementtree;_socket;_ssl;_msi;_ctypes;_hashlib;_multiprocessing;_lzma;_decimal;_overlapped;_sqlite3;_asyncio;_queue;_distutils_findvs ?> <?define exts=pyexpat;select;unicodedata;winsound;_bz2;_elementtree;_socket;_ssl;_msi;_ctypes;_hashlib;_multiprocessing;_lzma;_decimal;_overlapped;_sqlite3;_asyncio;_queue;_distutils_findvs;_contextvars ?>
<Fragment> <Fragment>
<ComponentGroup Id="lib_extensions"> <ComponentGroup Id="lib_extensions">
<?foreach ext in $(var.exts)?> <?foreach ext in $(var.exts)?>

View File

@ -644,6 +644,9 @@ class PyBuildExt(build_ext):
# array objects # array objects
exts.append( Extension('array', ['arraymodule.c']) ) exts.append( Extension('array', ['arraymodule.c']) )
# Context Variables
exts.append( Extension('_contextvars', ['_contextvarsmodule.c']) )
shared_math = 'Modules/_math.o' shared_math = 'Modules/_math.o'
# complex math library functions # complex math library functions
exts.append( Extension('cmath', ['cmathmodule.c'], exts.append( Extension('cmath', ['cmathmodule.c'],