From 7ac1fa17e63adadd6ead7b892c8e2bfb11299371 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 11 Jul 2021 14:51:25 +1000 Subject: [PATCH] move proto files into separate py_library in same namespace Will allow importing the Protobuf without pulling in the rest of the library. This is not a full PEP420 namespace, and the wheel still bundles everything - it just makes things easier in a Bazel workspace. I originally tried with PEP420, but it required more invasive changes, and I ran into issues with mypy. --- pylib/BUILD.bazel | 1 + pylib/anki/BUILD.bazel | 16 ++++++++++------ pylib/anki/__init__.py | 18 ------------------ pylib/anki/_backend/BUILD.bazel | 2 +- pylib/anki/_backend/__init__.py | 1 + pylib/anki/_backend/genbackend.py | 18 ++++++++++++++++-- pylib/anki/consts.py | 4 ++-- pylib/anki/dbproxy.py | 2 +- pylib/anki/httpclient.py | 2 +- pylib/anki/importing/__init__.py | 2 +- pylib/anki/stats.py | 3 ++- pylib/anki/stdmodels.py | 3 ++- pylib/anki/syncserver/__init__.py | 2 +- pylib/tests/shared.py | 2 +- pylib/tests/test_collection.py | 2 +- pylib/tests/test_exporting.py | 2 +- pylib/tests/test_schedv1.py | 2 +- qt/aqt/__init__.py | 16 +++++++++++++++- qt/aqt/models.py | 3 ++- qt/aqt/profiles.py | 2 +- qt/aqt/utils.py | 2 +- qt/dmypy.py | 4 ++-- 22 files changed, 64 insertions(+), 45 deletions(-) diff --git a/pylib/BUILD.bazel b/pylib/BUILD.bazel index 098d24a83..8c791286f 100644 --- a/pylib/BUILD.bazel +++ b/pylib/BUILD.bazel @@ -31,6 +31,7 @@ py_test( main = "tests/run_mypy.py", deps = [ "//pylib/anki", + "//pylib/anki:proto", requirement("mypy"), ], ) diff --git a/pylib/anki/BUILD.bazel b/pylib/anki/BUILD.bazel index ac9c29e89..872110c05 100644 --- a/pylib/anki/BUILD.bazel +++ b/pylib/anki/BUILD.bazel @@ -4,6 +4,7 @@ load("@py_deps//:requirements.bzl", "requirement") load("@rules_python//experimental/python:wheel.bzl", "py_package", "py_wheel") load("//:defs.bzl", "anki_version") load("//pylib:orjson.bzl", "orjson_if_available") +load("//pylib:protobuf.bzl", "py_proto") copy_file( name = "buildinfo", @@ -32,7 +33,6 @@ py_library( "py.typed", ":buildinfo", ":hooks_gen", - ":proto_py", "//pylib/anki/_backend", ], imports = [ @@ -40,6 +40,7 @@ py_library( ], visibility = ["//visibility:public"], deps = [ + ":proto", requirement("beautifulsoup4"), requirement("decorator"), requirement("distro"), @@ -107,8 +108,6 @@ filegroup( ], ) -load("//pylib:protobuf.bzl", "py_proto") - py_proto( name = "proto_py", srcs = ["//proto"], @@ -117,10 +116,15 @@ py_proto( ], ) -# only used for genbackend.py py_library( - name = "proto_lib", - srcs = [":proto_py", "__init__.py"], + name = "proto", + srcs = [ + "__init__.py", + # includes the .py files + ":proto_py", + ], + # includes the .pyi files + data = [":proto_py", "py.typed"], imports = [".."], visibility = ["//visibility:public"], ) diff --git a/pylib/anki/__init__.py b/pylib/anki/__init__.py index e44d9d175..e3d8b5638 100644 --- a/pylib/anki/__init__.py +++ b/pylib/anki/__init__.py @@ -1,20 +1,2 @@ # Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -import os -import sys - -if not os.getenv("PROTOS_ONLY"): - from anki.buildinfo import version - from anki.collection import Collection - - if sys.version_info[0] < 3 or sys.version_info[1] < 7: - raise Exception("Anki requires Python 3.7+") - - # ensure unicode filenames are supported - try: - "テスト".encode(sys.getfilesystemencoding()) - except UnicodeEncodeError as exc: - raise Exception("Anki requires a UTF-8 locale.") from exc - - __all__ = ["Collection"] diff --git a/pylib/anki/_backend/BUILD.bazel b/pylib/anki/_backend/BUILD.bazel index 4707ad1ff..e60346b55 100644 --- a/pylib/anki/_backend/BUILD.bazel +++ b/pylib/anki/_backend/BUILD.bazel @@ -12,7 +12,7 @@ py_binary( requirement("black"), requirement("stringcase"), requirement("protobuf"), - "//pylib/anki:proto_lib", + "//pylib/anki:proto", ], ) diff --git a/pylib/anki/_backend/__init__.py b/pylib/anki/_backend/__init__.py index 3decc0312..d0c23045d 100644 --- a/pylib/anki/_backend/__init__.py +++ b/pylib/anki/_backend/__init__.py @@ -11,6 +11,7 @@ from weakref import ref from markdown import markdown import anki.buildinfo +import anki.lang from anki import backend_pb2, i18n_pb2 from anki._backend.generated import RustBackendGenerated from anki.dbproxy import Row as DBRow diff --git a/pylib/anki/_backend/genbackend.py b/pylib/anki/_backend/genbackend.py index 174a40f10..28ce899ea 100755 --- a/pylib/anki/_backend/genbackend.py +++ b/pylib/anki/_backend/genbackend.py @@ -8,8 +8,6 @@ import sys import google.protobuf.descriptor -os.environ["PROTOS_ONLY"] = "1" - import anki.backend_pb2 import anki.i18n_pb2 import anki.cards_pb2 @@ -232,6 +230,22 @@ col.decks.all_config() from typing import * import anki +import anki.backend_pb2 +import anki.i18n_pb2 +import anki.cards_pb2 +import anki.collection_pb2 +import anki.decks_pb2 +import anki.deckconfig_pb2 +import anki.notes_pb2 +import anki.notetypes_pb2 +import anki.scheduler_pb2 +import anki.sync_pb2 +import anki.configs_pb2 +import anki.search_pb2 +import anki.stats_pb2 +import anki.card_rendering_pb2 +import anki.tags_pb2 +import anki.media_pb2 class RustBackendGenerated: def _run_command(self, service: int, method: int, input: Any) -> bytes: diff --git a/pylib/anki/consts.py b/pylib/anki/consts.py index d57a93b00..f26388245 100644 --- a/pylib/anki/consts.py +++ b/pylib/anki/consts.py @@ -6,8 +6,6 @@ from __future__ import annotations import sys from typing import Any, Dict, NewType, Optional -import anki - # whether new cards should be mixed with reviews, or shown first or last NEW_CARDS_DISTRIBUTE = 0 NEW_CARDS_LAST = 1 @@ -93,6 +91,8 @@ REVLOG_RESCHED = 4 # Labels ########################################################################## +import anki.collection + def _tr(col: Optional[anki.collection.Collection]) -> Any: if col: diff --git a/pylib/anki/dbproxy.py b/pylib/anki/dbproxy.py index 3561613c5..45c391383 100644 --- a/pylib/anki/dbproxy.py +++ b/pylib/anki/dbproxy.py @@ -7,7 +7,7 @@ import re from re import Match from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union -import anki +import anki._backend # DBValue is actually Union[str, int, float, None], but if defined # that way, every call site needs to do a type check prior to using diff --git a/pylib/anki/httpclient.py b/pylib/anki/httpclient.py index 55ca971b6..a26536496 100644 --- a/pylib/anki/httpclient.py +++ b/pylib/anki/httpclient.py @@ -76,7 +76,7 @@ class HttpClient: return buf.getvalue() def _agentName(self) -> str: - from anki import version + from anki.buildinfo import version return f"Anki {version}" diff --git a/pylib/anki/importing/__init__.py b/pylib/anki/importing/__init__.py index d2f434938..7c35ba545 100644 --- a/pylib/anki/importing/__init__.py +++ b/pylib/anki/importing/__init__.py @@ -3,7 +3,7 @@ from typing import Any, Callable, Sequence, Tuple, Type, Union -from anki import Collection +from anki.collection import Collection from anki.importing.anki2 import Anki2Importer from anki.importing.apkg import AnkiPackageImporter from anki.importing.base import Importer diff --git a/pylib/anki/stats.py b/pylib/anki/stats.py index 9bd302ed2..9ef02c026 100644 --- a/pylib/anki/stats.py +++ b/pylib/anki/stats.py @@ -10,7 +10,8 @@ import json import time from typing import Any, Dict, List, Optional, Sequence, Tuple, Union -import anki +import anki.cards +import anki.collection from anki.consts import * from anki.lang import FormatTimeSpan from anki.utils import ids2str diff --git a/pylib/anki/stdmodels.py b/pylib/anki/stdmodels.py index 720007408..eb5dad1a2 100644 --- a/pylib/anki/stdmodels.py +++ b/pylib/anki/stdmodels.py @@ -5,7 +5,8 @@ from __future__ import annotations from typing import Any, Callable, List, Tuple -import anki +import anki.collection +import anki.models from anki import notetypes_pb2 from anki.utils import from_json_bytes diff --git a/pylib/anki/syncserver/__init__.py b/pylib/anki/syncserver/__init__.py index fc7690bb0..9e6852eda 100644 --- a/pylib/anki/syncserver/__init__.py +++ b/pylib/anki/syncserver/__init__.py @@ -26,7 +26,7 @@ except ImportError as e: from flask import Response -from anki import Collection +from anki.collection import Collection from anki.sync_pb2 import SyncServerMethodRequest Method = SyncServerMethodRequest.Method # pylint: disable=no-member diff --git a/pylib/tests/shared.py b/pylib/tests/shared.py index b64599329..ce6fe9d8f 100644 --- a/pylib/tests/shared.py +++ b/pylib/tests/shared.py @@ -6,7 +6,7 @@ import shutil import tempfile import time -from anki import Collection as aopen +from anki.collection import Collection as aopen # Between 2-4AM, shift the time back so test assumptions hold. lt = time.localtime() diff --git a/pylib/tests/test_collection.py b/pylib/tests/test_collection.py index a2200c734..6588bc592 100644 --- a/pylib/tests/test_collection.py +++ b/pylib/tests/test_collection.py @@ -6,7 +6,7 @@ import os import tempfile -from anki import Collection as aopen +from anki.collection import Collection as aopen from anki.dbproxy import emulate_named_args from anki.lang import TR, without_unicode_isolation from anki.stdmodels import addBasicModel, get_stock_notetypes diff --git a/pylib/tests/test_exporting.py b/pylib/tests/test_exporting.py index 30a3acfd9..ef3309fae 100644 --- a/pylib/tests/test_exporting.py +++ b/pylib/tests/test_exporting.py @@ -6,7 +6,7 @@ import os import tempfile -from anki import Collection as aopen +from anki.collection import Collection as aopen from anki.exporting import * from anki.importing import Anki2Importer from tests.shared import errorsAfterMidnight diff --git a/pylib/tests/test_schedv1.py b/pylib/tests/test_schedv1.py index ef5073fb2..056d49c69 100644 --- a/pylib/tests/test_schedv1.py +++ b/pylib/tests/test_schedv1.py @@ -4,7 +4,7 @@ import copy import time -from anki import Collection +from anki.collection import Collection from anki.consts import * from anki.lang import without_unicode_isolation from anki.utils import intTime diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index 54b12ba93..e617eaa09 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -13,13 +13,27 @@ import traceback from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast import anki.lang -from anki import version as _version from anki._backend import RustBackend +from anki.buildinfo import version as _version +from anki.collection import Collection from anki.consts import HELP_SITE from anki.utils import checksum, isLin, isMac from aqt.qt import * from aqt.utils import TR, locale_dir, tr +if sys.version_info[0] < 3 or sys.version_info[1] < 7: + raise Exception("Anki requires Python 3.7+") + +# ensure unicode filenames are supported +try: + "テスト".encode(sys.getfilesystemencoding()) +except UnicodeEncodeError as exc: + raise Exception("Anki requires a UTF-8 locale.") from exc + +# compat aliases +anki.version = _version # type: ignore +anki.Collection = Collection # type: ignore + # we want to be able to print unicode debug info to console without # fear of a traceback on systems with the console set to ASCII try: diff --git a/qt/aqt/models.py b/qt/aqt/models.py index 8e0beb677..b65be9d34 100644 --- a/qt/aqt/models.py +++ b/qt/aqt/models.py @@ -6,7 +6,8 @@ from operator import itemgetter from typing import Any, List, Optional, Sequence import aqt.clayout -from anki import Collection, stdmodels +from anki import stdmodels +from anki.collection import Collection from anki.lang import without_unicode_isolation from anki.models import NotetypeDict, NotetypeId, NotetypeNameIdUseCount from anki.notes import Note diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py index 8e47ca2b1..2fce8ec77 100644 --- a/qt/aqt/profiles.py +++ b/qt/aqt/profiles.py @@ -17,7 +17,7 @@ from send2trash import send2trash import anki.lang import aqt.forms import aqt.sound -from anki import Collection +from anki.collection import Collection from anki.db import DB from anki.lang import without_unicode_isolation from anki.sync import SyncAuth diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py index 0b674dbd2..e44958c25 100644 --- a/qt/aqt/utils.py +++ b/qt/aqt/utils.py @@ -34,8 +34,8 @@ from PyQt5.QtWidgets import ( ) import aqt -from anki import Collection from anki._legacy import deprecated +from anki.collection import Collection from anki.lang import TR, tr_legacyglobal # pylint: disable=unused-import from anki.utils import invalidFilename, isMac, isWin, noBundledLibs, versionWithBuild from aqt.qt import * diff --git a/qt/dmypy.py b/qt/dmypy.py index 20f3caf0a..968061146 100755 --- a/qt/dmypy.py +++ b/qt/dmypy.py @@ -30,8 +30,8 @@ if subprocess.run( "--", "--config-file", "qt/mypy.ini", - "bazel-bin/qt/dmypy.runfiles/anki/pylib/anki", - "bazel-bin/qt/dmypy.runfiles/anki/qt/aqt", + "bazel-bin/qt/dmypy.runfiles/ankidesktop/pylib/anki", + "bazel-bin/qt/dmypy.runfiles/ankidesktop/qt/aqt", "--python-executable", os.path.abspath("pip/stubs/extendsitepkgs"), ],