Skip to content

--python-version 3.9 should warn about generic usages #10971

Open

Description

@rr-

Feature

From the Python documentation:

The redundant types are deprecated as of Python 3.9 but no deprecation warnings will be issued by the interpreter. It is expected that type checkers will flag the deprecated types when the checked program targets Python 3.9 or newer.

However that does not seem to be the case with mypy even if it's run with --python-version 3.9.

Pitch

Currently there is no way to learn about these deprecation warnings in automated way, because they're not issued by anything, which makes making the code future-proof a hassle.

Activity

  1. rr- commented on Aug 11, 2021

    @rr-
    Author

    Here's an initial patch that adds such warnings and breaks half of the Python ecosystem

    diff --git a/mypy/checker.py b/mypy/checker.py
    index ba020f5d9..37063f9e2 100644
    --- a/mypy/checker.py
    +++ b/mypy/checker.py
    @@ -1958,6 +1958,52 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
         def visit_import_from(self, node: ImportFrom) -> None:
             self.check_import(node)
     
    +        if (self.options.python_version[0] > 3 or self.options.python_version[0] == 3 and self.options.python_version[1] >= 9) and node.id == 'typing':
    +            deprecated_imports = {
    +                'Tuple': 'builtins.tuple',
    +                'Callable': 'collections.abc.Callable',
    +                'Type': 'builtins.type',
    +                'Dict': 'builtins.dict',
    +                'List': 'builtins.list',
    +                'Set': 'builtins.set',
    +                'FrozenSet': 'builtins.frozenset',
    +                'DefaultDict': 'collections.defaultdict',
    +                'OrderedDict': 'collections.OrderedDict',
    +                'ChainMap': 'collections.ChainMap',
    +                'Counter': 'collections.Counter',
    +                'Deque': 'collections.Deque',
    +                'Pattern': 're.Pattern',
    +                'Match': 're.Match',
    +                'AbstractSet': 'collections.abc.Set',
    +                'ByteString': 'collections.abc.ByteString',
    +                'Collection': 'collections.abc.Collection',
    +                'Container': 'collections.abc.Container',
    +                'ItemsView': 'collections.abc.ItemsView',
    +                'KeysView': 'collections.abc.KeysView',
    +                'Mapping': 'collections.abc.Mapping',
    +                'MappingView': 'collections.abc.MappingView',
    +                'MutableMapping': 'collections.abc.MutableMapping',
    +                'MutableSequence': 'collections.abc.MutableSequence',
    +                'MutableSet': 'collections.abc.MutableSet',
    +                'Sequence': 'collections.abc.Sequence',
    +                'ValuesView': 'collections.abc.ValuesView',
    +                'Iterable': 'collections.abc.Iterable',
    +                'Iterator': 'collections.abc.Iterator',
    +                'Generator': 'collections.abc.Generator',
    +                'Reversible': 'collections.abc.Reversible',
    +                'Coroutine': 'collections.abc.Coroutine',
    +                'AsyncGenerator': 'collections.abc.AsyncGenerator',
    +                'AsyncIterable': 'collections.abc.AsyncIterable',
    +                'AsyncIterator': 'collections.abc.AsyncIterator',
    +                'Awaitable': 'collections.abc.Awaitable',
    +                'ContextManager': 'collections.abc.AbstractContextManager',
    +                'AsyncContextManager': 'collections.abc.AbstractAsyncContextManager',
    +            }
    +            imported_names = [name[0] for name in node.names]
    +            for source, target in deprecated_imports.items():
    +                if source in imported_names:
    +                    self.fail(message_registry.TYPE_DEPRECATED_IN_FAVOR_OF_PY39.format(node.id + "." + source, target), node)
    +
         def visit_import_all(self, node: ImportAll) -> None:
             self.check_import(node)
     
    diff --git a/mypy/message_registry.py b/mypy/message_registry.py
    index 48b53336f..648e5a16e 100644
    --- a/mypy/message_registry.py
    +++ b/mypy/message_registry.py
    @@ -73,6 +73,7 @@ RETURN_TYPE_CANNOT_BE_CONTRAVARIANT: Final = (
     FUNCTION_PARAMETER_CANNOT_BE_COVARIANT: Final = (
         "Cannot use a covariant type variable as a parameter"
     )
    +TYPE_DEPRECATED_IN_FAVOR_OF_PY39: Final = "Type {} deprecated in favor of {} since Python 3.9"
     INCOMPATIBLE_IMPORT_OF: Final = "Incompatible import of"
     FUNCTION_TYPE_EXPECTED: Final = "Function is missing a type annotation"
     ONLY_CLASS_APPLICATION: Final = "Type application is only supported for generic classes"
    diff --git a/mypyc/test-data/run-imports.test b/mypyc/test-data/run-imports.test
    index 0957d1b2b..bf848d3e9 100644
    --- a/mypyc/test-data/run-imports.test
    +++ b/mypyc/test-data/run-imports.test
    @@ -192,3 +192,9 @@ a.x = 10
     x = 20
     [file driver.py]
     import native
    +
    +[case testPy39TypingImportDeprecationWarnings]
    +# flags: --python-version 3.9
    +from typing import Iterable
    +[out]
    +testPy39TypingImportDeprecationWarnings.py:1: note: Type typing.Iterable deprecated in favor of collections.abc.Iterable since Python 3.9
  2. added a commit that references this issue on Aug 14, 2021
  3. Avasam commented on May 18, 2026

    @Avasam
    SponsorContributor
  4. JelleZijlstra commented on May 18, 2026

    @JelleZijlstra
    Member

    This is also relevant to newer versions, right?

    Though in practice, linters (e.g. Ruff, pyupgrade) warn about this instead, so there isn't a strong need for mypy to do it too.

  5. Avasam commented on May 18, 2026

    @Avasam
    SponsorContributor

    For class and function symbols typeshed can add deprecated decorators (even if not decorated as such at runtime) added in Python 3.13 (but backportable for static typing)
    and mypy has https://mypy.readthedocs.io/en/stable/error_code_list2.html#check-that-imported-or-used-feature-is-deprecated-deprecated

    So yeah, I don't think there's any special casing needed from mypy anymore.

  6. JelleZijlstra commented on May 18, 2026

    @JelleZijlstra
    Member

    We can't add deprecated decorators for things like typing.List in typeshed though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions