From dfe3c457e5a5f66f014a73bb7e4cf89be23d39de Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 5 Feb 2021 19:13:55 +1000 Subject: [PATCH] use top level defs for protobuf enum cases While mypy can understand nested references like ConfigBool.Key.COLLAPSE_RECENT, PyCharm doesn't understand the metaclass syntax, and shows the definitions as invalid. --- pylib/.pylintrc | 6 ++++++ pylib/anki/_backend/__init__.py | 4 ++-- pylib/anki/collection.py | 18 +++++++++--------- pylib/anki/lang.py | 4 +++- pylib/anki/rsbackend.py | 2 +- pylib/anki/schedv2.py | 23 +++++++++++------------ pylib/anki/stats.py | 4 ++-- pylib/tests/test_find.py | 10 ++++------ pylib/tests/test_schedv2.py | 6 +++--- qt/.pylintrc | 2 +- qt/aqt/browser.py | 10 +++++----- qt/aqt/previewer.py | 6 +++--- qt/aqt/sidebar.py | 18 +++++++++--------- 13 files changed, 59 insertions(+), 54 deletions(-) diff --git a/pylib/.pylintrc b/pylib/.pylintrc index fd2c5cc6f..ceccc80d3 100644 --- a/pylib/.pylintrc +++ b/pylib/.pylintrc @@ -2,6 +2,12 @@ ignore-patterns=.*_pb2.* persistent = no +[TYPECHECK] +ignored-classes= + FormatTimespanIn, + UnburyCardsInCurrentDeckIn, + BuryOrSuspendCardsIn + [MESSAGES CONTROL] disable=C,R, fixme, diff --git a/pylib/anki/_backend/__init__.py b/pylib/anki/_backend/__init__.py index 445d031e7..d27f32c3a 100644 --- a/pylib/anki/_backend/__init__.py +++ b/pylib/anki/_backend/__init__.py @@ -11,7 +11,7 @@ from anki._backend.generated import RustBackendGenerated from anki.dbproxy import Row as DBRow from anki.dbproxy import ValueForDB from anki.errors import backend_exception_to_pylib -from anki.lang import TR, FormatTimeSpanContext +from anki.lang import TR, FormatTimeSpan from anki.utils import from_json_bytes, to_json_bytes from . import backend_pb2 as pb @@ -88,7 +88,7 @@ class RustBackend(RustBackendGenerated): def format_time_span( self, seconds: float, - context: FormatTimeSpanContext.V = FormatTimeSpanContext.INTERVALS, + context: FormatTimeSpan.Context.V = FormatTimeSpan.INTERVALS, ) -> str: print( "please use col.format_timespan() instead of col.backend.format_time_span()" diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 9eb31b35e..c583c0ab2 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -27,7 +27,7 @@ from anki.consts import * from anki.dbproxy import DBProxy from anki.decks import DeckManager from anki.errors import AnkiError, DBError -from anki.lang import TR, FormatTimeSpanContext +from anki.lang import TR, FormatTimeSpan from anki.media import MediaManager, media_paths_from_col_path from anki.models import ModelManager from anki.notes import Note @@ -50,11 +50,11 @@ MediaSyncProgress = _pb.MediaSyncProgress FullSyncProgress = _pb.FullSyncProgress NormalSyncProgress = _pb.NormalSyncProgress DatabaseCheckProgress = _pb.DatabaseCheckProgress -ConfigBoolKey = _pb.ConfigBool.Key # pylint: disable=no-member +ConfigBool = _pb.ConfigBool EmptyCardsReport = _pb.EmptyCardsReport NoteWithEmptyCards = _pb.NoteWithEmptyCards GraphPreferences = _pb.GraphPreferences -BuiltinSortKind = _pb.SortOrder.Builtin.Kind # pylint: disable=no-member +BuiltinSort = _pb.SortOrder.Builtin Preferences = _pb.Preferences @@ -117,7 +117,7 @@ class Collection: def format_timespan( self, seconds: float, - context: FormatTimeSpanContext.V = FormatTimeSpanContext.INTERVALS, + context: FormatTimeSpan.Context.V = FormatTimeSpan.INTERVALS, ) -> str: return self._backend.format_timespan(seconds=seconds, context=context) @@ -461,7 +461,7 @@ class Collection: def find_cards( self, query: str, - order: Union[bool, str, BuiltinSortKind.V] = False, + order: Union[bool, str, BuiltinSort.Kind.V] = False, reverse: bool = False, ) -> Sequence[int]: if isinstance(order, str): @@ -554,9 +554,9 @@ class Collection: term = self._backend.filter_to_search(term) searches.append(term) if match_any: - sep = _pb.ConcatenateSearchesIn.Separator.OR + sep = _pb.ConcatenateSearchesIn.OR else: - sep = _pb.ConcatenateSearchesIn.Separator.AND + sep = _pb.ConcatenateSearchesIn.AND search_string = self._backend.concatenate_searches(sep=sep, searches=searches) if negate: search_string = self._backend.negate_search(search_string) @@ -586,10 +586,10 @@ class Collection: "This is a debugging aid. Prefer .get_config() when you know the key you need." return from_json_bytes(self._backend.get_all_config()) - def get_config_bool(self, key: ConfigBoolKey.V) -> bool: + def get_config_bool(self, key: ConfigBool.Key.V) -> bool: return self._backend.get_config_bool(key) - def set_config_bool(self, key: ConfigBoolKey.V, value: bool) -> None: + def set_config_bool(self, key: ConfigBool.Key.V, value: bool) -> None: self.setMod() self._backend.set_config_bool(key=key, value=value) diff --git a/pylib/anki/lang.py b/pylib/anki/lang.py index 0528e1682..b65546be2 100644 --- a/pylib/anki/lang.py +++ b/pylib/anki/lang.py @@ -13,7 +13,9 @@ import anki._backend.fluent_pb2 as _fluent_pb # public exports TR = _fluent_pb.FluentString -FormatTimeSpanContext = _pb.FormatTimespanIn.Context # pylint: disable=no-member +FormatTimeSpan = _pb.FormatTimespanIn +# legacy alias used by add-ons +FormatTimeSpanContext = FormatTimeSpan langs = sorted( [ diff --git a/pylib/anki/rsbackend.py b/pylib/anki/rsbackend.py index 9657a7731..d67ebad0f 100644 --- a/pylib/anki/rsbackend.py +++ b/pylib/anki/rsbackend.py @@ -9,4 +9,4 @@ from anki.decks import DeckTreeNode from anki.errors import InvalidInput, NotFoundError -from anki.lang import FormatTimeSpanContext +from anki.lang import FormatTimeSpan diff --git a/pylib/anki/schedv2.py b/pylib/anki/schedv2.py index 38b1276ce..0e6620ae1 100644 --- a/pylib/anki/schedv2.py +++ b/pylib/anki/schedv2.py @@ -15,16 +15,15 @@ from anki import hooks from anki.cards import Card from anki.consts import * from anki.decks import Deck, DeckConfig, DeckManager, DeckTreeNode, QueueConfig -from anki.lang import FormatTimeSpanContext +from anki.lang import FormatTimeSpan from anki.notes import Note from anki.utils import from_json_bytes, ids2str, intTime CongratsInfo = _pb.CongratsInfoOut CountsForDeckToday = _pb.CountsForDeckTodayOut SchedTimingToday = _pb.SchedTimingTodayOut - -UnburyCurrentDeckMode = _pb.UnburyCardsInCurrentDeckIn.Mode # pylint:disable=no-member -BuryOrSuspendMode = _pb.BuryOrSuspendCardsIn.Mode # pylint:disable=no-member +UnburyCurrentDeck = _pb.UnburyCardsInCurrentDeckIn +BuryOrSuspend = _pb.BuryOrSuspendCardsIn # card types: 0=new, 1=lrn, 2=rev, 3=relrn # queue types: 0=new, 1=(re)lrn, 2=rev, 3=day (re)lrn, @@ -1254,7 +1253,7 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe ivl_secs = self.nextIvl(card, ease) if not ivl_secs: return self.col.tr(TR.SCHEDULING_END) - s = self.col.format_timespan(ivl_secs, FormatTimeSpanContext.ANSWER_BUTTONS) + s = self.col.format_timespan(ivl_secs, FormatTimeSpan.ANSWER_BUTTONS) if ivl_secs < self.col.conf["collapseTime"]: s = "<" + s return s @@ -1315,20 +1314,20 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe def unbury_cards_in_current_deck( self, - mode: UnburyCurrentDeckMode.V = UnburyCurrentDeckMode.ALL, + mode: UnburyCurrentDeck.Mode.V = UnburyCurrentDeck.ALL, ) -> None: self.col._backend.unbury_cards_in_current_deck(mode) def suspend_cards(self, ids: Sequence[int]) -> None: self.col._backend.bury_or_suspend_cards( - card_ids=ids, mode=BuryOrSuspendMode.SUSPEND + card_ids=ids, mode=BuryOrSuspend.SUSPEND ) def bury_cards(self, ids: Sequence[int], manual: bool = True) -> None: if manual: - mode = BuryOrSuspendMode.BURY_USER + mode = BuryOrSuspend.BURY_USER else: - mode = BuryOrSuspendMode.BURY_SCHED + mode = BuryOrSuspend.BURY_SCHED self.col._backend.bury_or_suspend_cards(card_ids=ids, mode=mode) def bury_note(self, note: Note) -> None: @@ -1351,11 +1350,11 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe "please use unbury_cards_in_current_deck() instead of unburyCardsForDeck()" ) if type == "all": - mode = UnburyCurrentDeckMode.ALL + mode = UnburyCurrentDeck.ALL elif type == "manual": - mode = UnburyCurrentDeckMode.USER_ONLY + mode = UnburyCurrentDeck.USER_ONLY else: # elif type == "siblings": - mode = UnburyCurrentDeckMode.SCHED_ONLY + mode = UnburyCurrentDeck.SCHED_ONLY self.unbury_cards_in_current_deck(mode) unsuspendCards = unsuspend_cards diff --git a/pylib/anki/stats.py b/pylib/anki/stats.py index 789d5e852..ad844305d 100644 --- a/pylib/anki/stats.py +++ b/pylib/anki/stats.py @@ -10,7 +10,7 @@ from typing import Any, Dict, List, Optional, Sequence, Tuple, Union import anki from anki.consts import * -from anki.lang import TR, FormatTimeSpanContext +from anki.lang import TR, FormatTimeSpan from anki.utils import ids2str # Card stats @@ -46,7 +46,7 @@ class CardStats: return time.strftime("%Y-%m-%d", time.localtime(tm)) def time(self, tm: float) -> str: - return self.col.format_timespan(tm, context=FormatTimeSpanContext.PRECISE) + return self.col.format_timespan(tm, context=FormatTimeSpan.PRECISE) # Collection stats diff --git a/pylib/tests/test_find.py b/pylib/tests/test_find.py index b3fd51dc3..264f2b3db 100644 --- a/pylib/tests/test_find.py +++ b/pylib/tests/test_find.py @@ -1,7 +1,7 @@ # coding: utf-8 import pytest -from anki.collection import BuiltinSortKind, ConfigBoolKey +from anki.collection import BuiltinSort, ConfigBool from anki.consts import * from tests.shared import getEmptyCol, isNearCutoff @@ -121,16 +121,14 @@ def test_findCards(): col.flush() assert col.findCards("", order=True)[-1] in latestCardIds assert col.findCards("", order=True)[0] == firstCardId - col.set_config_bool(ConfigBoolKey.BROWSER_SORT_BACKWARDS, True) + col.set_config_bool(ConfigBool.BROWSER_SORT_BACKWARDS, True) col.flush() assert col.findCards("", order=True)[0] in latestCardIds assert ( - col.find_cards("", order=BuiltinSortKind.CARD_DUE, reverse=False)[0] - == firstCardId + col.find_cards("", order=BuiltinSort.CARD_DUE, reverse=False)[0] == firstCardId ) assert ( - col.find_cards("", order=BuiltinSortKind.CARD_DUE, reverse=True)[0] - != firstCardId + col.find_cards("", order=BuiltinSort.CARD_DUE, reverse=True)[0] != firstCardId ) # model assert len(col.findCards("note:basic")) == 3 diff --git a/pylib/tests/test_schedv2.py b/pylib/tests/test_schedv2.py index 9417ae9aa..42ddb5c1a 100644 --- a/pylib/tests/test_schedv2.py +++ b/pylib/tests/test_schedv2.py @@ -6,7 +6,7 @@ import time from anki import hooks from anki.consts import * from anki.lang import without_unicode_isolation -from anki.schedv2 import UnburyCurrentDeckMode +from anki.schedv2 import UnburyCurrentDeck from anki.utils import intTime from tests.shared import getEmptyCol as getEmptyColOrig @@ -612,13 +612,13 @@ def test_bury(): col.reset() assert not col.sched.getCard() - col.sched.unbury_cards_in_current_deck(UnburyCurrentDeckMode.USER_ONLY) + col.sched.unbury_cards_in_current_deck(UnburyCurrentDeck.USER_ONLY) c.load() assert c.queue == QUEUE_TYPE_NEW c2.load() assert c2.queue == QUEUE_TYPE_SIBLING_BURIED - col.sched.unbury_cards_in_current_deck(UnburyCurrentDeckMode.SCHED_ONLY) + col.sched.unbury_cards_in_current_deck(UnburyCurrentDeck.SCHED_ONLY) c2.load() assert c2.queue == QUEUE_TYPE_NEW diff --git a/qt/.pylintrc b/qt/.pylintrc index 33e90dfdc..206cb6966 100644 --- a/qt/.pylintrc +++ b/qt/.pylintrc @@ -5,7 +5,7 @@ ignore = forms,hooks_gen.py [TYPECHECK] ignored-modules=win32file,pywintypes,socket,win32pipe,winrt,pyaudio -ignored-classes=SearchTerm +ignored-classes=SearchTerm,ConfigBool [REPORTS] output-format=colorized diff --git a/qt/aqt/browser.py b/qt/aqt/browser.py index 11a71158d..96ea760c9 100644 --- a/qt/aqt/browser.py +++ b/qt/aqt/browser.py @@ -13,7 +13,7 @@ from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union, import aqt import aqt.forms from anki.cards import Card -from anki.collection import Collection, ConfigBoolKey, SearchTerm +from anki.collection import Collection, ConfigBool, SearchTerm from anki.consts import * from anki.errors import InvalidInput from anki.lang import without_unicode_isolation @@ -850,13 +850,13 @@ QTableView {{ gridline-color: {grid} }} # default to descending for non-text fields if type == "noteFld": ord = not ord - self.col.set_config_bool(ConfigBoolKey.BROWSER_SORT_BACKWARDS, ord) + self.col.set_config_bool(ConfigBool.BROWSER_SORT_BACKWARDS, ord) self.col.setMod() self.col.save() self.search() else: - if self.col.get_config_bool(ConfigBoolKey.BROWSER_SORT_BACKWARDS) != ord: - self.col.set_config_bool(ConfigBoolKey.BROWSER_SORT_BACKWARDS, ord) + if self.col.get_config_bool(ConfigBool.BROWSER_SORT_BACKWARDS) != ord: + self.col.set_config_bool(ConfigBool.BROWSER_SORT_BACKWARDS, ord) self.col.setMod() self.col.save() self.model.reverse() @@ -869,7 +869,7 @@ QTableView {{ gridline-color: {grid} }} hh.setSortIndicatorShown(False) return idx = self.model.activeCols.index(type) - if self.col.get_config_bool(ConfigBoolKey.BROWSER_SORT_BACKWARDS): + if self.col.get_config_bool(ConfigBool.BROWSER_SORT_BACKWARDS): ord = Qt.DescendingOrder else: ord = Qt.AscendingOrder diff --git a/qt/aqt/previewer.py b/qt/aqt/previewer.py index e0262562e..983e07aff 100644 --- a/qt/aqt/previewer.py +++ b/qt/aqt/previewer.py @@ -7,7 +7,7 @@ import time from typing import Any, Callable, Optional, Tuple, Union from anki.cards import Card -from anki.collection import ConfigBoolKey +from anki.collection import ConfigBool from aqt import AnkiQt, gui_hooks from aqt.qt import ( QAbstractItemView, @@ -94,7 +94,7 @@ class Previewer(QDialog): both_sides_button.setToolTip(tr(TR.ACTIONS_SHORTCUT_KEY, val="B")) self.bbox.addButton(both_sides_button, QDialogButtonBox.ActionRole) self._show_both_sides = self.mw.col.get_config_bool( - ConfigBoolKey.PREVIEW_BOTH_SIDES + ConfigBool.PREVIEW_BOTH_SIDES ) both_sides_button.setChecked(self._show_both_sides) qconnect(both_sides_button.toggled, self._on_show_both_sides) @@ -221,7 +221,7 @@ class Previewer(QDialog): def _on_show_both_sides(self, toggle: bool) -> None: self._show_both_sides = toggle - self.mw.col.set_config_bool(ConfigBoolKey.PREVIEW_BOTH_SIDES, toggle) + self.mw.col.set_config_bool(ConfigBool.PREVIEW_BOTH_SIDES, toggle) self.mw.col.setMod() if self._state == "answer" and not toggle: self._state = "question" diff --git a/qt/aqt/sidebar.py b/qt/aqt/sidebar.py index a7c2c970e..ec2397461 100644 --- a/qt/aqt/sidebar.py +++ b/qt/aqt/sidebar.py @@ -9,7 +9,7 @@ from enum import Enum, auto from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, cast import aqt -from anki.collection import ConfigBoolKey, SearchTerm +from anki.collection import ConfigBool, SearchTerm from anki.decks import DeckTreeNode from anki.errors import DeckRenameError, InvalidInput from anki.tags import TagTreeNode @@ -526,7 +526,7 @@ class SidebarTreeView(QTreeView): root: SidebarItem, name: TR.V, icon: Union[str, ColoredIcon], - collapse_key: ConfigBoolKey.V, + collapse_key: ConfigBool.Key.V, type: Optional[SidebarItemType] = None, ) -> SidebarItem: def update(expanded: bool) -> None: @@ -557,7 +557,7 @@ class SidebarTreeView(QTreeView): root=root, name=TR.BROWSING_SIDEBAR_SAVED_SEARCHES, icon=icon, - collapse_key=ConfigBoolKey.COLLAPSE_SAVED_SEARCHES, + collapse_key=ConfigBool.COLLAPSE_SAVED_SEARCHES, type=SidebarItemType.SAVED_SEARCH_ROOT, ) @@ -584,7 +584,7 @@ class SidebarTreeView(QTreeView): root=root, name=TR.BROWSING_SIDEBAR_RECENT, icon=icon, - collapse_key=ConfigBoolKey.COLLAPSE_RECENT, + collapse_key=ConfigBool.COLLAPSE_RECENT, type=SidebarItemType.FLAG_ROOT, ) type = SidebarItemType.FLAG @@ -646,7 +646,7 @@ class SidebarTreeView(QTreeView): root=root, name=TR.BROWSING_SIDEBAR_CARD_STATE, icon=icon, - collapse_key=ConfigBoolKey.COLLAPSE_CARD_STATE, + collapse_key=ConfigBool.COLLAPSE_CARD_STATE, type=SidebarItemType.CARD_STATE_ROOT, ) type = SidebarItemType.CARD_STATE @@ -693,7 +693,7 @@ class SidebarTreeView(QTreeView): root=root, name=TR.BROWSING_SIDEBAR_FLAGS, icon=icon, - collapse_key=ConfigBoolKey.COLLAPSE_FLAGS, + collapse_key=ConfigBool.COLLAPSE_FLAGS, type=SidebarItemType.FLAG_ROOT, ) type = SidebarItemType.FLAG @@ -771,7 +771,7 @@ class SidebarTreeView(QTreeView): root=root, name=TR.BROWSING_SIDEBAR_TAGS, icon=icon, - collapse_key=ConfigBoolKey.COLLAPSE_TAGS, + collapse_key=ConfigBool.COLLAPSE_TAGS, type=SidebarItemType.TAG_ROOT, ) render(root, tree.children) @@ -810,7 +810,7 @@ class SidebarTreeView(QTreeView): root=root, name=TR.BROWSING_SIDEBAR_DECKS, icon=icon, - collapse_key=ConfigBoolKey.COLLAPSE_DECKS, + collapse_key=ConfigBool.COLLAPSE_DECKS, type=SidebarItemType.DECK_ROOT, ) render(root, tree.children) @@ -824,7 +824,7 @@ class SidebarTreeView(QTreeView): root=root, name=TR.BROWSING_SIDEBAR_NOTETYPES, icon=icon, - collapse_key=ConfigBoolKey.COLLAPSE_NOTETYPES, + collapse_key=ConfigBool.COLLAPSE_NOTETYPES, type=SidebarItemType.NOTETYPE_ROOT, )