Fix mypy not picking up on missing attributes

Behaviour changed in recent releases:
https://github.com/python/mypy/issues/13319
This commit is contained in:
Damien Elmes 2022-11-04 14:56:36 +10:00
parent 3973f27ee4
commit 2504ad0b99
7 changed files with 70 additions and 62 deletions

View file

@ -7,7 +7,7 @@ import functools
import os import os
import pathlib import pathlib
import traceback import traceback
from typing import Any, Callable, Union, no_type_check from typing import TYPE_CHECKING, Any, Callable, Union
from anki._vendor import stringcase from anki._vendor import stringcase
@ -66,10 +66,9 @@ class DeprecatedNamesMixin:
# deprecated name -> [new internal name, new name shown to user] # deprecated name -> [new internal name, new name shown to user]
_deprecated_attributes: dict[str, tuple[str, str | None]] = {} _deprecated_attributes: dict[str, tuple[str, str | None]] = {}
# the @no_type_check lines are required to prevent mypy allowing arbitrary # TYPE_CHECKING check is required for https://github.com/python/mypy/issues/13319
# attributes on the consuming class if not TYPE_CHECKING:
@no_type_check
def __getattr__(self, name: str) -> Any: def __getattr__(self, name: str) -> Any:
try: try:
remapped, replacement = _get_remapped_and_replacement(self, name) remapped, replacement = _get_remapped_and_replacement(self, name)
@ -123,7 +122,7 @@ class DeprecatedNamesMixinForModule:
_deprecated_names.register_deprecated_aliases(... _deprecated_names.register_deprecated_aliases(...
_deprecated_names.register_deprecated_attributes(... _deprecated_names.register_deprecated_attributes(...
@no_type_check if not TYPE_CHECKING:
def __getattr__(name: str) -> Any: def __getattr__(name: str) -> Any:
return _deprecated_names.__getattr__(name) return _deprecated_names.__getattr__(name)
``` ```
@ -135,7 +134,8 @@ class DeprecatedNamesMixinForModule:
self._deprecated_aliases: dict[str, str] = {} self._deprecated_aliases: dict[str, str] = {}
self._deprecated_attributes: dict[str, tuple[str, str | None]] = {} self._deprecated_attributes: dict[str, tuple[str, str | None]] = {}
@no_type_check if not TYPE_CHECKING:
def __getattr__(self, name: str) -> Any: def __getattr__(self, name: str) -> Any:
try: try:
remapped, replacement = _get_remapped_and_replacement(self, name) remapped, replacement = _get_remapped_and_replacement(self, name)

View file

@ -4,7 +4,7 @@
from __future__ import annotations from __future__ import annotations
import sys import sys
from typing import Any, NewType, no_type_check from typing import TYPE_CHECKING, Any, NewType
from anki._legacy import DeprecatedNamesMixinForModule from anki._legacy import DeprecatedNamesMixinForModule
@ -132,6 +132,7 @@ def new_card_scheduling_labels(
_deprecated_names = DeprecatedNamesMixinForModule(globals()) _deprecated_names = DeprecatedNamesMixinForModule(globals())
@no_type_check if not TYPE_CHECKING:
def __getattr__(name: str) -> Any: def __getattr__(name: str) -> Any:
return _deprecated_names.__getattr__(name) return _deprecated_names.__getattr__(name)

View file

@ -4,7 +4,7 @@
from __future__ import annotations from __future__ import annotations
import copy import copy
from typing import TYPE_CHECKING, Any, Iterable, NewType, Sequence, no_type_check from typing import TYPE_CHECKING, Any, Iterable, NewType, Sequence
if TYPE_CHECKING: if TYPE_CHECKING:
import anki import anki
@ -590,7 +590,8 @@ DeckManager.register_deprecated_aliases(
) )
@no_type_check if not TYPE_CHECKING:
def __getattr__(name): def __getattr__(name):
if name == "defaultDeck": if name == "defaultDeck":
print_deprecation_warning( print_deprecation_warning(
@ -598,7 +599,9 @@ def __getattr__(name):
) )
return 0 return 0
elif name == "defaultDynamicDeck": elif name == "defaultDynamicDeck":
print_deprecation_warning("defaultDynamicDeck is replaced with new_filtered()") print_deprecation_warning(
"defaultDynamicDeck is replaced with new_filtered()"
)
return 1 return 1
else: else:
raise AttributeError(f"module {__name__} has no attribute {name}") raise AttributeError(f"module {__name__} has no attribute {name}")

View file

@ -6,7 +6,7 @@ from __future__ import annotations
import locale import locale
import re import re
import weakref import weakref
from typing import Any, no_type_check from typing import TYPE_CHECKING, Any
import anki import anki
import anki._backend import anki._backend
@ -224,6 +224,7 @@ def with_collapsed_whitespace(string: str) -> str:
_deprecated_names = DeprecatedNamesMixinForModule(globals()) _deprecated_names = DeprecatedNamesMixinForModule(globals())
@no_type_check if not TYPE_CHECKING:
def __getattr__(name: str) -> Any: def __getattr__(name: str) -> Any:
return _deprecated_names.__getattr__(name) return _deprecated_names.__getattr__(name)

View file

@ -3,7 +3,7 @@
from __future__ import annotations from __future__ import annotations
from typing import Any, Callable, no_type_check from typing import TYPE_CHECKING, Any, Callable
import anki.collection import anki.collection
import anki.models import anki.models
@ -115,6 +115,7 @@ _deprecated_names.register_deprecated_attributes(
) )
@no_type_check if not TYPE_CHECKING:
def __getattr__(name: str) -> Any: def __getattr__(name: str) -> Any:
return _deprecated_names.__getattr__(name) return _deprecated_names.__getattr__(name)

View file

@ -16,7 +16,7 @@ import tempfile
import time import time
from contextlib import contextmanager from contextlib import contextmanager
from hashlib import sha1 from hashlib import sha1
from typing import Any, Callable, Iterable, Iterator, no_type_check from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator
from anki._legacy import DeprecatedNamesMixinForModule from anki._legacy import DeprecatedNamesMixinForModule
from anki.dbproxy import DBProxy from anki.dbproxy import DBProxy
@ -324,6 +324,7 @@ _deprecated_names.register_deprecated_aliases(
_deprecated_names.register_deprecated_attributes(json=((_json, "_json"), None)) _deprecated_names.register_deprecated_attributes(json=((_json, "_json"), None))
@no_type_check if not TYPE_CHECKING:
def __getattr__(name: str) -> Any: def __getattr__(name: str) -> Any:
return _deprecated_names.__getattr__(name) return _deprecated_names.__getattr__(name)

View file

@ -9,7 +9,7 @@ import subprocess
import sys import sys
from functools import partial, wraps from functools import partial, wraps
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence, Union, no_type_check from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence, Union
from send2trash import send2trash from send2trash import send2trash
@ -1243,6 +1243,7 @@ class KeyboardModifiersPressed:
_deprecated_names = DeprecatedNamesMixinForModule(globals()) _deprecated_names = DeprecatedNamesMixinForModule(globals())
@no_type_check if not TYPE_CHECKING:
def __getattr__(name: str) -> Any: def __getattr__(name: str) -> Any:
return _deprecated_names.__getattr__(name) return _deprecated_names.__getattr__(name)