CLICK HERE FOR A TL;DR
Put this line in something you’re deprecating, and everything will be fine*
warnings.warn("old_function() is deprecated. Use new_function() instead.", DeprecationWarning)
Remember to import warnings
and check the “The good way” section, if you want to know what, how and why.
The backstory
A while ago I was working with 2-6 other developers on a Django project that allowed a panel of metal music listeners to pick songs they liked, from a large list of songs. The picked songs would then be shown off in a weekly radio show.
The whole thing was entirely volunteer run with me as a project manager - both for the development project and the show project. This meant that I couldn’t set requirements that were too time-consuming.
During the development most developers brought in some amazing ideas, but also widely varying code styles. And sometimes that caused more mental load than neccessary. I couldn’t just dictate a strict code style, to prevent that extra mental load, since it was all volunteer work. So instead we developed some new “good practices” together.
Developing those together meant that we were all invested in making the work easier for each other - and most of all being considerate about the different coding habbits we all had.
One of those things we agreed upon was how we’d warn other developers when we’d deprecate something. We all agreed very quickly, that having a mailing list or just talking asynchronously wouldn’t work well, since we were all working on the project off and on.
Let’s say a function isn’t as good was we want it to be, but we don’t want to just replace it - in case someone else is using it.
The bad ways
We came up with several ways of indicating a deprecation nicely. Many of them were less than ideal. Let’s look at a few of the ways that initially felt good, but turned out to be a kinda bad.
With comments
One of the first mentioned ideas was to simply add a comment. Like this:
# DEPRECATED - USE read_file_better() INSTEAD!
def read_file(filename):
f = open(filename, "r")
data = f.read()
f.close()
return data
def read_file_better(filename):
with open(filename, "r") as f:
data = f.read()
return data
Comments are great. I love comments in code - as long as they don’t distract from the functionality of the code. In this case the comment does give the desired information, but it requires a developer to look at each class, method, function, etc. they’d like to use, if it’s been given a deprecation wanring comment, since they last used it.
That adds to a lot of extra effort in the long run - which is a good way to wear out a volunteer developer. Or even a paid one. I get where the idea comes from, and I’m sure it was well intended. But we found something better.
With a print()
call
I can’t imagine a developer who hasn’t used print()
calls for some light debugging or as gentle reminders to oneself.
This approach was mentioned pretty early on as well, since it’s such an “instinct” for many of us, to just throw a cheeky print()
call here and there.
def make_readable(name, age):
readable = "Name: %s, Age: %d" % (name, age)
print("You have called a deprecated function. Please use make_legible() instead.")
return readable
The problem here is that this function might not even get called by the developer - or that the print()
call output drowns in the output from tests or deployment.
Similarly to the previous approach, this also, at least to some degree, requires a developer to see of this function has gotten this print()
call, since they last used it.
With the deprecated
package
The deprecated
package by Laurent Laporte
seemed like an excellent idea, while we were talking about it.
It lets you simply add a decorator with message and a version cutoff, that will give a terminal output when the deprecated function is called.
from deprecated import deprecated
@deprecated(version='2.0', reason='Use make_legible() instead')
def make_readable(name, age):
readable = "Name: %s, Age: %d" % (name, age)
return readable
def make_legible(name, age):
return f"Name: {name}, Age: {age}"
print(make_readable("Sum", 41))
This will output a deprecation warning that even quotes the line of code, where the deprecated function is called.
/home/tim/Desktop/python-tidbits/20250320-deprecated/good_with_deprecated_package.py:11: DeprecationWarning: Call to deprecated function (or staticmethod) make_readable. (Use make_legible() instead) -- Deprecated since version 2.0.
print(make_readable("Sum", 41))
While really handy, it’s got a similar limitation as the use of a print()
call in the deprecated function.
We only have the output to indicate the deprecation - unless we specifically look for one, every time we want to use this function.
And the output might very well get lost in the terminal amongst test output or deployment/build/CI/CD messages.
Another downside here, is that another dependency is introduced. Meaning another risk of a supply-chain attack and another risk of trouble if the package gets reworked, deprecated or removed.
The good way
As is often the case, the problem we were facing has been faced and solved by others before us.
In this case there is even a solution built into Python since 2.1 (April 2001) with the DeprecationWarning
in the warnings
module.
import warnings
def make_readable(name, age):
readable = "Name: %s, Age: %d" % (name, age)
warnings.warn("make_readable() is deprecated. Use make_legible() instead.", DeprecationWarning)
return readable
def make_legible(name, age):
return f"Name: {name}, Age: {age}"
print(make_readable("Sum", 41))
As with the deprecated
decorator and the print()
call, we get a terminal output that mentions the deprecation.
/home/tim/Desktop/python-tidbits/20250320-deprecated/good_with_builtin.py:5: DeprecationWarning: old_function() is deprecated. Use make_legible() instead.
warnings.warn("make_readable() is deprecated. Use make_legible() instead.", DeprecationWarning)
Name: Sum, Age: 41
But in addition our IDEs could immediately indicate that we were about to use a deprecated funciton.


At the same time this would be caught and reported by our build tools. So the best of both worlds and then some.
The use is incredibly easy. You simply import the warnings
module and add a call to warnings.warn()
with the DeprecationWarning
warning category class.
In this case like this:
warnings.warn("make_readable() is deprecated. Use make_legible() instead.", DeprecationWarning)
One line added to the deprecated function - that’s all it takes. This one line gives us the terminal output, the indicators in our IDEs and the warnings for our build/deployment system.
We all agreed to use it this way moving forward, and one of the other developers mentioned getting this adopted in his employers style guide.
Bonus for those who love decorators
If you, like me, like decorators and want a combination of the decorator convenience from the deprecated
package
with the features of the built-in DeprecationWarning
- this might be to your taste.
from warnings import deprecated
@deprecated('make_readable() is deprecated. Use make_legible() instead.')
def make_readable(name, age):
readable = "Name: %s, Age: %d" % (name, age)
return readable
def make_legible(name, age):
return f"Name: {name}, Age: {age}"
print(make_readable("Sum", 41))
Further reading
-
Here’s some more info about the neat stuff the
warnings
module can be used for https://docs.python.org/3/library/warnings.html#warning-categories -
Scott Robinson has a neat and short post about how and when to disable warnings in python: https://stackabuse.com/bytes/how-to-disable-warnings-in-python/
-
If you’re using pytest, you might find this article on capturing warnings when testing handy: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
-
And if you’re not using pytest, but you’d like to give it a go, take a look here: https://betterstack.com/community/guides/testing/pytest-guide/
-
You’ll find the snippets from this example in my
Python Tidbits
repo on GitLab here: https://gitlab.com/veticus/python-tidbits/-/tree/main/20250320-deprecated
Thanks
Thanks to Justus Menke for making the original photo free to use.