Skip to content

Commit b5fcd20

Browse files
committed
Some better type hints on FileCacher / LargeObject
Use typing.IO[bytes] instead of BinaryIO, because some stdlib types are apparently only compatible with the former.
1 parent 7ae2783 commit b5fcd20

3 files changed

Lines changed: 17 additions & 17 deletions

File tree

cms/db/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def sa_mapper(self):
7474
return object_mapper(self)
7575

7676
@property
77-
def sa_session(self):
77+
def sa_session(self) -> Session:
7878
return object_session(self)
7979

8080
@property

cms/db/filecacher.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class FileCacherBackend(metaclass=ABCMeta):
9090
"""
9191

9292
@abstractmethod
93-
def get_file(self, digest: str) -> typing.BinaryIO:
93+
def get_file(self, digest: str) -> typing.IO[bytes]:
9494
"""Retrieve a file from the storage.
9595
9696
digest: the digest of the file to retrieve.
@@ -104,7 +104,7 @@ def get_file(self, digest: str) -> typing.BinaryIO:
104104
pass
105105

106106
@abstractmethod
107-
def create_file(self, digest: str) -> typing.BinaryIO | None:
107+
def create_file(self, digest: str) -> typing.IO[bytes] | None:
108108
"""Create an empty file that will live in the storage.
109109
110110
Once the caller has written the contents to the file, the commit_file()
@@ -120,7 +120,7 @@ def create_file(self, digest: str) -> typing.BinaryIO | None:
120120
pass
121121

122122
@abstractmethod
123-
def commit_file(self, fobj: typing.BinaryIO, digest: str, desc: str = "") -> bool:
123+
def commit_file(self, fobj: typing.IO[bytes], digest: str, desc: str = "") -> bool:
124124
"""Commit a file created by create_file() to be stored.
125125
126126
Given a file object returned by create_file(), this function populates
@@ -238,7 +238,6 @@ def create_file(self, digest):
238238
return None
239239

240240
# Create a temporary file in the same directory
241-
temp_file: typing.BinaryIO
242241
temp_file = tempfile.NamedTemporaryFile('wb', delete=False,
243242
prefix=".tmp.",
244243
suffix=digest,
@@ -305,11 +304,6 @@ def list(self):
305304
return list((x, "") for x in os.listdir(self.path))
306305

307306

308-
# this class has type-checking errors because our LargeObject type is not
309-
# compatible with typing.BinaryIO, despite being a valid IO object in practice.
310-
# This is mostly due to Python's IO types being difficult to handle statically.
311-
# Anyways, it should be fine to pretend that the values returned here are
312-
# typing.BinaryIO.
313307
class DBBackend(FileCacherBackend):
314308
"""This class implements an actual backend for FileCacher that
315309
stores the files as lobjects (encapsuled in a FSObject) into a
@@ -589,7 +583,7 @@ def precache_lock(self) -> io.TextIOBase | None:
589583
if not returned:
590584
fobj.close()
591585

592-
def _load(self, digest: str, cache_only: bool) -> typing.BinaryIO | None:
586+
def _load(self, digest: str, cache_only: bool) -> typing.IO[bytes] | None:
593587
"""Load a file into the cache and open it for reading.
594588
595589
cache_only: don't open the file for reading.
@@ -657,7 +651,7 @@ def cache_file(self, digest: str):
657651

658652
self._load(digest, True)
659653

660-
def get_file(self, digest: str) -> typing.BinaryIO:
654+
def get_file(self, digest: str) -> typing.IO[bytes]:
661655
"""Retrieve a file from the storage.
662656
663657
If it's available in the cache use that copy, without querying
@@ -703,7 +697,7 @@ def get_file_content(self, digest: str) -> bytes:
703697
with self.get_file(digest) as src:
704698
return src.read()
705699

706-
def get_file_to_fobj(self, digest: str, dst: typing.BinaryIO):
700+
def get_file_to_fobj(self, digest: str, dst: typing.IO[bytes]):
707701
"""Retrieve a file from the storage.
708702
709703
See `get_file'. This method will write the content of the file
@@ -741,7 +735,7 @@ def get_file_to_path(self, digest: str, dst_path: str):
741735
with open(dst_path, 'wb') as dst:
742736
copyfileobj(src, dst, self.CHUNK_SIZE)
743737

744-
def put_file_from_fobj(self, src: typing.BinaryIO, desc: str = "") -> str:
738+
def put_file_from_fobj(self, src: typing.IO[bytes], desc: str = "") -> str:
745739
"""Store a file in the storage.
746740
747741
If it's already (for some reason...) in the cache send that

cms/db/fsobject.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from . import Base, custom_psycopg2_connection, Session
3838

3939

40-
class LargeObject(io.RawIOBase):
40+
class LargeObject(io.RawIOBase, typing.BinaryIO):
4141

4242
"""Present a PostgreSQL large object as a Python file-object.
4343
@@ -119,7 +119,13 @@ def __init__(self, loid: int, mode: str = 'rb'):
119119
cursor.close()
120120

121121
# cursor is typed as typing.Any because psycopg2 doesn't have good type hints.
122-
def _execute(self, operation: str, parameters: dict[str, typing.Any], message: str, cursor: typing.Any | None = None) -> typing.Any:
122+
def _execute(
123+
self,
124+
operation: str,
125+
parameters: dict[str, typing.Any],
126+
message: str,
127+
cursor: typing.Any | None = None
128+
) -> typing.Any:
123129
"""Run the given query making many success checks.
124130
125131
Execute the given SQL statement, instantiated with the given
@@ -403,7 +409,7 @@ def delete(self):
403409
self.sa_session.delete(self)
404410

405411
@classmethod
406-
def get_from_digest(cls, digest: str, session: Session) -> Self:
412+
def get_from_digest(cls, digest: str, session: Session) -> Self | None:
407413
"""Return the FSObject with the specified digest, using the
408414
specified session.
409415

0 commit comments

Comments
 (0)