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 1, 2021 at 6:58 PM Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> wrote:
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.
Seems like a useful feature to add. ChrisA

On 01/09/2021 09:27, Steven D'Aprano wrote:
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!
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:
Then use filterwarnings("once", ...):
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:
That has the side effect that all OTHER warnings get their onceness reset too though.
(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:
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:
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

On Wed, Sep 1, 2021 at 6:58 PM Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> wrote:
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.
Seems like a useful feature to add. ChrisA

On 01/09/2021 09:27, Steven D'Aprano wrote:
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!
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:
Then use filterwarnings("once", ...):
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:
That has the side effect that all OTHER warnings get their onceness reset too though.
(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:
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:
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