Use typing.TYPE_CHECKING to avoid inner imports

This commit is contained in:
miruka 2020-02-11 16:22:05 -04:00
parent a653a6160a
commit b992db9bfe
9 changed files with 58 additions and 52 deletions

View File

@ -13,9 +13,11 @@ from appdirs import AppDirs
from . import __app_name__ from . import __app_name__
from .errors import MatrixError from .errors import MatrixError
from .matrix_client import MatrixClient from .matrix_client import MatrixClient
from .media_cache import MediaCache
from .models import SyncId from .models import SyncId
from .models.items import Account from .models.items import Account
from .models.model_store import ModelStore from .models.model_store import ModelStore
from .user_files import Accounts, History, Theme, UISettings, UIState
# Logging configuration # Logging configuration
log.getLogger().setLevel(log.INFO) log.getLogger().setLevel(log.INFO)
@ -71,7 +73,6 @@ class Backend:
def __init__(self) -> None: def __init__(self) -> None:
self.appdirs = AppDirs(appname=__app_name__, roaming=True) self.appdirs = AppDirs(appname=__app_name__, roaming=True)
from .user_files import Accounts, UISettings, UIState, History
self.saved_accounts: Accounts = Accounts(self) self.saved_accounts: Accounts = Accounts(self)
self.ui_settings: UISettings = UISettings(self) self.ui_settings: UISettings = UISettings(self)
self.ui_state: UIState = UIState(self) self.ui_state: UIState = UIState(self)
@ -87,7 +88,6 @@ class Backend:
self.send_locks: DefaultDict[str, asyncio.Lock] = \ self.send_locks: DefaultDict[str, asyncio.Lock] = \
DefaultDict(asyncio.Lock) # {room_id: lock} DefaultDict(asyncio.Lock) # {room_id: lock}
from .media_cache import MediaCache
cache_dir = Path(self.appdirs.user_cache_dir) cache_dir = Path(self.appdirs.user_cache_dir)
self.media_cache: MediaCache = MediaCache(self, cache_dir) self.media_cache: MediaCache = MediaCache(self, cache_dir)
@ -247,7 +247,6 @@ class Backend:
async def load_settings(self) -> tuple: async def load_settings(self) -> tuple:
"""Return parsed user config files.""" """Return parsed user config files."""
from .user_files import Theme
settings = await self.ui_settings.read() settings = await self.ui_settings.read()
ui_state = await self.ui_state.read() ui_state = await self.ui_state.read()
history = await self.history.read() history = await self.history.read()

View File

@ -14,19 +14,18 @@ from datetime import datetime
from functools import partial from functools import partial
from pathlib import Path from pathlib import Path
from typing import ( from typing import (
Any, DefaultDict, Dict, List, NamedTuple, Optional, Set, Tuple, Type, TYPE_CHECKING, Any, DefaultDict, Dict, List, NamedTuple, Optional, Set,
Union, Tuple, Type, Union,
) )
from urllib.parse import urlparse from urllib.parse import urlparse
from uuid import UUID, uuid4 from uuid import UUID, uuid4
import cairosvg import cairosvg
from PIL import Image as PILImage
from pymediainfo import MediaInfo
import nio import nio
from nio.crypto import AsyncDataT as UploadData from nio.crypto import AsyncDataT as UploadData
from nio.crypto import async_generator_from_data from nio.crypto import async_generator_from_data
from PIL import Image as PILImage
from pymediainfo import MediaInfo
from . import __app_name__, __display_name__, utils from . import __app_name__, __display_name__, utils
from .errors import ( from .errors import (
@ -34,12 +33,15 @@ from .errors import (
MatrixNotFound, UneededThumbnail, MatrixNotFound, UneededThumbnail,
) )
from .html_markdown import HTML_PROCESSOR as HTML from .html_markdown import HTML_PROCESSOR as HTML
from .models.items import ( from .media_cache import Media, Thumbnail
Event, Member, Room, TypeSpecifier, Upload, UploadStatus, from .models.items import Event, Member, Room, Upload, UploadStatus
)
from .models.model_store import ModelStore from .models.model_store import ModelStore
from .nio_callbacks import NioCallbacks
from .pyotherside_events import AlertRequested from .pyotherside_events import AlertRequested
if TYPE_CHECKING:
from .backend import Backend
CryptDict = Dict[str, Any] CryptDict = Dict[str, Any]
@ -103,8 +105,7 @@ class MatrixClient(nio.AsyncClient):
), ),
) )
from .backend import Backend self.backend: "Backend" = backend
self.backend: Backend = backend
self.models: ModelStore = self.backend.models self.models: ModelStore = self.backend.models
self.profile_task: Optional[asyncio.Future] = None self.profile_task: Optional[asyncio.Future] = None
@ -120,7 +121,6 @@ class MatrixClient(nio.AsyncClient):
self.skipped_events: DefaultDict[str, int] = DefaultDict(lambda: 0) self.skipped_events: DefaultDict[str, int] = DefaultDict(lambda: 0)
from .nio_callbacks import NioCallbacks
self.nio_callbacks = NioCallbacks(self) self.nio_callbacks = NioCallbacks(self)
@ -292,11 +292,9 @@ class MatrixClient(nio.AsyncClient):
) -> None: ) -> None:
"""Monitorably upload a file + thumbnail and send the built event.""" """Monitorably upload a file + thumbnail and send the built event."""
# TODO: this function is WAY TOO COMPLEX, and most of it should be # TODO: this function is way too complex, and most of it should be
# refactored into nio. # refactored into nio.
from .media_cache import Media, Thumbnail
transaction_id = uuid4() transaction_id = uuid4()
path = Path(path) path = Path(path)
encrypt = room_id in self.encrypted_rooms encrypt = room_id in self.encrypted_rooms

