From 2a1ab4de8b5c9e5056d68be2835135c9936ed802 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Tue, 12 Dec 2023 17:01:16 +0100 Subject: [PATCH] Beginnings of read/write backend typing --- src/fontra/core/fonthandler.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/fontra/core/fonthandler.py b/src/fontra/core/fonthandler.py index 9f4f81be4..0daacef12 100644 --- a/src/fontra/core/fonthandler.py +++ b/src/fontra/core/fonthandler.py @@ -6,6 +6,7 @@ from contextlib import asynccontextmanager from copy import deepcopy from dataclasses import dataclass +from functools import cached_property from typing import Any, AsyncGenerator, Awaitable, Callable, Optional from .changes import ( @@ -55,7 +56,7 @@ class FontHandler: allConnectionsClosedCallback: Optional[Callable[[], Awaitable[Any]]] = None def __post_init__(self): - if not isinstance(self.backend, WritableFontBackend): + if self.writableBackend is None: self.readOnly = True self.connections = set() self.glyphUsedBy = {} @@ -64,6 +65,10 @@ def __post_init__(self): self.localData = LRUCache() self._dataScheduledForWriting = {} + @cached_property + def writableBackend(self) -> WritableFontBackend | None: + return self.backend if isinstance(self.backend, WritableFontBackend) else None + async def startTasks(self) -> None: if isinstance(self.backend, WatchableFontBackend): self._watcherTask = scheduleTaskAndLogException( @@ -189,14 +194,14 @@ async def _getGlyphFromBackend(self, glyphName) -> VariableGlyph | None: self.updateGlyphDependencies(glyphName, glyph) return glyph - async def getData(self, key): + async def getData(self, key: str) -> Any: data = self.localData.get(key) if data is None: data = await self._getData(key) self.localData[key] = data return data - async def _getData(self, key): + async def _getData(self, key: str) -> Any: getterName = backendGetterNames[key] return await getattr(self.backend, getterName)() @@ -339,7 +344,8 @@ async def _prepareRootObject(self, change): async def _updateLocalData( self, rootKeys, rootObject, sourceConnection, writeToBackEnd - ): + ) -> None: + writeFunc: Callable[[], Awaitable] # inferencing with partial() goes wrong for rootKey in rootKeys + sorted(rootObject._assignedAttributeNames): if rootKey == "glyphs": glyphSet = rootObject.glyphs @@ -350,8 +356,9 @@ async def _updateLocalData( self.localData[writeKey] = glyphSet[glyphName] if not writeToBackEnd: continue + assert self.writableBackend is not None writeFunc = functools.partial( - self.backend.putGlyph, + self.writableBackend.putGlyph, glyphName, deepcopy(glyphSet[glyphName]), glyphMap.get(glyphName, []), @@ -362,14 +369,20 @@ async def _updateLocalData( _ = self.localData.pop(writeKey, None) if not writeToBackEnd: continue - writeFunc = functools.partial(self.backend.deleteGlyph, glyphName) + assert self.writableBackend is not None + writeFunc = functools.partial( + self.writableBackend.deleteGlyph, glyphName + ) await self.scheduleDataWrite(writeKey, writeFunc, sourceConnection) else: if rootKey in rootObject._assignedAttributeNames: self.localData[rootKey] = getattr(rootObject, rootKey) if not writeToBackEnd: continue - method = getattr(self.backend, backendSetterNames[rootKey], None) + assert self.writableBackend is not None + method = getattr( + self.writableBackend, backendSetterNames[rootKey], None + ) if method is None: logger.info(f"No backend write method found for {rootKey}") continue