Skip to content

Commit ae5de8f

Browse files
committed
Use Python 3.7+ dataclasses to describe stream & datastore metadata
1 parent e8774be commit ae5de8f

File tree

3 files changed

+46
-76
lines changed

3 files changed

+46
-76
lines changed

datastore/core/util/metadata.py

Lines changed: 14 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import dataclasses
12
import typing
23

34

5+
@dataclasses.dataclass(frozen=True)
46
class _MetadataBase:
57
"""
68
Attributes
@@ -15,71 +17,44 @@ class _MetadataBase:
1517
Time of entry creation in seconds since the Unix epoch, or `None`
1618
if unknown
1719
"""
18-
__doc__: str
19-
__slots__ = ("atime", "mtime", "btime")
2020

2121
# The backing record's last access time
22-
atime: typing.Optional[typing.Union[int, float]]
22+
atime: typing.Optional[typing.Union[int, float]] = None
2323
# The backing record's last modification time
24-
mtime: typing.Optional[typing.Union[int, float]]
24+
mtime: typing.Optional[typing.Union[int, float]] = None
2525
# The backing record's creation (“birth”) time
26-
btime: typing.Optional[typing.Union[int, float]]
27-
28-
def __init__(
29-
self, *,
30-
atime: typing.Optional[int] = None,
31-
mtime: typing.Optional[int] = None,
32-
btime: typing.Optional[int] = None,
33-
):
34-
self.atime = atime
35-
self.mtime = mtime
36-
self.btime = btime
26+
btime: typing.Optional[typing.Union[int, float]] = None
3727

3828

29+
@dataclasses.dataclass(frozen=True)
3930
class ChannelMetadata(_MetadataBase):
40-
__doc__ = _MetadataBase.__doc__[:-1] + """\
31+
__doc__ = (_MetadataBase.__doc__ or "")[:-1] + """\
4132
count
4233
The number of objects that will be returned, or `None` if unavailable
4334
"""
44-
__slots__ = ("count",)
4535

4636
# The total length of this stream (if available)
47-
count: typing.Optional[int]
48-
49-
def __init__(self, *args: typing.Any, count: typing.Optional[int] = None,
50-
**kwargs: typing.Any):
51-
super().__init__(*args, **kwargs)
52-
self.count = count
37+
count: typing.Optional[int] = None
5338

5439

40+
@dataclasses.dataclass(frozen=True)
5541
class StreamMetadata(_MetadataBase):
56-
__doc__ = _MetadataBase.__doc__[:-1] + """\
42+
__doc__ = (_MetadataBase.__doc__ or "")[:-1] + """\
5743
size
58-
The si
59-
__doc__: strze of the entire stream data in bytes, or `None` if unavailable
44+
The size of the entire stream data in bytes, or `None` if unavailable
6045
"""
61-
__slots__ = ("size",)
6246

6347
# The total length of this stream (if available)
64-
size: typing.Optional[int]
65-
66-
def __init__(self, *args: typing.Any, size: typing.Optional[int] = None,
67-
**kwargs: typing.Any):
68-
super().__init__(*args, **kwargs)
69-
self.size = size
48+
size: typing.Optional[int] = None
7049

7150

51+
@dataclasses.dataclass(frozen=True)
7252
class DatastoreMetadata:
7353
"""
7454
Attributes
7555
----------
7656
size
7757
The size of the entire datastore in bytes, or `None` if unavailable
7858
"""
79-
__doc__: str
80-
__slots__ = ("size",)
81-
82-
size: typing.Optional[int]
8359

84-
def __init__(self, *, size: typing.Optional[int] = None):
85-
self.size = size
60+
size: typing.Optional[int] = None

datastore/core/util/stream.py

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,13 @@ class TeeingReceiveChannel(ReceiveChannel[T_co], typing.Generic[T_co]):
7979

8080
def __init__(self, source: trio.abc.ReceiveChannel[T_co], buffer_size: int = 0, *,
8181
_shared: typing.Optional[_TeeingChannelShared[T_co]] = None):
82-
super().__init__()
82+
# Try to copy extra attributes from source channel
83+
super().__init__(
84+
count = getattr(source, "count", None),
85+
atime = getattr(source, "atime", None),
86+
mtime = getattr(source, "mtime", None),
87+
btime = getattr(source, "btime", None),
88+
)
8389

