mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
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:
parent
3973f27ee4
commit
2504ad0b99
7 changed files with 70 additions and 62 deletions
|
@ -7,7 +7,7 @@ import functools
|
|||
import os
|
||||
import pathlib
|
||||
import traceback
|
||||
from typing import Any, Callable, Union, no_type_check
|
||||
from typing import TYPE_CHECKING, Any, Callable, Union
|
||||
|
||||
from anki._vendor import stringcase
|
||||
|
||||
|
@ -66,21 +66,20 @@ class DeprecatedNamesMixin:
|
|||
# deprecated name -> [new internal name, new name shown to user]
|
||||
_deprecated_attributes: dict[str, tuple[str, str | None]] = {}
|
||||
|
||||
# the @no_type_check lines are required to prevent mypy allowing arbitrary
|
||||
# attributes on the consuming class
|
||||
# TYPE_CHECKING check is required for https://github.com/python/mypy/issues/13319
|
||||
if not TYPE_CHECKING:
|
||||
|
||||
@no_type_check
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
try:
|
||||
remapped, replacement = _get_remapped_and_replacement(self, name)
|
||||
out = getattr(self, remapped)
|
||||
except AttributeError:
|
||||
raise AttributeError(
|
||||
f"'{self.__class__.__name__}' object has no attribute '{name}'"
|
||||
) from None
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
try:
|
||||
remapped, replacement = _get_remapped_and_replacement(self, name)
|
||||
out = getattr(self, remapped)
|
||||
except AttributeError:
|
||||
raise AttributeError(
|
||||
f"'{self.__class__.__name__}' object has no attribute '{name}'"
|
||||
) from None
|
||||
|
||||
_print_replacement_warning(name, replacement)
|
||||
return out
|
||||
_print_replacement_warning(name, replacement)
|
||||
return out
|
||||
|
||||
@classmethod
|
||||
def register_deprecated_aliases(cls, **kwargs: DeprecatedAliasTarget) -> None:
|
||||
|
@ -123,9 +122,9 @@ class DeprecatedNamesMixinForModule:
|
|||
_deprecated_names.register_deprecated_aliases(...
|
||||
_deprecated_names.register_deprecated_attributes(...
|
||||
|
||||
@no_type_check
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
if not TYPE_CHECKING:
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
```
|
||||
See DeprecatedNamesMixin for more documentation.
|
||||
"""
|
||||
|
@ -135,19 +134,20 @@ class DeprecatedNamesMixinForModule:
|
|||
self._deprecated_aliases: dict[str, str] = {}
|
||||
self._deprecated_attributes: dict[str, tuple[str, str | None]] = {}
|
||||
|
||||
@no_type_check
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
try:
|
||||
remapped, replacement = _get_remapped_and_replacement(self, name)
|
||||
out = self.module_globals[remapped]
|
||||
except (AttributeError, KeyError):
|
||||
raise AttributeError(
|
||||
f"Module '{self.module_globals['__name__']}' has no attribute '{name}'"
|
||||
) from None
|
||||
if not TYPE_CHECKING:
|
||||
|
||||
# skip an additional frame as we are called from the module `__getattr__`
|
||||
_print_replacement_warning(name, replacement, frame=2)
|
||||
return out
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
try:
|
||||
remapped, replacement = _get_remapped_and_replacement(self, name)
|
||||
out = self.module_globals[remapped]
|
||||
except (AttributeError, KeyError):
|
||||
raise AttributeError(
|
||||
f"Module '{self.module_globals['__name__']}' has no attribute '{name}'"
|
||||
) from None
|
||||
|
||||
# skip an additional frame as we are called from the module `__getattr__`
|
||||
_print_replacement_warning(name, replacement, frame=2)
|
||||
return out
|
||||
|
||||
def register_deprecated_aliases(self, **kwargs: DeprecatedAliasTarget) -> None:
|
||||
self._deprecated_aliases = {k: _target_to_string(v) for k, v in kwargs.items()}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import Any, NewType, no_type_check
|
||||
from typing import TYPE_CHECKING, Any, NewType
|
||||
|
||||
from anki._legacy import DeprecatedNamesMixinForModule
|
||||
|
||||
|
@ -132,6 +132,7 @@ def new_card_scheduling_labels(
|
|||
_deprecated_names = DeprecatedNamesMixinForModule(globals())
|
||||
|
||||
|
||||
@no_type_check
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
if not TYPE_CHECKING:
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
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:
|
||||
import anki
|
||||
|
@ -590,15 +590,18 @@ DeckManager.register_deprecated_aliases(
|
|||
)
|
||||
|
||||
|
||||
@no_type_check
|
||||
def __getattr__(name):
|
||||
if name == "defaultDeck":
|
||||
print_deprecation_warning(
|
||||
"defaultDeck is deprecated; call decks.id() without it"
|
||||
)
|
||||
return 0
|
||||
elif name == "defaultDynamicDeck":
|
||||
print_deprecation_warning("defaultDynamicDeck is replaced with new_filtered()")
|
||||
return 1
|
||||
else:
|
||||
raise AttributeError(f"module {__name__} has no attribute {name}")
|
||||
if not TYPE_CHECKING:
|
||||
|
||||
def __getattr__(name):
|
||||
if name == "defaultDeck":
|
||||
print_deprecation_warning(
|
||||
"defaultDeck is deprecated; call decks.id() without it"
|
||||
)
|
||||
return 0
|
||||
elif name == "defaultDynamicDeck":
|
||||
print_deprecation_warning(
|
||||
"defaultDynamicDeck is replaced with new_filtered()"
|
||||
)
|
||||
return 1
|
||||
else:
|
||||
raise AttributeError(f"module {__name__} has no attribute {name}")
|
||||
|
|
|
@ -6,7 +6,7 @@ from __future__ import annotations
|
|||
import locale
|
||||
import re
|
||||
import weakref
|
||||
from typing import Any, no_type_check
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import anki
|
||||
import anki._backend
|
||||
|
@ -224,6 +224,7 @@ def with_collapsed_whitespace(string: str) -> str:
|
|||
_deprecated_names = DeprecatedNamesMixinForModule(globals())
|
||||
|
||||
|
||||
@no_type_check
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
if not TYPE_CHECKING:
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable, no_type_check
|
||||
from typing import TYPE_CHECKING, Any, Callable
|
||||
|
||||
import anki.collection
|
||||
import anki.models
|
||||
|
@ -115,6 +115,7 @@ _deprecated_names.register_deprecated_attributes(
|
|||
)
|
||||
|
||||
|
||||
@no_type_check
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
if not TYPE_CHECKING:
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
|
|
|
@ -16,7 +16,7 @@ import tempfile
|
|||
import time
|
||||
from contextlib import contextmanager
|
||||
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.dbproxy import DBProxy
|
||||
|
@ -324,6 +324,7 @@ _deprecated_names.register_deprecated_aliases(
|
|||
_deprecated_names.register_deprecated_attributes(json=((_json, "_json"), None))
|
||||
|
||||
|
||||
@no_type_check
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
if not TYPE_CHECKING:
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
|
|
|
@ -9,7 +9,7 @@ import subprocess
|
|||
import sys
|
||||
from functools import partial, wraps
|
||||
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
|
||||
|
||||
|
@ -1243,6 +1243,7 @@ class KeyboardModifiersPressed:
|
|||
_deprecated_names = DeprecatedNamesMixinForModule(globals())
|
||||
|
||||
|
||||
@no_type_check
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
if not TYPE_CHECKING:
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
return _deprecated_names.__getattr__(name)
|
||||
|
|
Loading…
Reference in a new issue