mirror of
https://github.com/python/cpython
synced 2024-09-04 15:56:13 +00:00
bpo-32436: Implement PEP 567 (#5027)
This commit is contained in:
parent
9089a26591
commit
f23746a934
|
@ -109,6 +109,7 @@
|
|||
#include "pyerrors.h"
|
||||
|
||||
#include "pystate.h"
|
||||
#include "context.h"
|
||||
|
||||
#include "pyarena.h"
|
||||
#include "modsupport.h"
|
||||
|
|
86
Include/context.h
Normal file
86
Include/context.h
Normal 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 */
|
41
Include/internal/context.h
Normal file
41
Include/internal/context.h
Normal 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
113
Include/internal/hamt.h
Normal 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 */
|
|
@ -143,6 +143,8 @@ typedef struct _is {
|
|||
/* AtExit module */
|
||||
void (*pyexitfunc)(PyObject *);
|
||||
PyObject *pyexitmodule;
|
||||
|
||||
uint64_t tstate_next_unique_id;
|
||||
} PyInterpreterState;
|
||||
#endif /* !Py_LIMITED_API */
|
||||
|
||||
|
@ -270,6 +272,12 @@ typedef struct _ts {
|
|||
PyObject *async_gen_firstiter;
|
||||
PyObject *async_gen_finalizer;
|
||||
|
||||
PyObject *context;
|
||||
uint64_t context_ver;
|
||||
|
||||
/* Unique thread state id. */
|
||||
uint64_t id;
|
||||
|
||||
/* XXX signal handlers should also be here */
|
||||
|
||||
} PyThreadState;
|
||||
|
|
|
@ -489,7 +489,7 @@ def time(self):
|
|||
"""
|
||||
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.
|
||||
|
||||
Return a Handle: an opaque object with a cancel() method that
|
||||
|
@ -505,12 +505,13 @@ def call_later(self, delay, callback, *args):
|
|||
Any positional arguments after the callback will be passed to
|
||||
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:
|
||||
del timer._source_traceback[-1]
|
||||
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.
|
||||
|
||||
Absolute time corresponds to the event loop's time() method.
|
||||
|
@ -519,14 +520,14 @@ def call_at(self, when, callback, *args):
|
|||
if self._debug:
|
||||
self._check_thread()
|
||||
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:
|
||||
del timer._source_traceback[-1]
|
||||
heapq.heappush(self._scheduled, timer)
|
||||
timer._scheduled = True
|
||||
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.
|
||||
|
||||
This operates as a FIFO queue: callbacks are called in the
|
||||
|
@ -540,7 +541,7 @@ def call_soon(self, callback, *args):
|
|||
if self._debug:
|
||||
self._check_thread()
|
||||
self._check_callback(callback, 'call_soon')
|
||||
handle = self._call_soon(callback, args)
|
||||
handle = self._call_soon(callback, args, context)
|
||||
if handle._source_traceback:
|
||||
del handle._source_traceback[-1]
|
||||
return handle
|
||||
|
@ -555,8 +556,8 @@ def _check_callback(self, callback, method):
|
|||
f'a callable object was expected by {method}(), '
|
||||
f'got {callback!r}')
|
||||
|
||||
def _call_soon(self, callback, args):
|
||||
handle = events.Handle(callback, args, self)
|
||||
def _call_soon(self, callback, args, context):
|
||||
handle = events.Handle(callback, args, self, context)
|
||||
if handle._source_traceback:
|
||||
del handle._source_traceback[-1]
|
||||
self._ready.append(handle)
|
||||
|
@ -579,12 +580,12 @@ def _check_thread(self):
|
|||
"Non-thread-safe operation invoked on an event loop other "
|
||||
"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."""
|
||||
self._check_closed()
|
||||
if self._debug:
|
||||
self._check_callback(callback, 'call_soon_threadsafe')
|
||||
handle = self._call_soon(callback, args)
|
||||
handle = self._call_soon(callback, args, context)
|
||||
if handle._source_traceback:
|
||||
del handle._source_traceback[-1]
|
||||
self._write_to_self()
|
||||
|
|
|
@ -41,13 +41,13 @@ def format_cb(callback):
|
|||
return format_helpers._format_callback_source(callback, ())
|
||||
|
||||
if size == 1:
|
||||
cb = format_cb(cb[0])
|
||||
cb = format_cb(cb[0][0])
|
||||
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:
|
||||
cb = '{}, <{} more>, {}'.format(format_cb(cb[0]),
|
||||
cb = '{}, <{} more>, {}'.format(format_cb(cb[0][0]),
|
||||
size - 2,
|
||||
format_cb(cb[-1]))
|
||||
format_cb(cb[-1][0]))
|
||||
return f'cb=[{cb}]'
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
'_get_running_loop',
|
||||
)
|
||||
|
||||
import contextvars
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
|
@ -32,9 +33,13 @@ class Handle:
|
|||
"""Object returned by callback registration methods."""
|
||||
|
||||
__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._callback = callback
|
||||
self._args = args
|
||||
|
@ -80,7 +85,7 @@ def cancelled(self):
|
|||
|
||||
def _run(self):
|
||||
try:
|
||||
self._callback(*self._args)
|
||||
self._context.run(self._callback, *self._args)
|
||||
except Exception as exc:
|
||||
cb = format_helpers._format_callback_source(
|
||||
self._callback, self._args)
|
||||
|
@ -101,9 +106,9 @@ class TimerHandle(Handle):
|
|||
|
||||
__slots__ = ['_scheduled', '_when']
|
||||
|
||||
def __init__(self, when, callback, args, loop):
|
||||
def __init__(self, when, callback, args, loop, context=None):
|
||||
assert when is not None
|
||||
super().__init__(callback, args, loop)
|
||||
super().__init__(callback, args, loop, context)
|
||||
if self._source_traceback:
|
||||
del self._source_traceback[-1]
|
||||
self._when = when
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
)
|
||||
|
||||
import concurrent.futures
|
||||
import contextvars
|
||||
import logging
|
||||
import sys
|
||||
|
||||
|
@ -144,8 +145,8 @@ def _schedule_callbacks(self):
|
|||
return
|
||||
|
||||
self._callbacks[:] = []
|
||||
for callback in callbacks:
|
||||
self._loop.call_soon(callback, self)
|
||||
for callback, ctx in callbacks:
|
||||
self._loop.call_soon(callback, self, context=ctx)
|
||||
|
||||
def cancelled(self):
|
||||
"""Return True if the future was cancelled."""
|
||||
|
@ -192,7 +193,7 @@ def exception(self):
|
|||
self.__log_traceback = False
|
||||
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.
|
||||
|
||||
The callback is called with a single argument - the future object. If
|
||||
|
@ -200,9 +201,11 @@ def add_done_callback(self, fn):
|
|||
scheduled with call_soon.
|
||||
"""
|
||||
if self._state != _PENDING:
|
||||
self._loop.call_soon(fn, self)
|
||||
self._loop.call_soon(fn, self, context=context)
|
||||
else:
|
||||
self._callbacks.append(fn)
|
||||
if context is None:
|
||||
context = contextvars.copy_context()
|
||||
self._callbacks.append((fn, context))
|
||||
|
||||
# New method not in PEP 3148.
|
||||
|
||||
|
@ -211,7 +214,9 @@ def remove_done_callback(self, fn):
|
|||
|
||||
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)
|
||||
if removed_count:
|
||||
self._callbacks[:] = filtered_callbacks
|
||||
|
|
|
@ -256,7 +256,7 @@ def _ensure_fd_no_transport(self, fd):
|
|||
|
||||
def _add_reader(self, fd, callback, *args):
|
||||
self._check_closed()
|
||||
handle = events.Handle(callback, args, self)
|
||||
handle = events.Handle(callback, args, self, None)
|
||||
try:
|
||||
key = self._selector.get_key(fd)
|
||||
except KeyError:
|
||||
|
@ -292,7 +292,7 @@ def _remove_reader(self, fd):
|
|||
|
||||
def _add_writer(self, fd, callback, *args):
|
||||
self._check_closed()
|
||||
handle = events.Handle(callback, args, self)
|
||||
handle = events.Handle(callback, args, self, None)
|
||||
try:
|
||||
key = self._selector.get_key(fd)
|
||||
except KeyError:
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
)
|
||||
|
||||
import concurrent.futures
|
||||
import contextvars
|
||||
import functools
|
||||
import inspect
|
||||
import types
|
||||
|
@ -96,8 +97,9 @@ def __init__(self, coro, *, loop=None):
|
|||
self._must_cancel = False
|
||||
self._fut_waiter = None
|
||||
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)
|
||||
|
||||
def __del__(self):
|
||||
|
@ -229,15 +231,18 @@ def _step(self, exc=None):
|
|||
new_exc = RuntimeError(
|
||||
f'Task {self!r} got Future '
|
||||
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:
|
||||
if result is self:
|
||||
new_exc = RuntimeError(
|
||||
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:
|
||||
result._asyncio_future_blocking = False
|
||||
result.add_done_callback(self._wakeup)
|
||||
result.add_done_callback(
|
||||
self._wakeup, context=self._context)
|
||||
self._fut_waiter = result
|
||||
if self._must_cancel:
|
||||
if self._fut_waiter.cancel():
|
||||
|
@ -246,21 +251,24 @@ def _step(self, exc=None):
|
|||
new_exc = RuntimeError(
|
||||
f'yield was used instead of yield from '
|
||||
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:
|
||||
# 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):
|
||||
# Yielding a generator is just wrong.
|
||||
new_exc = RuntimeError(
|
||||
f'yield was used instead of yield from for '
|
||||
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:
|
||||
# Yielding something else is an error.
|
||||
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:
|
||||
_leave_task(self._loop, self)
|
||||
self = None # Needed to break cycles when an exception occurs.
|
||||
|
|
|
@ -92,7 +92,7 @@ def add_signal_handler(self, sig, callback, *args):
|
|||
except (ValueError, OSError) as exc:
|
||||
raise RuntimeError(str(exc))
|
||||
|
||||
handle = events.Handle(callback, args, self)
|
||||
handle = events.Handle(callback, args, self, None)
|
||||
self._signal_handlers[sig] = handle
|
||||
|
||||
try:
|
||||
|
|
4
Lib/contextvars.py
Normal file
4
Lib/contextvars.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
from _contextvars import Context, ContextVar, Token, copy_context
|
||||
|
||||
|
||||
__all__ = ('Context', 'ContextVar', 'Token', 'copy_context')
|
|
@ -192,14 +192,14 @@ def test_close(self):
|
|||
self.assertRaises(RuntimeError, self.loop.run_until_complete, f)
|
||||
|
||||
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.assertFalse(self.loop._scheduled)
|
||||
self.assertIn(h, self.loop._ready)
|
||||
|
||||
def test__add_callback_cancelled_handle(self):
|
||||
h = asyncio.Handle(lambda: False, (), self.loop)
|
||||
h = asyncio.Handle(lambda: False, (), self.loop, None)
|
||||
h.cancel()
|
||||
|
||||
self.loop._add_callback(h)
|
||||
|
@ -333,9 +333,9 @@ def test_thread(loop, debug, create_loop=False):
|
|||
|
||||
def test__run_once(self):
|
||||
h1 = asyncio.TimerHandle(time.monotonic() + 5.0, lambda: True, (),
|
||||
self.loop)
|
||||
self.loop, None)
|
||||
h2 = asyncio.TimerHandle(time.monotonic() + 10.0, lambda: True, (),
|
||||
self.loop)
|
||||
self.loop, None)
|
||||
|
||||
h1.cancel()
|
||||
|
||||
|
@ -390,7 +390,7 @@ def cb(loop):
|
|||
handle = loop.call_soon(lambda: True)
|
||||
|
||||
h = asyncio.TimerHandle(time.monotonic() - 1, cb, (self.loop,),
|
||||
self.loop)
|
||||
self.loop, None)
|
||||
|
||||
self.loop._process_events = mock.Mock()
|
||||
self.loop._scheduled.append(h)
|
||||
|
|
|
@ -565,16 +565,22 @@ def __del__(self):
|
|||
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
||||
'requires the C _asyncio module')
|
||||
class CFutureTests(BaseFutureTests, test_utils.TestCase):
|
||||
cls = futures._CFuture
|
||||
try:
|
||||
cls = futures._CFuture
|
||||
except AttributeError:
|
||||
cls = None
|
||||
|
||||
|
||||
@unittest.skipUnless(hasattr(futures, '_CFuture'),
|
||||
'requires the C _asyncio module')
|
||||
class CSubFutureTests(BaseFutureTests, test_utils.TestCase):
|
||||
class CSubFuture(futures._CFuture):
|
||||
pass
|
||||
try:
|
||||
class CSubFuture(futures._CFuture):
|
||||
pass
|
||||
|
||||
cls = CSubFuture
|
||||
cls = CSubFuture
|
||||
except AttributeError:
|
||||
cls = None
|
||||
|
||||
|
||||
class PyFutureTests(BaseFutureTests, test_utils.TestCase):
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
import collections
|
||||
import contextlib
|
||||
import contextvars
|
||||
import functools
|
||||
import gc
|
||||
import io
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import types
|
||||
|
@ -1377,9 +1378,9 @@ def __init__(self, *args, **kwds):
|
|||
self.cb_added = False
|
||||
super().__init__(*args, **kwds)
|
||||
|
||||
def add_done_callback(self, fn):
|
||||
def add_done_callback(self, *args, **kwargs):
|
||||
self.cb_added = True
|
||||
super().add_done_callback(fn)
|
||||
super().add_done_callback(*args, **kwargs)
|
||||
|
||||
fut = Fut(loop=self.loop)
|
||||
result = None
|
||||
|
@ -2091,7 +2092,7 @@ def main():
|
|||
|
||||
@mock.patch('asyncio.base_events.logger')
|
||||
def test_error_in_call_soon(self, m_log):
|
||||
def call_soon(callback, *args):
|
||||
def call_soon(callback, *args, **kwargs):
|
||||
raise ValueError
|
||||
self.loop.call_soon = call_soon
|
||||
|
||||
|
@ -2176,6 +2177,91 @@ async def 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):
|
||||
BaseTask = cls.Task
|
||||
|
@ -2193,9 +2279,9 @@ def _schedule_callbacks(self):
|
|||
self.calls['_schedule_callbacks'] += 1
|
||||
return super()._schedule_callbacks()
|
||||
|
||||
def add_done_callback(self, *args):
|
||||
def add_done_callback(self, *args, **kwargs):
|
||||
self.calls['add_done_callback'] += 1
|
||||
return super().add_done_callback(*args)
|
||||
return super().add_done_callback(*args, **kwargs)
|
||||
|
||||
class Task(CommonFuture, BaseTask):
|
||||
def _step(self, *args):
|
||||
|
@ -2486,10 +2572,13 @@ class PyIntrospectionTests(unittest.TestCase, BaseTaskIntrospectionTests):
|
|||
@unittest.skipUnless(hasattr(tasks, '_c_register_task'),
|
||||
'requires the C _asyncio module')
|
||||
class CIntrospectionTests(unittest.TestCase, BaseTaskIntrospectionTests):
|
||||
_register_task = staticmethod(tasks._c_register_task)
|
||||
_unregister_task = staticmethod(tasks._c_unregister_task)
|
||||
_enter_task = staticmethod(tasks._c_enter_task)
|
||||
_leave_task = staticmethod(tasks._c_leave_task)
|
||||
if hasattr(tasks, '_c_register_task'):
|
||||
_register_task = staticmethod(tasks._c_register_task)
|
||||
_unregister_task = staticmethod(tasks._c_unregister_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:
|
||||
|
|
|
@ -365,7 +365,7 @@ def close(self):
|
|||
raise AssertionError("Time generator is not finished")
|
||||
|
||||
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):
|
||||
self.remove_reader_count[fd] += 1
|
||||
|
@ -391,7 +391,7 @@ def assert_no_reader(self, fd):
|
|||
raise AssertionError(f'fd {fd} is registered')
|
||||
|
||||
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):
|
||||
self.remove_writer_count[fd] += 1
|
||||
|
@ -457,9 +457,9 @@ def _run_once(self):
|
|||
self.advance_time(advance)
|
||||
self._timers = []
|
||||
|
||||
def call_at(self, when, callback, *args):
|
||||
def call_at(self, when, callback, *args, context=None):
|
||||
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):
|
||||
return
|
||||
|
|
1064
Lib/test/test_context.py
Normal file
1064
Lib/test/test_context.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -354,6 +354,8 @@ PYTHON_OBJS= \
|
|||
Python/pylifecycle.o \
|
||||
Python/pymath.o \
|
||||
Python/pystate.o \
|
||||
Python/context.o \
|
||||
Python/hamt.o \
|
||||
Python/pythonrun.o \
|
||||
Python/pytime.o \
|
||||
Python/bootstrap_hash.o \
|
||||
|
@ -996,6 +998,7 @@ PYTHON_HEADERS= \
|
|||
$(srcdir)/Include/pymem.h \
|
||||
$(srcdir)/Include/pyport.h \
|
||||
$(srcdir)/Include/pystate.h \
|
||||
$(srcdir)/Include/context.h \
|
||||
$(srcdir)/Include/pystrcmp.h \
|
||||
$(srcdir)/Include/pystrtod.h \
|
||||
$(srcdir)/Include/pystrhex.h \
|
||||
|
@ -1023,6 +1026,7 @@ PYTHON_HEADERS= \
|
|||
$(srcdir)/Include/internal/mem.h \
|
||||
$(srcdir)/Include/internal/pygetopt.h \
|
||||
$(srcdir)/Include/internal/pystate.h \
|
||||
$(srcdir)/Include/internal/context.h \
|
||||
$(srcdir)/Include/internal/warnings.h \
|
||||
$(DTRACE_HEADERS)
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Implement PEP 567
|
|
@ -176,6 +176,7 @@ _symtable symtablemodule.c
|
|||
#array arraymodule.c # array objects
|
||||
#cmath cmathmodule.c _math.c # -lm # complex math library functions
|
||||
#math mathmodule.c _math.c # -lm # math library functions, e.g. sin()
|
||||
#_contextvars _contextvarsmodule.c # Context Variables
|
||||
#_struct _struct.c # binary structure packing/unpacking
|
||||
#_weakref _weakref.c # basic weak reference support
|
||||
#_testcapi _testcapimodule.c # Python C API test module
|
||||
|
|
|
@ -36,6 +36,7 @@ static PyObject *asyncio_task_print_stack_func;
|
|||
static PyObject *asyncio_task_repr_info_func;
|
||||
static PyObject *asyncio_InvalidStateError;
|
||||
static PyObject *asyncio_CancelledError;
|
||||
static PyObject *context_kwname;
|
||||
|
||||
|
||||
/* WeakSet containing all alive tasks. */
|
||||
|
@ -59,6 +60,7 @@ typedef enum {
|
|||
PyObject_HEAD \
|
||||
PyObject *prefix##_loop; \
|
||||
PyObject *prefix##_callback0; \
|
||||
PyContext *prefix##_context0; \
|
||||
PyObject *prefix##_callbacks; \
|
||||
PyObject *prefix##_exception; \
|
||||
PyObject *prefix##_result; \
|
||||
|
@ -77,6 +79,7 @@ typedef struct {
|
|||
FutureObj_HEAD(task)
|
||||
PyObject *task_fut_waiter;
|
||||
PyObject *task_coro;
|
||||
PyContext *task_context;
|
||||
int task_must_cancel;
|
||||
int task_log_destroy_pending;
|
||||
} TaskObj;
|
||||
|
@ -336,11 +339,38 @@ get_event_loop(void)
|
|||
|
||||
|
||||
static int
|
||||
call_soon(PyObject *loop, PyObject *func, PyObject *arg)
|
||||
call_soon(PyObject *loop, PyObject *func, PyObject *arg, PyContext *ctx)
|
||||
{
|
||||
PyObject *handle;
|
||||
handle = _PyObject_CallMethodIdObjArgs(
|
||||
loop, &PyId_call_soon, func, arg, NULL);
|
||||
PyObject *stack[3];
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -387,8 +417,11 @@ future_schedule_callbacks(FutureObj *fut)
|
|||
/* There's a 1st callback */
|
||||
|
||||
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_context0);
|
||||
if (ret) {
|
||||
/* If an error occurs in pure-Python implementation,
|
||||
all callbacks are cleared. */
|
||||
|
@ -413,9 +446,11 @@ future_schedule_callbacks(FutureObj *fut)
|
|||
}
|
||||
|
||||
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,
|
||||
all callbacks are cleared. */
|
||||
Py_CLEAR(fut->fut_callbacks);
|
||||
|
@ -462,6 +497,7 @@ future_init(FutureObj *fut, PyObject *loop)
|
|||
}
|
||||
|
||||
fut->fut_callback0 = NULL;
|
||||
fut->fut_context0 = NULL;
|
||||
fut->fut_callbacks = NULL;
|
||||
|
||||
return 0;
|
||||
|
@ -566,7 +602,7 @@ future_get_result(FutureObj *fut, PyObject **result)
|
|||
}
|
||||
|
||||
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)) {
|
||||
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) {
|
||||
/* The future is done/cancelled, so schedule the callback
|
||||
right away. */
|
||||
if (call_soon(fut->fut_loop, arg, (PyObject*) fut)) {
|
||||
if (call_soon(fut->fut_loop, arg, (PyObject*) fut, ctx)) {
|
||||
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.
|
||||
*/
|
||||
|
||||
if (fut->fut_callbacks != NULL) {
|
||||
int err = PyList_Append(fut->fut_callbacks, arg);
|
||||
if (err != 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (fut->fut_callback0 == NULL) {
|
||||
if (fut->fut_callbacks == NULL && fut->fut_callback0 == NULL) {
|
||||
Py_INCREF(arg);
|
||||
fut->fut_callback0 = arg;
|
||||
Py_INCREF(ctx);
|
||||
fut->fut_context0 = ctx;
|
||||
}
|
||||
else {
|
||||
fut->fut_callbacks = PyList_New(1);
|
||||
if (fut->fut_callbacks == NULL) {
|
||||
PyObject *tup = PyTuple_New(2);
|
||||
if (tup == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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_callback0);
|
||||
Py_CLEAR(fut->fut_context0);
|
||||
Py_CLEAR(fut->fut_callbacks);
|
||||
Py_CLEAR(fut->fut_result);
|
||||
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_callback0);
|
||||
Py_VISIT(fut->fut_context0);
|
||||
Py_VISIT(fut->fut_callbacks);
|
||||
Py_VISIT(fut->fut_result);
|
||||
Py_VISIT(fut->fut_exception);
|
||||
|
@ -821,6 +873,8 @@ _asyncio.Future.add_done_callback
|
|||
|
||||
fn: object
|
||||
/
|
||||
*
|
||||
context: object = NULL
|
||||
|
||||
Add a callback to be run when the future becomes done.
|
||||
|
||||
|
@ -830,10 +884,21 @@ scheduled with call_soon.
|
|||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_asyncio_Future_add_done_callback(FutureObj *self, PyObject *fn)
|
||||
/*[clinic end generated code: output=819e09629b2ec2b5 input=8f818b39990b027d]*/
|
||||
_asyncio_Future_add_done_callback_impl(FutureObj *self, PyObject *fn,
|
||||
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]
|
||||
|
@ -865,6 +930,7 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
|
|||
if (cmp == 1) {
|
||||
/* callback0 == fn */
|
||||
Py_CLEAR(self->fut_callback0);
|
||||
Py_CLEAR(self->fut_context0);
|
||||
cleared_callback0 = 1;
|
||||
}
|
||||
}
|
||||
|
@ -880,8 +946,9 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
|
|||
}
|
||||
|
||||
if (len == 1) {
|
||||
PyObject *cb_tup = PyList_GET_ITEM(self->fut_callbacks, 0);
|
||||
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) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -903,7 +970,7 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
|
|||
int ret;
|
||||
PyObject *item = PyList_GET_ITEM(self->fut_callbacks, i);
|
||||
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 (j < len) {
|
||||
PyList_SET_ITEM(newlist, j, item);
|
||||
|
@ -1081,47 +1148,49 @@ static PyObject *
|
|||
FutureObj_get_callbacks(FutureObj *fut)
|
||||
{
|
||||
Py_ssize_t i;
|
||||
Py_ssize_t len;
|
||||
PyObject *new_list;
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
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) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *tup0 = PyTuple_New(2);
|
||||
if (tup0 == NULL) {
|
||||
Py_DECREF(new_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_INCREF(fut->fut_callback0);
|
||||
PyList_SET_ITEM(new_list, 0, fut->fut_callback0);
|
||||
for (i = 0; i < len; i++) {
|
||||
PyObject *cb = PyList_GET_ITEM(fut->fut_callbacks, i);
|
||||
Py_INCREF(cb);
|
||||
PyList_SET_ITEM(new_list, i + 1, cb);
|
||||
PyTuple_SET_ITEM(tup0, 0, fut->fut_callback0);
|
||||
assert(fut->fut_context0 != NULL);
|
||||
Py_INCREF(fut->fut_context0);
|
||||
PyTuple_SET_ITEM(tup0, 1, (PyObject *)fut->fut_context0);
|
||||
|
||||
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;
|
||||
|
@ -1912,6 +1981,11 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop)
|
|||
return -1;
|
||||
}
|
||||
|
||||
self->task_context = PyContext_CopyCurrent();
|
||||
if (self->task_context == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
self->task_fut_waiter = NULL;
|
||||
self->task_must_cancel = 0;
|
||||
self->task_log_destroy_pending = 1;
|
||||
|
@ -1928,6 +2002,7 @@ static int
|
|||
TaskObj_clear(TaskObj *task)
|
||||
{
|
||||
(void)FutureObj_clear((FutureObj*) task);
|
||||
Py_CLEAR(task->task_context);
|
||||
Py_CLEAR(task->task_coro);
|
||||
Py_CLEAR(task->task_fut_waiter);
|
||||
return 0;
|
||||
|
@ -1936,6 +2011,7 @@ TaskObj_clear(TaskObj *task)
|
|||
static int
|
||||
TaskObj_traverse(TaskObj *task, visitproc visit, void *arg)
|
||||
{
|
||||
Py_VISIT(task->task_context);
|
||||
Py_VISIT(task->task_coro);
|
||||
Py_VISIT(task->task_fut_waiter);
|
||||
(void)FutureObj_traverse((FutureObj*) task, visit, arg);
|
||||
|
@ -2451,7 +2527,7 @@ task_call_step_soon(TaskObj *task, PyObject *arg)
|
|||
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);
|
||||
return ret;
|
||||
}
|
||||
|
@ -2650,7 +2726,8 @@ task_step_impl(TaskObj *task, PyObject *exc)
|
|||
if (wrapper == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
res = future_add_done_callback((FutureObj*)result, wrapper);
|
||||
res = future_add_done_callback(
|
||||
(FutureObj*)result, wrapper, task->task_context);
|
||||
Py_DECREF(wrapper);
|
||||
if (res == NULL) {
|
||||
goto fail;
|
||||
|
@ -2724,14 +2801,23 @@ task_step_impl(TaskObj *task, PyObject *exc)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/* result.add_done_callback(task._wakeup) */
|
||||
wrapper = TaskWakeupMethWrapper_new(task);
|
||||
if (wrapper == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
res = _PyObject_CallMethodIdObjArgs(result,
|
||||
&PyId_add_done_callback,
|
||||
wrapper, NULL);
|
||||
|
||||
/* result.add_done_callback(task._wakeup) */
|
||||
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);
|
||||
if (res == NULL) {
|
||||
goto fail;
|
||||
|
@ -3141,6 +3227,8 @@ module_free(void *m)
|
|||
Py_CLEAR(current_tasks);
|
||||
Py_CLEAR(iscoroutine_typecache);
|
||||
|
||||
Py_CLEAR(context_kwname);
|
||||
|
||||
module_free_freelists();
|
||||
}
|
||||
|
||||
|
@ -3164,6 +3252,17 @@ module_init(void)
|
|||
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) \
|
||||
Py_CLEAR(module); \
|
||||
module = PyImport_ImportModule(NAME); \
|
||||
|
|
75
Modules/_contextvarsmodule.c
Normal file
75
Modules/_contextvarsmodule.c
Normal 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;
|
||||
}
|
|
@ -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[] = {
|
||||
{"raise_exception", raise_exception, METH_VARARGS},
|
||||
{"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS},
|
||||
|
@ -4655,6 +4662,7 @@ static PyMethodDef TestMethods[] = {
|
|||
{"get_mapping_values", get_mapping_values, METH_O},
|
||||
{"get_mapping_items", get_mapping_items, METH_O},
|
||||
{"test_pythread_tss_key_state", test_pythread_tss_key_state, METH_VARARGS},
|
||||
{"hamt", new_hamt, METH_NOARGS},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
|
|
@ -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__},
|
||||
|
||||
PyDoc_STRVAR(_asyncio_Future_add_done_callback__doc__,
|
||||
"add_done_callback($self, fn, /)\n"
|
||||
"add_done_callback($self, fn, /, *, context=None)\n"
|
||||
"--\n"
|
||||
"\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.");
|
||||
|
||||
#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__,
|
||||
"remove_done_callback($self, fn, /)\n"
|
||||
|
@ -763,4 +786,4 @@ _asyncio__leave_task(PyObject *module, PyObject *const *args, Py_ssize_t nargs,
|
|||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=616e814431893dcc input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=bcbaf1b2480f4aa9 input=a9049054013a1b77]*/
|
||||
|
|
21
Modules/clinic/_contextvarsmodule.c.h
Normal file
21
Modules/clinic/_contextvarsmodule.c.h
Normal 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]*/
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
|
||||
#include "Python.h"
|
||||
#include "internal/context.h"
|
||||
#include "internal/mem.h"
|
||||
#include "internal/pystate.h"
|
||||
#include "frameobject.h" /* for PyFrame_ClearFreeList */
|
||||
|
@ -790,6 +791,7 @@ clear_freelists(void)
|
|||
(void)PyDict_ClearFreeList();
|
||||
(void)PySet_ClearFreeList();
|
||||
(void)PyAsyncGen_ClearFreeLists();
|
||||
(void)PyContext_ClearFreeList();
|
||||
}
|
||||
|
||||
/* This is the main function. Read this to understand how the
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "Python.h"
|
||||
#include "internal/pystate.h"
|
||||
#include "internal/context.h"
|
||||
#include "frameobject.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
77
PCbuild/_contextvars.vcxproj
Normal file
77
PCbuild/_contextvars.vcxproj
Normal 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>
|
16
PCbuild/_contextvars.vcxproj.filters
Normal file
16
PCbuild/_contextvars.vcxproj.filters
Normal 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>
|
|
@ -121,4 +121,4 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
<!-- pyshellext.dll -->
|
||||
<Projects Include="pyshellext.vcxproj" />
|
||||
<!-- 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 -->
|
||||
<ExternalModules Include="_bz2;_lzma;_sqlite3" />
|
||||
<!-- _ssl will build _socket as well, which may cause conflicts in parallel builds -->
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
<ClInclude Include="..\Include\codecs.h" />
|
||||
<ClInclude Include="..\Include\compile.h" />
|
||||
<ClInclude Include="..\Include\complexobject.h" />
|
||||
<ClInclude Include="..\Include\context.h" />
|
||||
<ClInclude Include="..\Include\datetime.h" />
|
||||
<ClInclude Include="..\Include\descrobject.h" />
|
||||
<ClInclude Include="..\Include\dictobject.h" />
|
||||
|
@ -112,7 +113,9 @@
|
|||
<ClInclude Include="..\Include\import.h" />
|
||||
<ClInclude Include="..\Include\internal\ceval.h" />
|
||||
<ClInclude Include="..\Include\internal\condvar.h" />
|
||||
<ClInclude Include="..\Include\internal\context.h" />
|
||||
<ClInclude Include="..\Include\internal\gil.h" />
|
||||
<ClInclude Include="..\Include\internal\hamt.h" />
|
||||
<ClInclude Include="..\Include\internal\mem.h" />
|
||||
<ClInclude Include="..\Include\internal\pystate.h" />
|
||||
<ClInclude Include="..\Include\internal\warnings.h" />
|
||||
|
@ -232,6 +235,7 @@
|
|||
<ClCompile Include="..\Modules\_blake2\blake2s_impl.c" />
|
||||
<ClCompile Include="..\Modules\_codecsmodule.c" />
|
||||
<ClCompile Include="..\Modules\_collectionsmodule.c" />
|
||||
<ClCompile Include="..\Modules\_contextvarsmodule.c" />
|
||||
<ClCompile Include="..\Modules\_csv.c" />
|
||||
<ClCompile Include="..\Modules\_functoolsmodule.c" />
|
||||
<ClCompile Include="..\Modules\_heapqmodule.c" />
|
||||
|
@ -359,6 +363,7 @@
|
|||
<ClCompile Include="..\Python\ceval.c" />
|
||||
<ClCompile Include="..\Python\codecs.c" />
|
||||
<ClCompile Include="..\Python\compile.c" />
|
||||
<ClCompile Include="..\Python\context.c" />
|
||||
<ClCompile Include="..\Python\dynamic_annotations.c" />
|
||||
<ClCompile Include="..\Python\dynload_win.c" />
|
||||
<ClCompile Include="..\Python\errors.c" />
|
||||
|
@ -373,6 +378,7 @@
|
|||
<ClCompile Include="..\Python\getplatform.c" />
|
||||
<ClCompile Include="..\Python\getversion.c" />
|
||||
<ClCompile Include="..\Python\graminit.c" />
|
||||
<ClCompile Include="..\Python\hamt.c" />
|
||||
<ClCompile Include="..\Python\import.c" />
|
||||
<ClCompile Include="..\Python\importdl.c" />
|
||||
<ClCompile Include="..\Python\marshal.c" />
|
||||
|
|
|
@ -81,6 +81,9 @@
|
|||
<ClInclude Include="..\Include\complexobject.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\context.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\datetime.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
|
@ -135,9 +138,15 @@
|
|||
<ClInclude Include="..\Include\internal\condvar.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\internal\context.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\internal\gil.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\internal\hamt.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\internal\mem.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
|
@ -842,6 +851,9 @@
|
|||
<ClCompile Include="..\Python\compile.c">
|
||||
<Filter>Python</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Python\context.h">
|
||||
<Filter>Python</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Python\dynamic_annotations.c">
|
||||
<Filter>Python</Filter>
|
||||
</ClCompile>
|
||||
|
@ -884,6 +896,9 @@
|
|||
<ClCompile Include="..\Python\graminit.c">
|
||||
<Filter>Python</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Python\hamt.h">
|
||||
<Filter>Python</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Python\import.c">
|
||||
<Filter>Python</Filter>
|
||||
</ClCompile>
|
||||
|
@ -998,6 +1013,9 @@
|
|||
<ClCompile Include="..\Modules\_asynciomodule.c">
|
||||
<Filter>Modules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Modules\_contextvarsmodule.c">
|
||||
<Filter>Modules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(zlibDir)\adler32.c">
|
||||
<Filter>Modules\zlib</Filter>
|
||||
</ClCompile>
|
||||
|
|
146
Python/clinic/context.c.h
Normal file
146
Python/clinic/context.c.h
Normal 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
1220
Python/context.c
Normal file
File diff suppressed because it is too large
Load diff
2982
Python/hamt.c
Normal file
2982
Python/hamt.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include "Python-ast.h"
|
||||
#undef Yield /* undefine macro conflicting with winbase.h */
|
||||
#include "internal/context.h"
|
||||
#include "internal/hamt.h"
|
||||
#include "internal/pystate.h"
|
||||
#include "grammar.h"
|
||||
#include "node.h"
|
||||
|
@ -758,6 +760,9 @@ _Py_InitializeCore(const _PyCoreConfig *core_config)
|
|||
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 */
|
||||
if (!interp->core_config._disable_importlib) {
|
||||
err = initimport(interp, sysmod);
|
||||
|
@ -1176,6 +1181,7 @@ Py_FinalizeEx(void)
|
|||
_Py_HashRandomization_Fini();
|
||||
_PyArg_Fini();
|
||||
PyAsyncGen_Fini();
|
||||
_PyContext_Fini();
|
||||
|
||||
/* Cleanup Unicode implementation */
|
||||
_PyUnicode_Fini();
|
||||
|
|
|
@ -173,6 +173,8 @@ PyInterpreterState_New(void)
|
|||
}
|
||||
HEAD_UNLOCK();
|
||||
|
||||
interp->tstate_next_unique_id = 0;
|
||||
|
||||
return interp;
|
||||
}
|
||||
|
||||
|
@ -313,6 +315,11 @@ new_threadstate(PyInterpreterState *interp, int init)
|
|||
tstate->async_gen_firstiter = NULL;
|
||||
tstate->async_gen_finalizer = NULL;
|
||||
|
||||
tstate->context = NULL;
|
||||
tstate->context_ver = 1;
|
||||
|
||||
tstate->id = ++interp->tstate_next_unique_id;
|
||||
|
||||
if (init)
|
||||
_PyThreadState_Init(tstate);
|
||||
|
||||
|
@ -499,6 +506,8 @@ PyThreadState_Clear(PyThreadState *tstate)
|
|||
Py_CLEAR(tstate->coroutine_wrapper);
|
||||
Py_CLEAR(tstate->async_gen_firstiter);
|
||||
Py_CLEAR(tstate->async_gen_finalizer);
|
||||
|
||||
Py_CLEAR(tstate->context);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<ComponentGroup Id="lib_extensions">
|
||||
<?foreach ext in $(var.exts)?>
|
||||
|
|
3
setup.py
3
setup.py
|
@ -644,6 +644,9 @@ def detect_modules(self):
|
|||
# array objects
|
||||
exts.append( Extension('array', ['arraymodule.c']) )
|
||||
|
||||
# Context Variables
|
||||
exts.append( Extension('_contextvars', ['_contextvarsmodule.c']) )
|
||||
|
||||
shared_math = 'Modules/_math.o'
|
||||
# complex math library functions
|
||||
exts.append( Extension('cmath', ['cmathmodule.c'],
|
||||
|
|
Loading…
Reference in a new issue