8490
if _shared is not None:
8591
self._shared = _shared
@@ -89,11 +95,6 @@ def __init__(self, source: trio.abc.ReceiveChannel[T_co], buffer_size: int = 0,
8995
self._shared.bufsize = buffer_size
9096
self._shared.source = source
9197

92-
# Try to copy extra attributes from source channel
93-
self.count = getattr(source, "count", None)
94-
self.atime = getattr(source, "atime", None)
95-
self.mtime = getattr(source, "mtime", None)
96-
self.btime = getattr(source, "btime", None)
9798

9899
self._closed = False
99100

@@ -276,8 +277,9 @@ class _WrapingIterReceiveChannelBase(ReceiveChannel[T_co], typing.Generic[T_co,
276277
_shared: _WrapingChannelShared[U_co]
277278

278279
def __init__(self, source: typing.Optional[U_co], *,
280+
count: typing.Optional[int] = None,
279281
_shared: typing.Optional[_WrapingChannelShared[U_co]] = None):
280-
super().__init__()
282+
super().__init__(count=count)
281283

282284
assert source is not None or _shared is not None
283285

@@ -366,9 +368,8 @@ class _WrapingAsyncIterReceiveChannel(
366368
_WrapingIterReceiveChannelBase[T_co, typing.AsyncIterator[T_co]],
367369
typing.Generic[T_co]
368370
):
369-
def __init__(self, source: typing.Optional[typing.AsyncIterable[T_co]],
370-
**kwargs: typing.Any):
371-
super().__init__(source.__aiter__() if source is not None else None, **kwargs)
371+
def __init__(self, source: typing.Optional[typing.AsyncIterable[T_co]]):
372+
super().__init__(source.__aiter__() if source is not None else None)
372373

373374

374375
async def _receive(self) -> T_co:
@@ -397,11 +398,9 @@ class _WrapingSyncIterReceiveChannel(
397398
_WrapingIterReceiveChannelBase[T_co, typing.Iterator[T_co]],
398399
typing.Generic[T_co]
399400
):
400-
def __init__(self, source: typing.Optional[typing.Iterable[T_co]],
401-
count_hint: typing.Optional[int] = None, **kwargs: typing.Any):
402-
super().__init__(iter(source) if source is not None else None, **kwargs)
403-
404-
self.count = count_hint
401+
def __init__(self, source: typing.Optional[typing.Iterable[T_co]], *,
402+
count: typing.Optional[int] = None):
403+
super().__init__(iter(source) if source is not None else None, count=count)
405404

406405

407406
async def _receive(self) -> T_co:
@@ -465,7 +464,7 @@ async def await_iter_wrapper(channel: typing.Awaitable[T_co]) \
465464
if isinstance(source2, collections.abc.Sequence):
466465
count = len(source2)
467466

468-
return _WrapingSyncIterReceiveChannel(source2, count)
467+
return _WrapingSyncIterReceiveChannel(source2, count=count)
469468

470469
assert False, "Unreachable code"
471470

@@ -510,17 +509,19 @@ class TeeingReceiveStream(ReceiveStream):
510509
_source: typing.Optional[trio.abc.ReceiveStream]
511510

512511
def __init__(self, source: trio.abc.ReceiveStream, buffer_size: int = 0):
513-
super().__init__()
512+
# Try to copy extra attributes from source stream
513+
super().__init__(
514+
size = getattr(source, "size", None),
515+
atime = getattr(source, "atime", None),
516+
mtime = getattr(source, "mtime", None),
517+
btime = getattr(source, "btime", None),
518+
)
514519

515520
self._bufsize = buffer_size
516521
self._source = source
517522
self._closed = False
518523

519-
# Try to copy extra attributes from source stream
520-
self.size = getattr(source, "size", None)
521-
self.atime = getattr(source, "atime", None)
522-
self.mtime = getattr(source, "mtime", None)
523-
self.btime = getattr(source, "btime", None)
524+
524525

525526
# Create nursery without using a `async with`-statement
526527
# (Only works because the `__aenter__`-call does not actually block on anything.)
@@ -649,8 +650,8 @@ class _WrapingIterReceiveStreamBase(ReceiveStream, typing.Generic[T_co]):
649650

650651
_source: typing.Optional[T_co]
651652

652-
def __init__(self, source: T_co):
653-
super().__init__()
653+
def __init__(self, source: T_co, *, size: typing.Optional[int] = None):
654+
super().__init__(size=size)
654655

655656
self._source = source
656657

@@ -756,10 +757,8 @@ async def _close_source(self) -> None:
756757

757758

758759
class _WrapingSyncIterReceiveStream(_WrapingIterReceiveStreamBase[typing.Iterator[bytes]]):
759-
def __init__(self, source: typing.Iterable[bytes], size_hint: typing.Optional[int]):
760-
super().__init__(iter(source))
761-
762-
self.size = size_hint
760+
def __init__(self, source: typing.Iterable[bytes], *, size: typing.Optional[int] = None):
761+
super().__init__(iter(source), size=size)
763762

764763

765764
async def _receive(self, _: typing.Optional[int]) -> bytes:
@@ -833,6 +832,6 @@ async def await_iter_wrapper(stream: typing.Awaitable[bytes]) \
833832
size = source2.tell() - pos
834833
source2.seek(pos, io.SEEK_SET)
835834

836-
return _WrapingSyncIterReceiveStream(source2, size)
835+
return _WrapingSyncIterReceiveStream(source2, size=size)
837836

838837
assert False, "Unreachable code"

datastore/filesystem/filesystem.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import copy
2+
import dataclasses
23
import errno
34
import io
45
import json
@@ -183,19 +184,14 @@ async def from_path(cls, filepath: typing.Union[str, bytes, os_PathLike_str]) ->
183184
accuracy_t = typing_Literal["unknown", "initial-exact", "initial-approximate", "initial-timed-out"]
184185

185186

187+
@dataclasses.dataclass(frozen=True)
186188
class DatastoreMetadata(datastore.util.DatastoreMetadata):
187-
__doc__ = datastore.util.DatastoreMetadata.__doc__[:-1] + """\
189+
__doc__ = (datastore.util.DatastoreMetadata.__doc__ or "")[:-1] + """\
188190
size_accuracy
189191
The accuracy of the returned size value
190192
"""
191-
__slots__ = ("size_accuracy",)
192193

193-
size_accuracy: accuracy_t
194-
195-
def __init__(self, *args: typing.Any, size_accuracy: accuracy_t = "unknown",
196-
**kwargs: typing.Any):
197-
super().__init__(*args, **kwargs)
198-
self.size_accuracy = size_accuracy
194+
size_accuracy: accuracy_t = "unknown"
199195

200196

201197
# work around GH/mypy/mypy#731: no recursive structural types yet

0 commit comments

Comments
 (0)