add a partial Python implementation of the backend

This commit is contained in:
Damien Elmes 2020-01-06 12:24:47 +10:00
parent 8df5f49c52
commit 99141d9dfb
5 changed files with 91 additions and 7 deletions

View file

@ -16,7 +16,6 @@ from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
import anki.find
import anki.latex # sets up hook
import anki.template
from anki.backend import Backend
from anki.cards import Card
from anki.consts import *
from anki.db import DB
@ -27,6 +26,7 @@ from anki.lang import _, ngettext
from anki.media import MediaManager
from anki.models import ModelManager
from anki.notes import Note
from anki.rsbackend import RustBackend
from anki.sched import Scheduler as V1Scheduler
from anki.schedv2 import Scheduler as V2Scheduler
from anki.sound import stripSounds
@ -75,12 +75,12 @@ class _Collection:
ls: int
conf: Dict[str, Any]
_undo: List[Any]
backend: Backend
backend: RustBackend
def __init__(
self,
db: DB,
backend: Backend,
backend: RustBackend,
server: Optional["anki.storage.ServerData"] = None,
log: bool = False,
) -> None:

82
pylib/anki/pybackend.py Normal file
View file

@ -0,0 +1,82 @@
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
"""
A Python implementation of some backend commands.
Unimplemented commands will be forwarded on to the Rust backend.
"""
from typing import Tuple, Any, Dict
import anki # pylint: disable=unused-import
import anki.backend_pb2 as pb
class PythonBackend:
def __init__(self, col: "anki.storage._Collection"):
self.col = col
def run_command_bytes(self, input: bytes) -> bytes:
pb_input = pb.BackendInput()
pb_input.ParseFromString(input)
pb_output = self.run_command(pb_input)
output = pb_output.SerializeToString()
return output
def run_command(self, input: pb.BackendInput) -> pb.BackendOutput:
kind = input.WhichOneof("value")
handler = getattr(self, kind, None)
# run the equivalent of the following, based on available method names
# if kind == "deck_tree":
# return pb.BackendOutput(deck_tree=self.deck_tree(input.deck_tree))
if handler is not None:
input_variant = getattr(input, kind)
output_variant = handler(input_variant)
output_args: Dict[str, Any] = {kind: output_variant}
output = pb.BackendOutput(**output_args)
return output
else:
# forward any unknown commands onto the Rust backend
return self.col.backend._run_command(input)
def deck_tree(self, _input: pb.Empty) -> pb.DeckTreeOut:
native = self.col.sched.deckDueTree()
return native_deck_tree_to_proto(native)
def find_cards(self, input: pb.FindCardsIn) -> pb.FindCardsOut:
cids = self.col.findCards(input.search)
return pb.FindCardsOut(card_ids=cids)
def browser_rows(self, input: pb.BrowserRowsIn) -> pb.BrowserRowsOut:
sort_fields = []
for cid in input.card_ids:
sort_fields.append(
self.col.db.scalar(
"select sfld from notes n,cards c where n.id=c.nid and c.id=?", cid
)
)
return pb.BrowserRowsOut(sort_fields=sort_fields)
def native_deck_tree_to_proto(native):
top = pb.DeckTreeNode(children=[native_deck_node_to_proto(c) for c in native])
out = pb.DeckTreeOut(top=top)
return out
def native_deck_node_to_proto(native: Tuple) -> pb.DeckTreeNode:
return pb.DeckTreeNode(
# fixme: need to decide whether full list
# should be included or just tail element
names=[native[0]],
deck_id=native[1],
review_count=native[2],
learn_count=native[3],
new_count=native[4],
children=[native_deck_node_to_proto(c) for c in native[5]],
# fixme: currently hard-coded
collapsed=False,
)

View file

@ -1,3 +1,5 @@
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
# pylint: skip-file
from typing import Dict, List
@ -44,7 +46,7 @@ def proto_template_reqs_to_legacy(
return legacy_reqs
class Backend:
class RustBackend:
def __init__(self, path: str):
self._backend = ankirspy.Backend(path)

View file

@ -12,11 +12,11 @@ from operator import itemgetter
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
import anki # pylint: disable=unused-import
from anki.backend import SchedTimingToday
from anki.cards import Card
from anki.consts import *
from anki.hooks import runHook
from anki.lang import _
from anki.rsbackend import SchedTimingToday
from anki.utils import fmtTimeSpan, ids2str, intTime
# card types: 0=new, 1=lrn, 2=rev, 3=relrn

View file

@ -7,11 +7,11 @@ import os
import re
from typing import Any, Dict, Optional, Tuple
from anki.backend import Backend
from anki.collection import _Collection
from anki.consts import *
from anki.db import DB
from anki.lang import _
from anki.rsbackend import RustBackend
from anki.stdmodels import (
addBasicModel,
addBasicTypingModel,
@ -30,7 +30,7 @@ def Collection(
path: str, lock: bool = True, server: Optional[ServerData] = None, log: bool = False
) -> _Collection:
"Open a new or existing collection. Path must be unicode."
backend = Backend(path)
backend = RustBackend(path)
# fixme: this call is temporarily here to ensure the brige is working
# on all platforms, and should be removed in a future beta
assert backend.plus_one(5) == 6