Remove a single warning from the warnings filter list
Maybe I'm missing something, but I don't think that there is anyway to remove a warning from the warnings filter list so that it will be shown again. Example: >>> import warnings >>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened >>> warnings.warn("something happened") >>> Once a warning has been displayed, it won't be displayed again until you exit the interpreter and start a new session. That's usually what we want, but sometimes I do want to re-display the warning. The warnings module has a function, reset_warnings, but it does too much, removing all the filters including those set at interpreter startup. I'd like a function to remove a single item, something like this: >>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened >>> warnings.warn("something happened") >>> >>> warnings.forget(UserWarning("something happened")) >>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened or similar. Thoughts? -- Steve
On Wed, Sep 01, 2021 at 05:27:40PM +1000, Steven D'Aprano wrote:
Maybe I'm missing something, but I don't think that there is anyway to remove a warning from the warnings filter list so that it will be shown again.
Example:
>>> import warnings >>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened >>> warnings.warn("something happened") >>>
Once a warning has been displayed, it won't be displayed again until you exit the interpreter and start a new session. That's usually what we want, but sometimes I do want to re-display the warning.
The warnings module has a function, reset_warnings, but it does too much, removing all the filters including those set at interpreter startup. I'd like a function to remove a single item, something like this:
>>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened >>> warnings.warn("something happened") >>> >>> warnings.forget(UserWarning("something happened")) >>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened
or similar.
Thoughts?
Sounds like a missing feature. Zbyszek
On Wed, Sep 1, 2021 at 6:58 PM Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> wrote:
On Wed, Sep 01, 2021 at 05:27:40PM +1000, Steven D'Aprano wrote:
Maybe I'm missing something, but I don't think that there is anyway to remove a warning from the warnings filter list so that it will be shown again.
Example:
>>> import warnings >>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened >>> warnings.warn("something happened") >>>
Once a warning has been displayed, it won't be displayed again until you exit the interpreter and start a new session. That's usually what we want, but sometimes I do want to re-display the warning.
The warnings module has a function, reset_warnings, but it does too much, removing all the filters including those set at interpreter startup. I'd like a function to remove a single item, something like this:
>>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened >>> warnings.warn("something happened") >>> >>> warnings.forget(UserWarning("something happened")) >>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened
or similar.
Thoughts?
Sounds like a missing feature.
I'd agree. A bit of poking around in the warnings module shows multiple ways that the onceness can be registered (there's one for if it's set to "once", another if it's set to "module", and a third for "default" which is also checked by the other two), most of which are tossed into __warningsregistry__ in the module's globals. Should be easy enough to purge a particular message from that, but reaching into it manually seems like the wrong thing to do. It does work, though.
import warnings warnings.warn("Hello") <stdin>:1: UserWarning: Hello warnings.warn("Hello") __warningregistry__ {'version': 0, ('Hello', <class 'UserWarning'>, 1): True} warnings.warn("Hello") warnings.warn("Hello world") <stdin>:1: UserWarning: Hello world warnings.warn("Hello world") __warningregistry__ {'version': 0, ('Hello', <class 'UserWarning'>, 1): True, ('Hello world', <class 'UserWarning'>, 1): True} __warningregistry__.pop(('Hello', UserWarning, 1), None) True warnings.warn("Hello world") warnings.warn("Hello") <stdin>:1: UserWarning: Hello
Seems like a useful feature to add. ChrisA
On 01/09/2021 09:27, Steven D'Aprano wrote:
Maybe I'm missing something, but I don't think that there is anyway to remove a warning from the warnings filter list so that it will be shown again.
Example:
>>> import warnings >>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened >>> warnings.warn("something happened") >>>
Once a warning has been displayed, it won't be displayed again until you exit the interpreter and start a new session. That's usually what we want, but sometimes I do want to re-display the warning.
The warnings module has a function, reset_warnings, but it does too much, removing all the filters including those set at interpreter startup. I'd like a function to remove a single item, something like this:
>>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened >>> warnings.warn("something happened") >>> >>> warnings.forget(UserWarning("something happened")) >>> warnings.warn("something happened") <stdin>:1: UserWarning: something happened
or similar.
Thoughts?
Instead of removing it you might add a filter to get a similar effect:
import warnings def bark(): warnings.warn("woof!")
bark()
Warning (from warnings module): File "<pyshell#76>", line 1 UserWarning: woof!
bark() warnings.filterwarnings("always", "woof!") bark()
Warning (from warnings module): File "<pyshell#76>", line 1 UserWarning: woof!
bark()
Warning (from warnings module): File "<pyshell#76>", line 1 UserWarning: woof!
On Wed, Sep 01, 2021 at 03:40:37PM +0200, Peter Otten wrote:
Instead of removing it you might add a filter to get a similar effect:
[...]
warnings.filterwarnings("always", "woof!")
Unfortunately that's too aggressive. In my use-case, `bark` will be called many, many times in a loop, and so potentially "woof" will be displayed over and over again. That will be annoying. Once it has been displayed *once* in that loop, it shouldn't be displayed again for the rest of the loop. Also, `bark` is being called from a comprehension or sort key function, so it is difficult to keep an explicit flag myself: # My use case is *not* this def process(values): seen = False for item in values: if condition: if seen: bark() seen = True # More like this: sorted(values, key=func) # func may call bark Allowing `bark` to *always* display could be annoying. Ideally I want it to be displayed *at most* once per call to sorted(). I can do that by removing the filter after sorting. Or at least I could if it were possible to remove individual filters. -- Steve
On 02/09/2021 04:32, Steven D'Aprano wrote:
On Wed, Sep 01, 2021 at 03:40:37PM +0200, Peter Otten wrote:
Instead of removing it you might add a filter to get a similar effect:
[...]
warnings.filterwarnings("always", "woof!")
Unfortunately that's too aggressive. In my use-case, `bark` will be called many, many times in a loop, and so potentially "woof" will be displayed over and over again. That will be annoying.
Once it has been displayed *once* in that loop, it shouldn't be displayed again for the rest of the loop.
Also, `bark` is being called from a comprehension or sort key function, so it is difficult to keep an explicit flag myself:
# My use case is *not* this def process(values): seen = False for item in values: if condition: if seen: bark() seen = True
# More like this: sorted(values, key=func) # func may call bark
Allowing `bark` to *always* display could be annoying. Ideally I want it to be displayed *at most* once per call to sorted(). I can do that by removing the filter after sorting. Or at least I could if it were possible to remove individual filters.
Then use filterwarnings("once", ...):
import warnings def bark(): warnings.warn("woof!")
for i in range(6): print(i) if i == 3: warnings.filterwarnings("once", "woof!") bark()
0 Warning (from warnings module): File "<pyshell#145>", line 2 UserWarning: woof! 1 2 3 Warning (from warnings module): File "<pyshell#145>", line 2 UserWarning: woof! 4 5 For good performance you'd ideally provide the lineno, but I don't see a clean way to get that.
On Thu, Sep 2, 2021 at 7:36 PM Peter Otten <__peter__@web.de> wrote:
On 02/09/2021 04:32, Steven D'Aprano wrote:
On Wed, Sep 01, 2021 at 03:40:37PM +0200, Peter Otten wrote:
Instead of removing it you might add a filter to get a similar effect:
[...]
warnings.filterwarnings("always", "woof!")
Unfortunately that's too aggressive. In my use-case, `bark` will be called many, many times in a loop, and so potentially "woof" will be displayed over and over again. That will be annoying.
Once it has been displayed *once* in that loop, it shouldn't be displayed again for the rest of the loop.
Also, `bark` is being called from a comprehension or sort key function, so it is difficult to keep an explicit flag myself:
# My use case is *not* this def process(values): seen = False for item in values: if condition: if seen: bark() seen = True
# More like this: sorted(values, key=func) # func may call bark
Allowing `bark` to *always* display could be annoying. Ideally I want it to be displayed *at most* once per call to sorted(). I can do that by removing the filter after sorting. Or at least I could if it were possible to remove individual filters.
Then use filterwarnings("once", ...):
import warnings def bark(): warnings.warn("woof!")
for i in range(6): print(i) if i == 3: warnings.filterwarnings("once", "woof!") bark()
0
Warning (from warnings module): File "<pyshell#145>", line 2 UserWarning: woof! 1 2 3
Warning (from warnings module): File "<pyshell#145>", line 2 UserWarning: woof! 4 5
For good performance you'd ideally provide the lineno, but I don't see a clean way to get that.
That has the side effect that all OTHER warnings get their onceness reset too though.
warnings.warn("hello") <stdin>:1: UserWarning: hello warnings.warn("world") <stdin>:1: UserWarning: world warnings.warn("hello") warnings.warn("world") warnings.filterwarnings("once", "hello") warnings.warn("hello") <stdin>:1: UserWarning: hello warnings.warn("world") <stdin>:1: UserWarning: world
(It's actually the same as resetfilters, and for the same reason - whenever the filters change, all oncenesses get forgotten.) ChrisA
On 02/09/2021 11:41, Chris Angelico wrote:
On Thu, Sep 2, 2021 at 7:36 PM Peter Otten <__peter__@web.de> wrote:
On 02/09/2021 04:32, Steven D'Aprano wrote:
On Wed, Sep 01, 2021 at 03:40:37PM +0200, Peter Otten wrote:
Instead of removing it you might add a filter to get a similar effect:
[...]
> warnings.filterwarnings("always", "woof!")
Unfortunately that's too aggressive. In my use-case, `bark` will be called many, many times in a loop, and so potentially "woof" will be displayed over and over again. That will be annoying.
Then use filterwarnings("once", ...):
That has the side effect that all OTHER warnings get their onceness reset too though.
Oops, insufficient testing. But I really didn't expect that...
(It's actually the same as resetfilters, and for the same reason - whenever the filters change, all oncenesses get forgotten.)
Could be changed (I'm tempted to say fixed)?
On Fri, Sep 3, 2021 at 12:45 AM Peter Otten <__peter__@web.de> wrote:
On 02/09/2021 11:41, Chris Angelico wrote:
On Thu, Sep 2, 2021 at 7:36 PM Peter Otten <__peter__@web.de> wrote:
On 02/09/2021 04:32, Steven D'Aprano wrote:
On Wed, Sep 01, 2021 at 03:40:37PM +0200, Peter Otten wrote:
Instead of removing it you might add a filter to get a similar effect:
[...]
>> warnings.filterwarnings("always", "woof!")
Unfortunately that's too aggressive. In my use-case, `bark` will be called many, many times in a loop, and so potentially "woof" will be displayed over and over again. That will be annoying.
Then use filterwarnings("once", ...):
That has the side effect that all OTHER warnings get their onceness reset too though.
Oops, insufficient testing. But I really didn't expect that...
(It's actually the same as resetfilters, and for the same reason - whenever the filters change, all oncenesses get forgotten.)
Could be changed (I'm tempted to say fixed)?
Not sure. I don't think __warningregistry__ is meant to be manipulated other than by the warnings module itself. (It gets a single passing mention in the docs, nothing more.) The solution would be pretty simple, and technically *can* be done from the calling module, but I think a warnings.forget() function would be the best way to do it. I'm disinclined to change the behaviour of resetfilters and filter changes in general (they bump a version number, and if the registry has an older version, it's ignored), but adding a separate function to forget a single warning would be backward compatible. ChrisA
participants (4)
-
Chris Angelico
-
Peter Otten
-
Steven D'Aprano
-
Zbigniew Jędrzejewski-Szmek