View File

@ -10,17 +10,18 @@ import shutil
import sys import sys
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import Any, DefaultDict, Dict, Optional from typing import TYPE_CHECKING, Any, DefaultDict, Dict, Optional
from urllib.parse import urlparse from urllib.parse import urlparse
import aiofiles import aiofiles
import nio
from PIL import Image as PILImage from PIL import Image as PILImage
import nio
from .backend import Backend
from .utils import Size from .utils import Size
if TYPE_CHECKING:
from .backend import Backend
if sys.version_info < (3, 8): if sys.version_info < (3, 8):
import pyfastcopy # noqa import pyfastcopy # noqa
@ -34,8 +35,8 @@ ACCESS_LOCKS: DefaultDict[str, asyncio.Lock] = DefaultDict(asyncio.Lock)
class MediaCache: class MediaCache:
"""Matrix downloaded media cache.""" """Matrix downloaded media cache."""
backend: Backend = field() backend: "Backend" = field()
base_dir: Path = field() base_dir: Path = field()
def __post_init__(self) -> None: def __post_init__(self) -> None:

View File

@ -1,13 +1,15 @@
# SPDX-License-Identifier: LGPL-3.0-or-later # SPDX-License-Identifier: LGPL-3.0-or-later
from threading import Lock from threading import Lock
from typing import Any, Dict, Iterator, List, MutableMapping from typing import TYPE_CHECKING, Any, Dict, Iterator, List, MutableMapping
from ..pyotherside_events import ( from ..pyotherside_events import (
ModelCleared, ModelItemDeleted, ModelItemInserted, ModelCleared, ModelItemDeleted, ModelItemInserted,
) )
from . import SyncId from . import SyncId
from .model_item import ModelItem
if TYPE_CHECKING:
from .model_item import ModelItem
class Model(MutableMapping): class Model(MutableMapping):
@ -30,10 +32,10 @@ class Model(MutableMapping):
""" """
def __init__(self, sync_id: SyncId) -> None: def __init__(self, sync_id: SyncId) -> None:
self.sync_id: SyncId = sync_id self.sync_id: SyncId = sync_id
self._data: Dict[Any, ModelItem] = {} self._data: Dict[Any, "ModelItem"] = {}
self._sorted_data: List[ModelItem] = [] self._sorted_data: List["ModelItem"] = []
self._write_lock: Lock = Lock() self._write_lock: Lock = Lock()
def __repr__(self) -> str: def __repr__(self) -> str:
@ -58,7 +60,7 @@ class Model(MutableMapping):
return self._data[key] return self._data[key]
def __setitem__(self, key, value: ModelItem) -> None: def __setitem__(self, key, value: "ModelItem") -> None:
"""Merge new item with an existing one if possible, else add it. """Merge new item with an existing one if possible, else add it.
If an existing item with the passed `key` is found, its fields will be If an existing item with the passed `key` is found, its fields will be

View File

