Update docstrings
This commit is contained in:
parent
04790b3ed3
commit
77d877047b
1
TODO.md
1
TODO.md
|
@ -4,7 +4,6 @@
|
|||
|
||||
- Catch server 5xx errors when sending message and retry
|
||||
- nio ClientTimeout (to fix sync hanging after network changes or hibernation)
|
||||
- Update docstrings
|
||||
- Update README.md
|
||||
|
||||
## Refactoring
|
||||
|
|
|
@ -29,43 +29,42 @@ class Backend:
|
|||
"""Manage matrix clients and provide other useful general methods.
|
||||
|
||||
Attributes:
|
||||
saved_accounts: User config file for saved matrix account details.
|
||||
saved_accounts: User config file for saved matrix account.
|
||||
|
||||
ui_settings: User config file for QML interface settings.
|
||||
|
||||
ui_state: User data file for saving/restoring QML UI state.
|
||||
|
||||
history: User data file for saving/restoring lines typed into QML
|
||||
history: User data file for saving/restoring text typed into QML
|
||||
components.
|
||||
|
||||
models: A dict containing our data models that are synchronized between
|
||||
the Python backend and the QML UI.
|
||||
models: A mapping containing our data models that are
|
||||
synchronized between the Python backend and the QML UI.
|
||||
The models should only ever be modified from the backend.
|
||||
|
||||
The dict keys are the `Model`'s synchronization ID,
|
||||
which are in one of these form:
|
||||
If a non-existent key is accessed, it is creating with an
|
||||
associated `Model` and returned.
|
||||
|
||||
- A `ModelItem` type, the type of item that this model stores;
|
||||
- A `(ModelItem, str...)` tuple.
|
||||
The mapping keys are the `Model`'s synchronization ID,
|
||||
which a strings or tuple of strings.
|
||||
|
||||
Currently used sync ID throughout the code are:
|
||||
|
||||
- `Account`: logged-in accounts;
|
||||
- `"accounts"`: logged-in accounts;
|
||||
|
||||
- `(Room, "<user_id>")`: rooms a `user_id` account is part of;
|
||||
- `("<user_id>", "rooms")`: rooms our account `user_id` is part of;
|
||||
|
||||
- `(Upload, "<user_id>")`: ongoing or failed file uploads for a
|
||||
`user_id` account;
|
||||
- `("<user_id>", "uploads")`: ongoing or failed file uploads for
|
||||
our account `user_id`;
|
||||
|
||||
- `(Member, "<user_id>", "<room_id>")`: members in the room
|
||||
`room_id` that account `user_id` is part of;
|
||||
- `("<user_id>", "<room_id>", "members")`: members in the room
|
||||
`room_id` that our account `user_id` is part of;
|
||||
|
||||
- `(Event, "<user_id>", "<room_id>")`: state events and messages
|
||||
in the room `room_id` that account `user_id` is part of.
|
||||
- `("<user_id>", "<room_id>", "events")`: state events and messages
|
||||
in the room `room_id` that our account `user_id` is part of.
|
||||
|
||||
clients: A dict containing the logged `MatrixClient` objects we manage.
|
||||
Each client represents a logged-in account,
|
||||
identified by its `user_id`.
|
||||
clients: A `{user_id: MatrixClient}` dict for the logged-in clients
|
||||
we managed. Every client is logged to one matrix account.
|
||||
|
||||
media_cache: A matrix media cache for downloaded files.
|
||||
"""
|
||||
|
|
|
@ -357,7 +357,7 @@ class MatrixClient(nio.AsyncClient):
|
|||
async def _send_file(
|
||||
self, item_uuid: UUID, room_id: str, path: Union[Path, str],
|
||||
) -> None:
|
||||
"""Monitorably upload a file + thumbnail and send the built event."""
|
||||
"""Upload and monitor a file + thumbnail and send the built event."""
|
||||
|
||||
# TODO: this function is way too complex, and most of it should be
|
||||
# refactored into nio.
|
||||
|
@ -993,7 +993,7 @@ class MatrixClient(nio.AsyncClient):
|
|||
|
||||
|
||||
async def clear_events(self, room_id: str) -> None:
|
||||
"""Remove every `Event` of a room we registred in our model.
|
||||
"""Remove every `Event` of a room we registered in our model.
|
||||
|
||||
The events will be gone from the UI, until the client is restarted.
|
||||
"""
|
||||
|
|
|
@ -94,7 +94,15 @@ class Media:
|
|||
|
||||
@property
|
||||
def local_path(self) -> Path:
|
||||
"""The path where the file either exists or should be downloaded."""
|
||||
"""The path where the file either exists or should be downloaded.
|
||||
|
||||
The returned paths are in this form:
|
||||
```
|
||||
<base download folder>/<homeserver domain>/
|
||||
<file title>_<mxc id>.<file extension>`
|
||||
```
|
||||
e.g. `~/.cache/mirage/downloads/matrix.org/foo_Hm24ar11i768b0el.png`.
|
||||
"""
|
||||
|
||||
parsed = urlparse(self.mxc)
|
||||
mxc_id = parsed.path.lstrip("/")
|
||||
|
@ -255,8 +263,12 @@ class Thumbnail(Media):
|
|||
"""The path where the thumbnail either exists or should be downloaded.
|
||||
|
||||
The returned paths are in this form:
|
||||
`<base thumbnail folder>/<homeserver domain>/<standard size>/<mxc id>`,
|
||||
e.g. `~/.cache/appname/thumbnails/matrix.org/32x32/Hm24ar11i768b0el`.
|
||||
```
|
||||
<base thumbnail folder>/<homeserver domain>/<standard size>/
|
||||
<file title>_<mxc id>.<file extension>`
|
||||
```
|
||||
e.g.
|
||||
`~/.cache/mirage/thumbnails/matrix.org/32x32/foo_Hm24ar11i768b0el.png`.
|
||||
"""
|
||||
|
||||
size = self.normalize_size(self.server_size or self.wanted_size)
|
||||
|
@ -271,12 +283,16 @@ class Thumbnail(Media):
|
|||
|
||||
|
||||
async def _get_local_existing_file(self) -> Path:
|
||||
"""Return an existing thumbnail path or raise `FileNotFoundError`.
|
||||
|
||||
If we have a bigger size thumbnail downloaded than the `wanted_size`
|
||||
for the media, return it instead of asking the server for a
|
||||
smaller thumbnail.
|
||||
"""
|
||||
|
||||
if self.local_path.exists():
|
||||
return self.local_path
|
||||
|
||||
# If we have a bigger size thumbnail than the wanted_size for this pic,
|
||||
# return it instead of asking the server for a smaller thumbnail.
|
||||
|
||||
try_sizes = ((32, 32), (96, 96), (320, 240), (640, 480), (800, 600))
|
||||
parts = list(self.local_path.parts)
|
||||
size = self.normalize_size(self.server_size or self.wanted_size)
|
||||
|
@ -295,6 +311,8 @@ class Thumbnail(Media):
|
|||
|
||||
|
||||
async def _get_remote_data(self) -> bytes:
|
||||
"""Return the (decrypted) media file's content from the server."""
|
||||
|
||||
parsed = urlparse(self.mxc)
|
||||
|
||||
if self.crypt_dict:
|
||||
|
|
|
@ -16,22 +16,17 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Model(MutableMapping):
|
||||
"""A mapping of `{identifier: ModelItem}` synced between Python & QML.
|
||||
"""A mapping of `{ModelItem.id: ModelItem}` synced between Python & QML.
|
||||
|
||||
From the Python side, the model is usable like a normal dict of
|
||||
`ModelItem` subclass objects.
|
||||
Different types of `ModelItem` must not be mixed in the same model.
|
||||
|
||||
When items are added, changed or removed from the model, a synchronization
|
||||
with QML is scheduled.
|
||||
The model will synchronize with QML no more than every 0.25s, for
|
||||
performance reasons; though it is possible to request an instant sync
|
||||
via `sync_now()` for certain cases when this delay is unacceptable.
|
||||
When items are added, replaced, removed, have field value changes, or the
|
||||
model is cleared, corresponding `PyOtherSideEvent` are fired to inform
|
||||
QML of the changes so that it can keep its models in sync.
|
||||
|
||||
Model data is sent to QML using a `ModelUpdated` event from the
|
||||
`pyotherside_events` module.
|
||||
The data is a list of serialized `ModelItem` dicts, as expected
|
||||
by QML for components like `ListView`.
|
||||
Items in the model are kept sorted using the `ModelItem` subclass `__lt__`.
|
||||
"""
|
||||
|
||||
def __init__(self, sync_id: SyncId) -> None:
|
||||
|
|
|
@ -17,7 +17,7 @@ class ModelItem:
|
|||
|
||||
Subclasses are also expected to implement `__lt__()`,
|
||||
to provide support for comparisons with the `<`, `>`, `<=`, `=>` operators
|
||||
and thus allow a `Model` to sort its `ModelItem`s.
|
||||
and thus allow a `Model` to keep its data sorted.
|
||||
"""
|
||||
|
||||
def __new__(cls, *_args, **_kwargs) -> "ModelItem":
|
||||
|
|
|
@ -14,7 +14,7 @@ class ModelStore(UserDict):
|
|||
|
||||
The dict keys must be the sync ID of `Model` values.
|
||||
If a non-existent key is accessed, a corresponding `Model` will be
|
||||
created, put into the interal `data` dict and returned.
|
||||
created, put into the internal `data` dict and returned.
|
||||
"""
|
||||
|
||||
data: Dict[SyncId, Model] = field(default_factory=dict)
|
||||
|
|
|
@ -55,7 +55,7 @@ class CoroutineDone(PyOtherSideEvent):
|
|||
|
||||
@dataclass
|
||||
class LoopException(PyOtherSideEvent):
|
||||
"""Indicate that an uncaught exception occured in the asyncio loop."""
|
||||
"""Indicate an uncaught exception occurance in the asyncio loop."""
|
||||
|
||||
message: str = field()
|
||||
exception: Optional[Exception] = field()
|
||||
|
@ -64,6 +64,8 @@ class LoopException(PyOtherSideEvent):
|
|||
|
||||
@dataclass
|
||||
class ModelItemInserted(PyOtherSideEvent):
|
||||
"""Indicate a `ModelItem` insertion into a `Backend` `Model`."""
|
||||
|
||||
sync_id: SyncId = field()
|
||||
index: int = field()
|
||||
item: "ModelItem" = field()
|
||||
|
@ -71,6 +73,8 @@ class ModelItemInserted(PyOtherSideEvent):
|
|||
|
||||
@dataclass
|
||||
class ModelItemFieldChanged(PyOtherSideEvent):
|
||||
"""Indicate a `ModelItem`'s field value change in a `Backend` `Model`."""
|
||||
|
||||
sync_id: SyncId = field()
|
||||
item_index_then: int = field()
|
||||
item_index_now: int = field()
|
||||
|
@ -80,10 +84,14 @@ class ModelItemFieldChanged(PyOtherSideEvent):
|
|||
|
||||
@dataclass
|
||||
class ModelItemDeleted(PyOtherSideEvent):
|
||||
"""Indicate the removal of a `ModelItem` from a `Backend` `Model`."""
|
||||
|
||||
sync_id: SyncId = field()
|
||||
index: int = field()
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModelCleared(PyOtherSideEvent):
|
||||
"""Indicate that a `Backend` `Model` was cleared."""
|
||||
|
||||
sync_id: SyncId = field()
|
||||
|
|
|
@ -26,7 +26,7 @@ auto = autostr
|
|||
|
||||
|
||||
class AutoStrEnum(Enum):
|
||||
"""An Enum where auto() assigns the member's name instead of an int.
|
||||
"""An Enum where auto() assigns the member's name instead of an integer.
|
||||
|
||||
Example:
|
||||
>>> class Fruits(AutoStrEnum): apple = auto()
|
||||
|
@ -130,12 +130,24 @@ def serialize_value_for_qml(value: Any, json_list_dicts: bool = False) -> Any:
|
|||
|
||||
Returns:
|
||||
|
||||
- Return the member's actual value for `Enum` members
|
||||
- A `file://...` string for `Path` objects
|
||||
- Strings for `UUID` objects
|
||||
- A number of milliseconds for `datetime.timedelta` objects
|
||||
- The class `__name__` for class types.
|
||||
- `ModelItem.serialized` for `ModelItem`s
|
||||
- For `int`, `float`, `bool`, `str` and `datetime`: the unchanged value
|
||||
|
||||
- For `Sequence` and `Mapping` subclasses (includes `list` and `dict`):
|
||||
a JSON dump if `json_list_dicts` is `True`, else the unchanged value
|
||||
|
||||
- If the value as a `serialized` attribute or property, return that
|
||||
|
||||
- For `Enum` members, the actual value of the member
|
||||
|
||||
- For `Path` objects, a `file://<path...>` string
|
||||
|
||||
- For `UUID` object: the UUID in string form
|
||||
|
||||
- For `timedelta` objects: the delta as a number of milliseconds `int`
|
||||
|
||||
- For class types: the class `__name__`
|
||||
|
||||
- For anything else: the unchanged value
|
||||
"""
|
||||
|
||||
if isinstance(value, (int, float, bool, str, datetime)):
|
||||
|
|
Loading…
Reference in New Issue
Block a user