[Python-ideas] Create a @deprecated decorator (annotation)

47 views
Skip to first unread message

Leonardo Freua

unread,
Jul 29, 2021, 10:39:21 AM7/29/21
to python...@python.org
[Migrating from https://bugs.python.org/issue44701]

Would it be interesting to create a @deprecated decorator to avoid adding warnings.warn("deprecation message", DeprecationWarning, stacklevel=2) in methods body?

Using the decorator approach to indicate depreciation would make the methods cleaner (leaving only their responsibilities in the body) and would be easier to identify, as the cue would be close to the method signature and not mixed with the logic inside the body.

in some cases it will still be necessary to put warnings.warn (..) inside the body of functions/methods because of some message display condition, or we could also express the message display condition in the decorator in @deprecated itself. But it would be interesting to have the possibility of not putting this inside the method body. Even the decorator can come from the notices module.

Example:

(Before)

def check_metadata(self):
"""Deprecated API."""
warn("distutils.command.register.check_metadata is deprecated, \
use the check command instead", PendingDeprecationWarning)
check = self.distribution.get_command_obj('check')
check.ensure_finalized()
check.strict = self.strict
check.restructuredtext = 1
check.run()

(after)

@deprecated("distutils.command.register.check_metadata is deprecated, \
use the check command instead")
def check_metadata(self):
"""Deprecated API."""
check = self.distribution.get_command_obj('check')
check.ensure_finalized()
check.strict = self.strict
check.restructuredtext = 1
check.run()
_______________________________________________
Python-ideas mailing list -- python...@python.org
To unsubscribe send an email to python-id...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python...@python.org/message/62CTVNQ2GIS4B6WUBX23K4CCCK5MCGYL/
Code of Conduct: http://python.org/psf/codeofconduct/

Stéfane Fermigier

unread,
Jul 29, 2021, 10:59:29 AM7/29/21
to Leonardo Freua, Python-Ideas
Hi,

I've been using https://pypi.org/project/Deprecated/ for quite some time and I recommend it.

  S.

--
Stefane Fermigier - http://fermigier.com/ - http://twitter.com/sfermigier - http://linkedin.com/in/sfermigier
Founder & CEO, Abilian - Enterprise Social Software - http://www.abilian.com/
Chairman, National Council for Free & Open Source Software (CNLL) - http://cnll.fr/
Founder & Organiser, PyParis & PyData Paris - http://pyparis.org/http://pydata.fr/

Paul Moore

unread,
Jul 29, 2021, 11:00:40 AM7/29/21
to Leonardo Freua, Python-Ideas
On Thu, 29 Jul 2021 at 15:39, Leonardo Freua
<leonardo.ba...@gmail.com> wrote:
>
> Would it be interesting to create a @deprecated decorator to avoid adding warnings.warn("deprecation message", DeprecationWarning, stacklevel=2) in methods body?

I don't see the value personally.

> Using the decorator approach to indicate depreciation would make the methods cleaner (leaving only their responsibilities in the body) and would be easier to identify, as the cue would be close to the method signature and not mixed with the logic inside the body.

First line of the body vs line before the declaration doesn't feel
like it makes much difference to me.

> in some cases it will still be necessary to put warnings.warn (..) inside the body of functions/methods because of some message display condition, or we could also express the message display condition in the decorator in @deprecated itself. But it would be interesting to have the possibility of not putting this inside the method body. Even the decorator can come from the notices module.

Why would it be "interesting"? I don't see any practical advantage,
and as soon as you need any form of logic you have to rewrite, so why
bother?

If you want this to get support, I think you need to argue the
benefits far more than you have so far...

> Example:
>
> (Before)
>
> def check_metadata(self):
> """Deprecated API."""
> warn("distutils.command.register.check_metadata is deprecated, \
> use the check command instead", PendingDeprecationWarning)
> check = self.distribution.get_command_obj('check')
> check.ensure_finalized()
> check.strict = self.strict
> check.restructuredtext = 1
> check.run()
>
> (after)
>
> @deprecated("distutils.command.register.check_metadata is deprecated, \
> use the check command instead")
> def check_metadata(self):
> """Deprecated API."""
> check = self.distribution.get_command_obj('check')
> check.ensure_finalized()
> check.strict = self.strict
> check.restructuredtext = 1
> check.run()

TBH, the decorator version makes it harder to see the function signature.

-1 from me, I'm afraid.

Disclaimer: I've never actually deprecated an API in my own code, so
my objections are mainly theoretical.

Paul
_______________________________________________
Python-ideas mailing list -- python...@python.org
To unsubscribe send an email to python-id...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python...@python.org/message/6O7WJ3MLF3WEQ6XR7HEZPM6OBUZVY4PU/

Sergei Lebedev

unread,
Jul 29, 2021, 2:25:48 PM7/29/21
to Paul Moore, Leonardo Freua, Python-Ideas
> Why would it be "interesting"? I don't see any practical advantage, and as soon as you need any form of logic you have to rewrite, so why bother?

The advantage of having a declarative API for deprecations is tooling support. It is much easier to detect decorator application than to reliably infer whether a function/class emits (or raises) a DeprecationWarning.

I started a similar thread [*] a few weeks ago, but have had no chance to reply properly since.

Sergei

Michael Smith

unread,
Jul 29, 2021, 3:00:49 PM7/29/21
to Sergei Lebedev, Leonardo Freua, Python-Ideas
Perhaps another approach would be to make a more general purpose warning decorator, that would give a warning when invoking a function or instantiating a class. It could be used for deprecation notices, of course, but has the potential for tooling to give other types of warnings, such as using a thread unsafe call in a threading context.


Leonardo Freua

unread,
Jul 29, 2021, 4:55:14 PM7/29/21
to python...@python.org
This is a good example of how using a decorator to express depreciation is much better and less polluting the method, as the depreciation message doesn't need to be in the method body.

In my view, it would be interesting for Python to natively have the ability to annotate deprecated methods.
_______________________________________________
Python-ideas mailing list -- python...@python.org
To unsubscribe send an email to python-id...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python...@python.org/message/BVYHXKUUCXD3D2JSSBEIEF2WR2PV2TLI/

Paul Bryan

unread,
Jul 29, 2021, 4:58:08 PM7/29/21
to Leonardo Freua, python...@python.org
I'm +1 on deprecation decorator, with some way to represent it so that it can be determined at runtime (e.g. dunder).

Thomas Grainger

unread,
Jul 29, 2021, 5:01:18 PM7/29/21
to Paul Bryan, Leonardo Freua, python-ideas
I'd like to be able to specificy @deprecate on only some @overloads

Thomas Grainger

unread,
Jul 29, 2021, 5:12:01 PM7/29/21
to Paul Bryan, Leonardo Freua, python-ideas
Specially I'd like to be able to deprecate the `Callable[..., Iterable[T]]` type of contextlib.contextmanager

Leonardo Freua

unread,
Jul 29, 2021, 5:14:00 PM7/29/21
to python...@python.org
>>> First line of the body vs line before the declaration doesn't feel
>>> like it makes much difference to me.

Usually, decorators or annotations are placed near the method signature, that is, at the beginning where any reading begins, because it's easier to see that method is deprecated or anything else that needs a decorator.

Putting this information inside the method body, you end up putting information that is not the method's responsibility to provide, this ends up mixing with what the method actually does, it's more information to read in a debug or in maintenance that the method will need to receive. Not to mention the fact that sometimes depreciation messages can be longer than 2 lines.

Most programming languages ​​have some way of expressing depreciation, and it's usually close to the method signature.

I'm not proposing to do away with the way it is done today, but rather to expand the possibilities. There are times when the existence of just one decorator would make the code cleaner than inserting more information into the body of the method/function.

Ps.: Relax Paul, you don't need to be afraid, this is just a proposal.

Thanks for the contribution, good points raised.
_______________________________________________
Python-ideas mailing list -- python...@python.org
To unsubscribe send an email to python-id...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python...@python.org/message/R4OS67MVBS6FYBGFTSEDFFC5NGCXLAQ3/

Chris Angelico

unread,
Jul 29, 2021, 5:19:15 PM7/29/21
to python-ideas
On Fri, Jul 30, 2021 at 7:11 AM Leonardo Freua
<leonardo.ba...@gmail.com> wrote:
>
> >>> First line of the body vs line before the declaration doesn't feel
> >>> like it makes much difference to me.
>
> Usually, decorators or annotations are placed near the method signature, that is, at the beginning where any reading begins, because it's easier to see that method is deprecated or anything else that needs a decorator.
>
> Putting this information inside the method body, you end up putting information that is not the method's responsibility to provide, this ends up mixing with what the method actually does, it's more information to read in a debug or in maintenance that the method will need to receive. Not to mention the fact that sometimes depreciation messages can be longer than 2 lines.
>
> Most programming languages have some way of expressing depreciation, and it's usually close to the method signature.
>

One advantage of language support for deprecation can be an "entry
point only" warning - if a regular function calls a deprecated
function, there's a warning, but if a deprecated function calls
another deprecated function, there won't be. Whether this is worth
implementing here is a separate question, but it would certainly be
easier to do with a decorator than an explicit warn() call.

ChrisA
_______________________________________________
Python-ideas mailing list -- python...@python.org
To unsubscribe send an email to python-id...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python...@python.org/message/FRQXPFRFV72QXVEJ6PITFZ3LI74IP7DU/

Leonardo Freua

unread,
Jul 29, 2021, 5:27:38 PM7/29/21
to python...@python.org
Hi Sergei, I really thought your proposal was very good, in fact, it's quite complete with examples in libraries that I didn't even know implement a depreciation API.

Thanks for the contribution.
_______________________________________________
Python-ideas mailing list -- python...@python.org
To unsubscribe send an email to python-id...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python...@python.org/message/TGQVDHA4QWFSQYSJUUBERSTH4FJ76QDH/

Jack DeVries

unread,
Jul 30, 2021, 8:23:02 AM7/30/21
to Leonardo Freua, python-ideas
Perhaps another approach would be to make a more general purpose warning decorator, that would give a warning when invoking a function or instantiating a class. It could be used for deprecation notices, of course, but has the potential for tooling to give other types of warnings, such as using a thread unsafe call in a threading context.

What about "mark"

import mark

@mark.deprecated
@mark.slow
@mark.thread_unsafe
@mark.insecure

Felipe Rodrigues

unread,
Jul 31, 2021, 8:54:31 AM7/31/21
to Jack DeVries, Leonardo Freua, python-ideas
Recently I needed to do just that on an API and went with the decorator approach.

Having this on the stdlib would be nice, and we can make it more powerful
than just raising a warning. For instance, we could attach a `deprecated = True`
to the function object so that framework authors can use that to handle
depreciation as well - think flask endpoints.

Another option is to have an `alternative` optional kwarg in the decorator,
something like


def new_function():
    ....

@deprecated(alternative=new_function)
def old_function():
    ...


This would raise a warning stating that `new_function` should be used
in the place of `old_function`


Oscar Benjamin

unread,
Jul 31, 2021, 10:02:50 AM7/31/21
to Paul Moore, Leonardo Freua, Python-Ideas
On Thu, 29 Jul 2021 at 16:00, Paul Moore <p.f....@gmail.com> wrote:
>
> On Thu, 29 Jul 2021 at 15:39, Leonardo Freua
> <leonardo.ba...@gmail.com> wrote:
> >
> > Would it be interesting to create a @deprecated decorator to avoid adding warnings.warn("deprecation message", DeprecationWarning, stacklevel=2) in methods body?
>
> I don't see the value personally.

I do see the value. Many projects use this kind of functionality and
end up reinventing something along these lines e.g.:
https://github.com/scipy/scipy/blob/6bc0b7297aaca7c7c1b4854d421b20d35172222c/scipy/_lib/deprecation.py#L7

Actually deprecating functions and methods is probably the easiest
part. You can either emit a warning from inside the function or use a
deprecator to do the same when the function is called. That does mean
though that there's no programmatic way to know without calling the
function that it is deprecated.

Other cases are deprecating e.g. a class, or a particular
instance/object or a module. If a module is deprecated you can trigger
a warning on import but of course import caching means that subsequent
imports won't see the warning (maybe that's okay...). For an object
you might want to use __getattr__ to trigger the warning. Then
difficulty comes if introspective tools use getattr, dir etc so users
end up seeing the warning even though nothing is actually using the
deprecated functionality:
https://github.com/sympy/sympy/issues/9371

