It's a good practice to gradually deprecate one's library's API, so that
users get advance warning of coming changes. The built in way to
do so is Python's
import warnings if __name__ == '__main__': warnings.warn('deprecation', DeprecationWarning)
By default, they are not reported!
However, if I run this code, there is no output.
$ python3.5 demo.py $ echo $? 0
The documentation on default warning filters explains:
By default, Python installs several warning filters, which can be overridden by the command-line options passed to
-Wand calls to
BytesWarningis ignored unless the
-boption is given once or twice; in this case this warning is either printed (
-b) or turned into an exception (
ResourceWarningis ignored unless Python was built in debug mode.
Forcing defaults from the command line makes them reported
However, if we force the default warning behavior from the command line, we get the warnings - even though theoretically we only specified how often the warnings should be reported, not which warnings to be reported!
$ python3.5 -W d demo.py demo.py:4: DeprecationWarning: deprecation warnings.warn('warning', DeprecationWarning) $ echo $? 0
Before reading the docs, I suspected the operating system maintainers didn't want to 'spam' the end users of their systems with python library warnings while going about their daily tasks, but apparently this is built into python itself.
So how should I deprecate things?
I'm a big fan of executable documentation, but this might be one of those cases where good old fashioned documentation might be more effective, as we can't expect users of our library to run with warnings enabled.
I would still leave in these deprecations for the project's developers as well as for the pedant users of the library.
Checking against deprecations of our dependencies
That's easier, as we own that code. I would run my builds and tests with
-W d at the very
least, but I would like to try to run with
-W error. Except that I don't want to fail the
build if one of my dependencies is using deprecated apis, so probably I would just have a
main.py where I would explicitly set and reset my warning filters. E.g.: updating
the above demo code would give me the following:
import warnings import os if os.environ.get('TEST', '0') == '1': warnings.filterwarnings(module='.*', action='ignore') warnings.filterwarnings(module=__name__, action='error') if __name__ == '__main__': warnings.warn('deprecation', DeprecationWarning)
$ TEST=1 python3.5 demo.py Traceback (most recent call last): File "demo.py", line 8, in <module> warnings.warn('deprecation', DeprecationWarning) DeprecationWarning: deprecation $ echo $? 1
I yet have to test how feasible it is when our library supports multiple versions of a dependent library, e.g.: Django, but my gut feeling is that it should be doable
Beware of the ordering of warning filters
If in the above example we got the order reversed, then our own
be ignored too!
if os.environ.get('TEST', '0') == '1': warnings.filterwarnings(module=__name__, action='error') warnings.filterwarnings(module='.*', action='ignore')