Eric Traut and I recently discussed this topic for pyright: https://github.com/microsoft/pyright/discussions/2300. I'm definitely on board with the idea of marking deprecations in a way that is visible to type checkers.

El jue, 16 sept 2021 a las 11:22, Yilei Yang (<yileiyang9@gmail.com>) escribió:
Hi everyone,

I'm working on a proposal to introduce a deprecation library. It is intended to be used at type checking time, and will be supported by pytype. The primary benefit (over DeprecationWarning at runtime) is to provide deprecation signals at static analysis time (think type checking, linting, IDE support).

**The gist of the deprecation library**
============================

The gist of the deprecation library is to use a data class Deprecated wrapped in a typing.Annotated to mark variables/methods/functions as deprecated. Here are a couple of examples:

# API:
@dataclasses.dataclass
class Deprecated:
  reason: str

# Usages:
my_constant: Annotated[int, Deprecated("my_constant is deprecated.")] = 123
def do_something(name: str,
                 arg: Annotated[Any, Deprecated("`arg` is deprecated.")] = None) -> None:
  # The parameter `arg` is deprecated
def my_function(name: str) -> Annotated[str, Deprecated("my_function is deprecated.")]:
  # Putting Deprecated in the return value type marks the method/function as deprecated.
class MyClass:
  def __init__(self) -> Annotated[None, Deprecated("MyClass is deprecated.")]:
    # Marking __init__ as deprecated makes the class deprecated

It is a no-op at runtime, and per PEP 593 it should be ignored by type checkers that don't support it.

We also intend to include a higher-level decorator that will be turned into the lower-level annotations at type checking time. Here is an example:

# API:
def deprecated(message: str, stacklevel: int = 2) -> Callable[[_T], _T]:
  def decorator(wrapped):
    @functools.wraps(wrapped)
    def wrapper(*args, **kwargs):
      warnings.warn(message, DeprecationWarning, stacklevel=stacklevel)
      return wrapped(*args, **kwargs)
    return wrapper
  return decorator

# Usage:
@deprecated("my_func is deprecated")
def my_func(name):
  print(f"Called my_func with {name}")

At type checking time, it turns the function to def my_func(name) -> Annotated[?, Deprecated("my_func is deprecated")]. ? is either a concrete type if the type checker can infer its return type, or Any if not. At runtime, it raises a DeprecationWarning whenever my_func is called.

**Feedback I'm looking for**
======================

Above isn't very detailed nor a finalized design. But I hope the idea is just clear enough to seek some initial feedback. The things I'm specifically looking for are:

* Would the use of these APIs cause issues for other type checkers? Per my interpretation of PEP 593, it shouldn't. I tested with pytype, mypy, pyright, and pyre, one hiccup I found is pyre thinks annotating __init__ with Annotated[None, Deprecated("")] is an error (Incompatible constructor annotation [17]: __init__ is annotated as returning typing.Annotated[None], but it should return None.)
* Is there interest from other type checkers to support such a deprecation library, so that static analysis tooling can provide better support detecting the use of deprecated APIs?

Thanks,
Yilei
_______________________________________________
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
https://mail.python.org/mailman3/lists/typing-sig.python.org/
Member address: jelle.zijlstra@gmail.com