tantale / deprecated

Python @deprecated decorator to deprecate old python classes, functions or methods.
MIT License
298 stars 32 forks source link

"default", "module", and "once" actions do not work in Python 3 #25

Closed heidecjj closed 4 years ago

heidecjj commented 4 years ago

Expected Behavior

Paste the following into a file named once.py

from deprecated import deprecated
@deprecated(reason='this should happen once', action='once')
def a():
    return

a()
a()
a()

I expect the deprecation warning to print once:

$ python3 once.py
once.py:6: DeprecationWarning: Call to deprecated function (or staticmethod) a. (this should happen once)
  a()

Actual Behavior

Tell us what happens instead.

$ python3 once.py
once.py:6: DeprecationWarning: Call to deprecated function (or staticmethod) a. (this should happen once)
  a()
once.py:7: DeprecationWarning: Call to deprecated function (or staticmethod) a. (this should happen once)
  a()
once.py:8: DeprecationWarning: Call to deprecated function (or staticmethod) a. (this should happen once)
  a()

Environment

Additional Info

Note: this issue is slightly different from the issue #24 aims to solve. #24 deals with actions specified by global warning filters while this issue deals with actions specified to the @deprecated decorator.

This is not a problem in Python 2. This is only a problem in Python 3 due to the way warnings.catch_warnings() works. Entering and exiting the warnings.catch_warnings() context manager causes Python 3 to clear out its warning registries. The warning registries keep track of which warnings have fired in order to enforce actions like "once", "default", and "module". When deprecated fires its warning inside warnings.catch_warnings(), the warning registries will always be empty, so "once", "default", and "module" end up behaving like "always". I'm not sure there's much we can do to fix this problem.

heidecjj commented 2 years ago

Here's the underlying python bug that causes #25: https://bugs.python.org/issue29672