-
-
Notifications
You must be signed in to change notification settings - Fork 31.5k
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
gh-128384: Use a context variable for warnings.catch_warnings #130010
base: main
Are you sure you want to change the base?
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 added a link to a pyperformance comparison. Small enough to be within the noise, it seems, as I would suspect. |
Using the critical section seems the easiest fix for this. Add a unit test that fails TSAN check if the fix is not applied.
Hello @gpshead please take another look if you can. I believe your feedback has been addressed. For notifying people of this change, the plan is to do that in the following places: first the what's new document can have a note for the 3.14 release; second, there are notes in the free-threaded howto (part of the help) and in the "warnings" and "threading" library docs. |
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.
In the free-threaded build, the flag :data:`~sys.flags.thread_inherit_context` | ||
is set to true by default. In the default GIL-enabled build, the flag | ||
defaults to false. This will cause threads created with | ||
:class:`threading.Thread` to start with a copy of the | ||
:class:`~contextvars.Context()` of the caller of | ||
:meth:`~threading.Thread.start`. If the flag is false, threads start with an | ||
empty :class:`~contextvars.Context()`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the free-threaded build, the flag :data:`~sys.flags.thread_inherit_context` | |
is set to true by default. In the default GIL-enabled build, the flag | |
defaults to false. This will cause threads created with | |
:class:`threading.Thread` to start with a copy of the | |
:class:`~contextvars.Context()` of the caller of | |
:meth:`~threading.Thread.start`. If the flag is false, threads start with an | |
empty :class:`~contextvars.Context()`. | |
In the free-threaded build, the flag :data:`~sys.flags.thread_inherit_context` | |
is set to true by default which causes threads created with | |
:class:`threading.Thread` to start with a copy of the | |
:class:`~contextvars.Context()` of the caller of | |
:meth:`~threading.Thread.start`. In the default GIL-enabled build, the flag | |
defaults to false so threads start with an | |
empty :class:`~contextvars.Context()`. |
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/