This is the kind of thing that is on the edge of being worth a PyPI
dependency: do you really want to have an external dependency just to
give out warnings for stuff that you wish wasn't in your codebase in
the first place?

Not needing to reinvent the wheel in many projects is useful but
actually the real advantages of standardising deprecation as I see it
are things like:

1. If there is a standardised attribute like __deprecated__ then
introspective tools can avoid triggering the warning.
2. Static/introspective analysis could determine if deprecated
functionality is potentially being used even if e.g. the function does
not happen to be called during the test suite.
3. The attribute could expose useful information in a standardised way
like version_deprecated, version_will_be_removed, use_instead,
url_for_more_information etc so that smart editors and other tooling
can check this (perhaps in tandem with checking a requirements.txt or
something).

--
Oscar
_______________________________________________
Python-ideas mailing list -- python...@python.org
To unsubscribe send an email to python-id...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python...@python.org/message/6Q42Y253RVAZ77SLAM2WCDVB5KKLH5YN/

Marco Sulla

unread,
Aug 15, 2021, 6:35:54 PM8/15/21
to Oscar Benjamin, Leonardo Freua, Python-Ideas
@Deprecated is used in Java and I find it very easy to use. Remember
you can also use parameters in decorators.....
_______________________________________________
Python-ideas mailing list -- python...@python.org
To unsubscribe send an email to python-id...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python...@python.org/message/OBTRKWOMEE3TUF2MZKWVZSAAV4OFQQ47/

Cameron Simpson

unread,
Aug 15, 2021, 10:14:48 PM8/15/21
to Python-Ideas
On 29Jul2021 15:58, Paul Moore <p.f....@gmail.com> wrote:
>On Thu, 29 Jul 2021 at 15:39, Leonardo Freua
><leonardo.ba...@gmail.com> wrote:
>> Would it be interesting to create a @deprecated decorator to avoid
>> adding warnings.warn("deprecation message", DeprecationWarning,
>> stacklevel=2) in methods body?
>
>I don't see the value personally.

I do. Like various others here I wrote myself such a decorator long ago.
It was named @OBSOLETE and was by no means as complete as, say, the
Deprecated PyPI module. But the use case was the same:

@OBSOLETE
def old_func():

A decorator is concise and right up front in the code. As others have
mentioned, it becomes easy for tooling to see or use. (Not mine, it is a
miracle of crudity, just issuing warning() calls when the function is
first called from some place - it does at least say where the call was
made from.)

[...]
>Why would it be "interesting"? I don't see any practical advantage,
>and as soon as you need any form of logic you have to rewrite, so why
>bother?

That's the case for any presupplied convenience. But it covers off a lot
of the common cases to my mind.

The idea's a definite +1 from me.

Cheers,
Cameron Simpson <c...@cskk.id.au>
_______________________________________________
Python-ideas mailing list -- python...@python.org
To unsubscribe send an email to python-id...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python...@python.org/message/YG2VHW6RJNHGD6ITJNAQNWHS6LAR7GWR/
Reply all
Reply to author
Forward
0 new messages