Skip to content

Python 3: warnings displayed multiple times despite default 'once' filter #11207

Open

Description

@embray

(I suspect this has come up before but I couldn't find an existing issue for it.)

The expected default behavior in Python for handling warnings is such that once a specific warning has been displayed once, it should not be displayed again (unless the warning filters are updated). E.g.:

Python 3.6.1 (default, Feb 21 2018, 12:59:49)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import warnings
>>> def foo():
...     warnings.warn('foo')
...
>>> foo()
__main__:2: UserWarning: foo
>>> foo()
>>>

However, in IPython CLI this behavior is not observed (probably same for the notebook but I haven't tested):

Python 3.6.1 (default, Feb 21 2018, 12:59:49)
Type "copyright", "credits" or "license" for more information.

IPython 5.5.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import warnings

In [2]: def foo():
   ...:     warnings.warn('foo')
   ...:

In [3]: foo()
.../local/bin/ipython3:2: UserWarning: foo
  # EASY-INSTALL-ENTRY-SCRIPT: 'ipython==5.5.0','console_scripts','ipython3'

In [4]: foo()
.../local/bin/ipython3:2: UserWarning: foo
  # EASY-INSTALL-ENTRY-SCRIPT: 'ipython==5.5.0','console_scripts','ipython3'

I have documented the cause of this behavior here, and I believe it is more of a CPython bug than an IPython bug. But it's possible there's a workaround that could be applied, or at the very least I thought the issue should be noted.

Activity

  1. embray commented on Jun 28, 2018

    @embray
    ContributorAuthor

    Incidentally it seems this behavior is broken on Python 2 as well, but in that case I'm not sure what the reason is...

  2. embray commented on Jun 28, 2018

    @embray
    ContributorAuthor

    Incidentally it seems this behavior is broken on Python 2 as well, but in that case I'm not sure what the reason is...

    I see, this is #6611. So that's intentional. I don't agree with the solution though--it's even more brute-force than the Python 3 bug since it basically breaks the intended behavior (although I understand why a workaround was needed...)

    On second thought, that only impacts code that was entered interactively. It won't affect code from modules so I'm okay with that. Then we're just left with the Python 3 issue which, unfortunately, impacts warnings regardless where they come from....

  3. Carreau commented on Jun 28, 2018

    @Carreau
    Member

    Hum, I can see how this can be annoying in the REPL.

    We switched the default on purpose, as you noted in #6611, and #6680. Would a configuration option to disable this behavior suits you ?

    If there is an upstream issue with Python, the fix will depends on the cost/benefits ratio.

  4. embray commented on Jun 28, 2018

    @embray
    ContributorAuthor

    I believe a configuration option would be useful here. While I understand the rationale behind #6680 it would also be nice to be able to get back more "standard" behavior w.r.t. warnings, even if it's not the default.

    That would do nothing to help with the upstream issue, but it would be good to have either way.

  5. shumpohl commented on Apr 16, 2026

    @shumpohl

    I spend some hours trying to get "once" warning only display once in IPython 9.12.0 with python 3.14.3 and now I am giving up due to a lack of time. Here are my findings in case anybody else wants to try. I was using this little helper

    def persistent_ipython_warning_registry():
        import IPython
        ip = IPython.get_ipython()
        if ip is None:
            return
    
        clear_warning_registry_callbacks = [
            cb
            for cb in ip.events.callbacks["pre_execute"]
            if "clear_warning_registry" in cb.__name__
        ]
    
        if not clear_warning_registry_callbacks:
            logging.getLogger(__name__).info("No warning registry clearing callback found")
        elif len(clear_warning_registry_callbacks) == 1:
            ip.events.callbacks["pre_execute"].remove(clear_warning_registry_callbacks[0])
            logging.getLogger(__name__).info("Cleared warning registry callback")
        else:
            warnings.warn("Multiple candidates found in the IPython EventManager")

    to make globals()["__warningregistry__"] persistent but sadly this did not fix the problem.

    Apparently something touches the warnings filter list in every cell execution. This modifies CPython's internal st->filters_version counter. If filters_version does not match the version key in the warnings registry the registry gets cleared in warnings.warn_explicit.

    Another thing which makes this more complicated is that the CPython implementation from _warnings and the pure python implementation in _py_warnings differ in behavior.

    CPython uses onceregistry only if the registry argument is None(source) while the pure python implementation (source) always uses onceregistry for "once" actions.

    The filters_version of CPython is not accessible from the interpreter. It is increased every time the lock for mutating the filter list is acquired with warnings._filters_mutated_lock_held.

    This is done every time warnings.catch_warnings or warnings.simplefilter are called which happens 6 times for each cell execution in my setup.

  6. Carreau commented on Apr 19, 2026

    @Carreau
    Member

    probably #6611 ? So on purpose?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions