Search code examples
pythonterminalgoogle-colaboratorywarningsstack-level

Inconsistent behavior of `warnings.warn`


In Google Colab, when I run the following:

def f():
    import warnings
    warnings.warn("deprecated", DeprecationWarning, stacklevel=2)

def g():
    return f()

def h():
    return g()

h() # or f() or g()

I get 1 warning in the output:

<ipython-input-9-e09ca081f372>:6: DeprecationWarning: deprecated
  return f()

And when I run:

f()
g()
h()

I get only 2 warnings(I expected 3 warnings because all 3 functions were called):

<ipython-input-10-0d5243608655>:1: DeprecationWarning: deprecated
  f()
<ipython-input-9-e09ca081f372>:6: DeprecationWarning: deprecated
  return f()

Now, I tried the same in Terminal Python and I only got warnings for f() and g() and not for h()

Python 3.11.1 (main, Feb  5 2023, 16:11:00) [Clang 13.1.6 (clang-1316.0.21.2.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def f():
...     import warnings
...     warnings.warn("deprecated", DeprecationWarning, stacklevel=2)
... 
>>> def g():
...     return f()
... 
>>> def h():
...     return g()
... 
>>> f()
<stdin>:1: DeprecationWarning: deprecated
>>> g()
<stdin>:2: DeprecationWarning: deprecated
>>> h()
>>>

I wanted to understand:

  1. why do I see the warning in colab when I run h() individually but not when I run it with f() and g() in the same cell? and why does h() never shows a warning in Terminal Python(I think it is because stacklevel=2)? And how exactly running a warning code in colab different from running a warning code in Terminal Python?
  2. How can I make it so that a warning message is displayed every time f() is called, by me or by some wrapper function?
  3. How does stacklevel and skip_file_prefixes fit in all this?

Thank you :)


Solution

  • This is due to the default warning filter, which will only print the first occurrence of a warning for each location. With stacklevel=2, the warning location is where f() was called, which is <ipython-input-10-0d5243608655>:1 when you call f() directly in the notebook, and <ipython-input-9-e09ca081f372>:6 inside of g() when you call g() directly or indirectly via h(). The same logic applies to the terminal session. Note that if you call h() before g(), the warning will be printed after h() instead:

    Python 3.12.3 (main, Apr 17 2024, 00:00:00) [GCC 13.2.1 20240316 (Red Hat 13.2.1-7)] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> def f():
    ...     import warnings
    ...     warnings.warn("deprecated", DeprecationWarning, stacklevel=2)
    ... 
    >>> def g():
    ...     return f()
    ... 
    >>> def h():
    ...     return g()
    ... 
    >>> f()
    <stdin>:1: DeprecationWarning: deprecated
    >>> h()
    <stdin>:2: DeprecationWarning: deprecated
    >>> g()
    >>> g()
    >>> h()
    >>> 
    

    The inconsistency between the notebook and the terminal is probably this issue: https://github.com/ipython/ipython/issues/11207

    If you want the warning to be printed on every call, you can change the warning filter programmatically by running warnings.simplefilter("always").