@ -1,6 +1,12 @@
# SPDX-License-Identifier: LGPL-3.0-or-later # SPDX-License-Identifier: LGPL-3.0-or-later
from typing import Any, Dict, Optional from typing import TYPE_CHECKING, Any, Dict, Optional
from ..pyotherside_events import ModelItemFieldChanged
from ..utils import serialize_value_for_qml
if TYPE_CHECKING:
from .model import Model
class ModelItem: class ModelItem:
@ -18,7 +24,6 @@ class ModelItem:
""" """
def __new__(cls, *_args, **_kwargs) -> "ModelItem": def __new__(cls, *_args, **_kwargs) -> "ModelItem":
from .model import Model
cls.parent_model: Optional[Model] = None cls.parent_model: Optional[Model] = None
return super().__new__(cls) return super().__new__(cls)
@ -39,7 +44,6 @@ class ModelItem:
self.parent_model._sorted_data.sort() self.parent_model._sorted_data.sort()
new_index = self.parent_model._sorted_data.index(self) new_index = self.parent_model._sorted_data.index(self)
from ..pyotherside_events import ModelItemFieldChanged
ModelItemFieldChanged( ModelItemFieldChanged(
self.parent_model.sync_id, old_index, new_index, name, value, self.parent_model.sync_id, old_index, new_index, name, value,
) )
@ -53,8 +57,6 @@ class ModelItem:
def serialized(self) -> Dict[str, Any]: def serialized(self) -> Dict[str, Any]:
"""Return this item as a dict ready to be passed to QML.""" """Return this item as a dict ready to be passed to QML."""
from ..utils import serialize_value_for_qml
return { return {
name: serialize_value_for_qml(getattr(self, name), json_lists=True) name: serialize_value_for_qml(getattr(self, name), json_lists=True)
for name in dir(self) for name in dir(self)

View File

@ -6,16 +6,18 @@ import logging as log
from contextlib import suppress from contextlib import suppress
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime from datetime import datetime
from typing import Optional, Tuple from typing import TYPE_CHECKING, Optional, Tuple
from urllib.parse import quote from urllib.parse import quote
import nio import nio
from . import utils from . import utils
from .html_markdown import HTML_PROCESSOR from .html_markdown import HTML_PROCESSOR
from .matrix_client import MatrixClient
from .models.items import TypeSpecifier from .models.items import TypeSpecifier
if TYPE_CHECKING:
from .matrix_client import MatrixClient
@dataclass @dataclass
class NioCallbacks: class NioCallbacks:
@ -30,7 +32,7 @@ class NioCallbacks:
These are processed from QML, to allow translations of the strings. These are processed from QML, to allow translations of the strings.
""" """
client: MatrixClient = field() client: "MatrixClient" = field()
def __post_init__(self) -> None: def __post_init__(self) -> None:

View File

@ -2,14 +2,16 @@
from abc import ABC from abc import ABC
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, Optional from typing import TYPE_CHECKING, Any, Optional
import pyotherside import pyotherside
from .models import SyncId from .models import SyncId
from .models.model_item import ModelItem
from .utils import serialize_value_for_qml from .utils import serialize_value_for_qml
if TYPE_CHECKING:
from .models.model_item import ModelItem
@dataclass @dataclass
class PyOtherSideEvent: class PyOtherSideEvent:
@ -69,9 +71,9 @@ class ModelEvent(ABC, PyOtherSideEvent):
@dataclass @dataclass
class ModelItemInserted(ModelEvent): class ModelItemInserted(ModelEvent):
sync_id: SyncId = field() sync_id: SyncId = field()
index: int = field() index: int = field()
item: ModelItem = field() item: "ModelItem" = field()
@dataclass @dataclass

View File

@ -7,14 +7,16 @@ import json
import logging as log import logging as log
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import Any, ClassVar, Dict, Optional from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional
import aiofiles import aiofiles
from .backend import Backend
from .theme_parser import convert_to_qml from .theme_parser import convert_to_qml
from .utils import dict_update_recursive from .utils import dict_update_recursive
if TYPE_CHECKING:
from .backend import Backend
JsonData = Dict[str, Any] JsonData = Dict[str, Any]
WRITE_LOCK = asyncio.Lock() WRITE_LOCK = asyncio.Lock()
@ -26,8 +28,8 @@ class DataFile:
is_config: ClassVar[bool] = False is_config: ClassVar[bool] = False
backend: Backend = field(repr=False) backend: "Backend" = field(repr=False)
filename: str = field() filename: str = field()
_to_write: Optional[str] = field(init=False, default=None) _to_write: Optional[str] = field(init=False, default=None)

View File

@ -21,8 +21,6 @@ from aiofiles.threadpool.binary import AsyncBufferedReader
from nio.crypto import AsyncDataT as File from nio.crypto import AsyncDataT as File
from nio.crypto import async_generator_from_data from nio.crypto import async_generator_from_data
from .models.model_item import ModelItem
Size = Tuple[int, int] Size = Tuple[int, int]
auto = autostr auto = autostr
@ -143,12 +141,12 @@ def serialize_value_for_qml(value: Any, json_lists: bool = False) -> Any:
if json_lists and isinstance(value, list): if json_lists and isinstance(value, list):
return json.dumps(value) return json.dumps(value)
if hasattr(value, "serialized"):
return value.serialized
if hasattr(value, "__class__") and issubclass(value.__class__, Enum): if hasattr(value, "__class__") and issubclass(value.__class__, Enum):
return value.value return value.value
if isinstance(value, ModelItem):
return value.serialized
if isinstance(value, Path): if isinstance(value, Path):
return f"file://{value!s}" return f"file://{value!s}"