diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 28d56909b30034..7ac5add810d0c7 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -32,6 +32,7 @@ import unittest import numbers import locale +import contextvars from test.support import (run_unittest, run_doctest, is_resource_enabled, requires_IEEE_754, requires_docstrings, requires_legacy_unicode_capi) @@ -1616,8 +1617,8 @@ def test_threading(self): self.finish1 = threading.Event() self.finish2 = threading.Event() - th1 = threading.Thread(target=thfunc1, args=(self,)) - th2 = threading.Thread(target=thfunc2, args=(self,)) + th1 = threading.Thread(target=thfunc1, args=(self,), context=contextvars.Context()) + th2 = threading.Thread(target=thfunc2, args=(self,), context=contextvars.Context()) th1.start() th2.start() diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index c51de6f4b85537..8eb2a85b1755b2 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -8,6 +8,7 @@ from test.support.import_helper import import_module from test.support.script_helper import assert_python_ok, assert_python_failure +import contextvars import random import sys import _thread @@ -928,6 +929,31 @@ def test_debug_deprecation(self): b'is deprecated and will be removed in Python 3.12') self.assertIn(msg, err) + def test_contextvars(self): + context_var = contextvars.ContextVar("context_var") + context_var.set("default") + + result = None + + def target(): + nonlocal result + result = context_var.get() + + t = threading.Thread(target=target) + t.start() + t.join() + + self.assertEqual(result, "default") + + custom_ctx = contextvars.Context() + custom_ctx.run(lambda: context_var.set("custom")) + + t = threading.Thread(target=target, context=custom_ctx) + t.start() + t.join() + + self.assertEqual(result, "custom") + class ThreadJoinOnShutdown(BaseTestCase): diff --git a/Lib/threading.py b/Lib/threading.py index c2b94a5045514f..3d5a86c5cb042c 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -8,6 +8,7 @@ from time import monotonic as _time from _weakrefset import WeakSet from itertools import islice as _islice, count as _count +from contextvars import copy_context as _copy_context try: from _collections import deque as _deque except ImportError: @@ -818,7 +819,7 @@ class Thread: _initialized = False def __init__(self, group=None, target=None, name=None, - args=(), kwargs=None, *, daemon=None): + args=(), kwargs=None, *, daemon=None, context=None): """This constructor should always be called with keyword arguments. Arguments are: *group* should be None; reserved for future extension when a ThreadGroup @@ -863,6 +864,10 @@ class is implemented. else: self._daemonic = current_thread().daemon self._ident = None + if context is not None: + self._context = context + else: + self._context = _copy_context() if _HAVE_THREAD_NATIVE_ID: self._native_id = None self._tstate_lock = None @@ -943,11 +948,11 @@ def run(self): """ try: if self._target is not None: - self._target(*self._args, **self._kwargs) + self._context.run(self._target, *self._args, **self._kwargs) finally: # Avoid a refcycle if the thread is running a function with # an argument that has a member that points to the thread. - del self._target, self._args, self._kwargs + del self._context, self._target, self._args, self._kwargs def _bootstrap(self): # Wrapper around the real bootstrap code that ignores diff --git a/Misc/NEWS.d/next/Library/2021-01-03-13-44-44.bpo-42815.IJqHPr.rst b/Misc/NEWS.d/next/Library/2021-01-03-13-44-44.bpo-42815.IJqHPr.rst new file mode 100644 index 00000000000000..804bdfb9820626 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-01-03-13-44-44.bpo-42815.IJqHPr.rst @@ -0,0 +1,2 @@ +Fix issue when new thread doesn't copy context of a parent thread. Patch +provided by Yurii Karabas. diff --git a/Modules/Setup b/Modules/Setup index 5e26472446677a..a48018dd6b0270 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -169,7 +169,7 @@ _symtable symtablemodule.c #array -DPy_BUILD_CORE_MODULE arraymodule.c # array objects #cmath cmathmodule.c _math.c -DPy_BUILD_CORE_MODULE # -lm # complex math library functions #math mathmodule.c _math.c -DPy_BUILD_CORE_MODULE # -lm # math library functions, e.g. sin() -#_contextvars _contextvarsmodule.c # Context Variables +_contextvars _contextvarsmodule.c # Context Variables #_struct -DPy_BUILD_CORE_MODULE _struct.c # binary structure packing/unpacking #_weakref _weakref.c # basic weak reference support #_testcapi _testcapimodule.c # Python C API test module