-
-
Notifications
You must be signed in to change notification settings - Fork 31.7k
gh-128384: Use a context variable for warnings.catch_warnings #130010
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
* Add ``sys.flags.inherit_context``. * Add ``-X inherit_context`` and :envvar:`PYTHON_INHERIT_CONTEXT`
Since the unittest/runner.py establishes a new warnings context with `catch_warnings`, these tests must use `warnings._get_filters()` to retrieve the current list of filters, not the `warnings.filters` global.
I think we should add some tests which use both asyncio and warnings to check for correct contexts in async environments too. |
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
Check behaviour of the catch_warnings() context manager when used with asyncio co-routines and threads.
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
51da563
to
543927b
Compare
This seems a bit simpler since gather() will create tasks as needed.
This is needed because unit tests are now run with "-W error" on the command line in CI (pythonGH-128770). We want to start with an empty list of filters to avoid supurious errors.
…ythongh-130010) Make `warnings.catch_warnings()` use a context variable for holding the warning filtering state if the `sys.flags.context_aware_warnings` flag is set to true. This makes using the context manager thread-safe in multi-threaded programs. Add the `sys.flags.thread_inherit_context` flag. If true, starting a new thread with `threading.Thread` will use a copy of the context from the caller of `Thread.start()`. Both these flags are set to true by default for the free-threaded build and false for the default build. Move the Python implementation of warnings.py into _py_warnings.py. Make _contextvars a builtin module. Co-authored-by: Kumar Aditya <kumaraditya@python.org>
@nascheme @kumaraditya303 Please could you check the buildbots? https://buildbot.python.org/#/release_status AMD64 CentOS9 NoGIL Refleaks 3.x (tier-1: breaks need immediate fix/revert) and ARM64 MacOS M1 Refleaks NoGIL 3.x (tier-2: breaks need fix/revert within 24h) have been failing for 6 days, and this PR seems a likely cause? For example: test_threaded_context (test.test_warnings.CThreadTests.test_threaded_context) ... Warning -- Uncaught thread exception: TypeErrorException in thread Thread-5 (run_a):
Traceback (most recent call last):
File "/Users/ec2-user/buildbot/buildarea/3.x.itamaro-macos-arm64-aws.macos-with-brew.refleak.nogil/build/Lib/threading.py", line 1079, in _bootstrap_inner
self._context.run(self.run)
~~~~~~~~~~~~~~~~~^^^^^^^^^^
File "/Users/ec2-user/buildbot/buildarea/3.x.itamaro-macos-arm64-aws.macos-with-brew.refleak.nogil/build/Lib/threading.py", line 1021, in run
self._target(*self._args, **self._kwargs)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/ec2-user/buildbot/buildarea/3.x.itamaro-macos-arm64-aws.macos-with-brew.refleak.nogil/build/Lib/test/test_warnings/__init__.py", line 1650, in run_a
self.module.warn('run_a warning', UserWarning)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/ec2-user/buildbot/buildarea/3.x.itamaro-macos-arm64-aws.macos-with-brew.refleak.nogil/build/Lib/_py_warnings.py", line 227, in _showwarnmsg
raise TypeError("warnings.showwarning() must be set to a "
"function or method")TypeError: warnings.showwarning() must be set to a function or method https://buildbot.python.org/#/builders/1368/builds/2970/steps/6/logs/stdio |
…rnings (pythongh-130010)" This reverts commit d687900.
I'll look into it. My guess is it is a flaky test and the test could just be disabled as a stop-gap. |
Thanks, here's a draft revert PR has passing buildbots: #132601. |
I believe this should fix the test failure without reverting the whole change: GH-132611. I'm waiting for CI tests to complete. |
Make
warnings.catch_warnings()
use a context variable for holding the warning filtering state if thesys.flags.context_aware_warnings
flag is set to true. This makes using the context manager thread-safe in multi-threaded programs and safe for asyncio coroutines and tasks. The flag is true by default in free-threaded builds and is otherwise false. The value of the flag can be overridden by the the-X thread_safe_warnings
command-line option or by thePYTHON_CONTEXT_AWARE_WARNINGS
environment variable.It is expected that one day the flag might default to true for all Python builds, not just the free-threaded one. However, I think it makes sense to not commit to a schedule to do that until we have a better idea of what code is affected by this change. Feedback from people using the free-threaded build should provide guidance.
This PR is on top of gh-128209.
A previous version of this PR used a single flag to control both behavior of
Thread
inheriting the context and also thecatch_warnings
context manager. However, based on some feedback, I've decided that two flags makes things more clear. Likely programs would like to set both flags to true to get the most intuitive behavior.When the
context_aware_warnings
flag is true and acatch_warnings()
context is active, added filters go into a list of filters stored in the contextvar, not thewarnings.filters
list. The filters inwarnings.filters
are still applied but they apply after the contextvar filters.That difference with how
warnings.filters
works is probably the most likely thing to cause broken user code. In the unit tests, I had to change a number of references towarnings.filters
towarnings._get_filters()
. That function returns the list of filters from the current context or the global filters if there is no context active. Perhaps_get_filters()
should become a public function? I think it would be better if examining and manipulating that list was discouraged and so that's why I made the function non-public, at least for now.I created
_py_warnings.py
to contain the Python implementation for the warnings module. This matches other modules likedecimal
that have Python and C versions of the same module. In the case ofwarnings
things are a bit more complicated since you can assign to module globals (thefilters
list or theshowwarning()
function are common to override). This is a cleaner organization, IMHO. Nowwarnings.py
just imports the parts it needs and the unit tests are a bit simpler.I cleaned up the unit tests for
warnings
a bit. Passingmodule
tocatch_warnings()
actually serves no purpose. Sincesys.modules['warnings']
is already swapped to the module under test, the code does the right thing even when that parameter is not given. I also replaced calls tooriginal_warnings.catch_warnings()
withself.module.catch_warnings()
. Both those calls do the same thing but the second spelling seems less confusing to me.pyperformance comparison
📚 Documentation preview 📚: https://cpython-previews--130010.org.readthedocs.build/