Skip to content

Commit ea2a4e7

Browse files
committed
Add more type hints
1 parent 2de4040 commit ea2a4e7

File tree

6 files changed

+117
-94
lines changed

6 files changed

+117
-94
lines changed

ext4/htree.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def verify(self):
2828
return
2929

3030
message = f"{self} dot or dotdot entry name invalid! actual={self.name}"
31-
if not self.ignore_magic:
31+
if not self.volume.ignore_magic:
3232
raise MagicError(message)
3333

3434

@@ -98,6 +98,8 @@ class DXRoot(DXEntriesBase):
9898

9999
def __init__(self, inode):
100100
super().__init__(inode, 0)
101+
self.dot.volume = self.inode.volume
102+
self.dotdot.volume = self.inode.volume
101103

102104

103105
class DXFake(LittleEndianStructure):
@@ -130,6 +132,9 @@ class DXNode(DXEntriesBase):
130132
# ("entries", DXEntry * self.count),
131133
]
132134

135+
def __init__(self, directory, offset):
136+
super().__init__(directory, offset)
137+
133138

134139
class DXTail(DXBase):
135140
_pack_ = 1

ext4/inode.py

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from ctypes import c_uint16
1010
from ctypes import sizeof
1111

12+
from collections.abc import Generator
13+
1214
from ._compat import override
1315

1416
from .struct import Ext4Struct
@@ -152,42 +154,42 @@ class Inode(Ext4Struct):
152154
("i_projid", c_uint32),
153155
]
154156

155-
def __new__(cls, volume, offset, i_no):
157+
def __new__(cls, volume, offset: int, i_no: int):
156158
if cls is not Inode:
157159
return super().__new__(cls)
158160

159-
volume.seek(offset + Inode.i_mode.offset)
161+
_ = volume.seek(offset + Inode.i_mode.offset)
160162
file_type = (
161163
Inode.field_type("i_mode").from_buffer_copy(volume.read(Inode.i_mode.size))
162164
& 0xF000
163165
)
164166
if file_type == MODE.IFIFO:
165-
return Fifo(volume, offset, i_no)
167+
return super().__new__(Fifo)
166168

167169
if file_type == MODE.IFDIR:
168-
return Directory(volume, offset, i_no)
170+
return super().__new__(Directory)
169171

170172
if file_type == MODE.IFREG:
171-
return File(volume, offset, i_no)
173+
return super().__new__(File)
172174

173175
if file_type == MODE.IFLNK:
174-
return SymbolicLink(volume, offset, i_no)
176+
return super().__new__(SymbolicLink)
175177

176178
if file_type == MODE.IFCHR:
177-
return CharacterDevice(volume, offset, i_no)
179+
return super().__new__(CharacterDevice)
178180

179181
if file_type == MODE.IFBLK:
180-
return BlockDevice(volume, offset, i_no)
182+
return super().__new__(BlockDevice)
181183

182184
if file_type == MODE.IFSOCK:
183-
return Socket(volume, offset, i_no)
185+
return super().__new__(Socket)
184186

185187
raise InodeError(f"Unknown file type 0x{file_type:X}")
186188

187-
def __init__(self, volume, offset, i_no):
188-
self.i_no = i_no
189+
def __init__(self, volume, offset: int, i_no: int):
190+
self.i_no: int = i_no
189191
super().__init__(volume, offset)
190-
self.tree = ExtentTree(self)
192+
self.tree: ExtentTree = ExtentTree(self)
191193

192194
@property
193195
def superblock(self):
@@ -206,27 +208,27 @@ def i_file_acl(self):
206208
return self.osd2.linux2.l_i_file_acl_high << 32 | self.i_file_acl_lo
207209

208210
@property
209-
def has_hi(self):
211+
def has_hi(self) -> bool:
210212
return self.superblock.s_inode_size > self.EXT2_GOOD_OLD_INODE_SIZE
211213

212214
@property
213-
def fits_in_hi(self):
215+
def fits_in_hi(self) -> bool:
214216
return (
215217
self.has_hi
216218
and self.i_checksum_hi.offset + self.i_checksum_hi.size
217219
<= self.EXT2_GOOD_OLD_INODE_SIZE + self.i_extra_isize
218220
)
219221

220222
@property
221-
def seed(self):
223+
def seed(self) -> int:
222224
seed = crc32c(self.i_no.to_bytes(4, "little"), self.volume.seed)
223225
return crc32c(
224226
self.i_generation.to_bytes(Inode.i_generation.size, "little"),
225227
seed,
226228
)
227229

228-
@property
229-
def checksum(self):
230+
@Ext4Struct.checksum.getter
231+
def checksum(self) -> int | None:
230232
if self.superblock.s_creator_os != EXT4_OS.LINUX:
231233
return None
232234

@@ -255,8 +257,8 @@ def checksum(self):
255257

256258
return csum
257259

258-
@property
259-
def expected_checksum(self):
260+
@Ext4Struct.expected_checksum.getter
261+
def expected_checksum(self) -> int | None:
260262
if self.superblock.s_creator_os != EXT4_OS.LINUX:
261263
return None
262264

@@ -267,6 +269,7 @@ def expected_checksum(self):
267269

268270
return provided_csum
269271

272+
@override
270273
def validate(self):
271274
super().validate()
272275
if self.tree is not None:
@@ -299,11 +302,13 @@ def _open(self, mode: str = "rb", encoding: None = None, newline: None = None):
299302

300303
return BlockIO(self)
301304

302-
def open(self, mode="rb", encoding=None, newline=None):
305+
def open(self, mode: str = "rb", encoding=None, newline=None):
303306
raise NotImplementedError()
304307

305308
@property
306-
def xattrs(self):
309+
def xattrs(
310+
self,
311+
) -> Generator[tuple[str, bytes], None, None]:
307312
inline_offset = self.offset + self.EXT2_GOOD_OLD_INODE_SIZE + self.i_extra_isize
308313
inline_size = self.offset + self.superblock.s_inode_size - inline_offset
309314
if inline_size > sizeof(ExtendedAttributeIBodyHeader):
@@ -359,38 +364,40 @@ def readlink(self):
359364

360365

361366
class Directory(Inode):
362-
def __init__(self, volume, offset, i_no):
367+
def __init__(self, volume, offset: int, i_no: int):
363368
super().__init__(volume, offset, i_no)
364-
self._dirents = None
369+
self._dirents: None | list[DirectoryEntry | DirectoryEntry2] = None
365370
if self.is_htree:
366371
self.htree = DXRoot(self)
367372

373+
@override
368374
def verify(self):
369375
super().verify()
370376
# TODO verify DirectoryEntryHash? Or should this be in validate?
371377

378+
@override
372379
def validate(self):
373380
super().validate()
374381
# TODO validate each directory entry block with DirectoryEntryTail
375382

376383
@property
377-
def has_filetype(self):
384+
def has_filetype(self) -> bool:
378385
return self.superblock.s_feature_incompat & EXT4_FEATURE_INCOMPAT.FILETYPE != 0
379386

380387
@property
381-
def is_htree(self):
388+
def is_htree(self) -> bool:
382389
return self.i_flags & EXT4_FL.INDEX != 0
383390

384391
@property
385-
def is_casefolded(self):
392+
def is_casefolded(self) -> bool:
386393
return self.i_flags & EXT4_FL.CASEFOLD != 0
387394

388395
@property
389-
def is_encrypted(self):
396+
def is_encrypted(self) -> bool:
390397
return self.i_flags & EXT4_FL.ENCRYPTED != 0
391398

392399
@property
393-
def hash_in_dirent(self):
400+
def hash_in_dirent(self) -> bool:
394401
return self.is_casefolded and self.is_encrypted
395402

396403
def _opendir(self):
@@ -401,7 +408,7 @@ def _opendir(self):
401408
return
402409

403410
_type = DirectoryEntry2 if self.has_filetype else DirectoryEntry
404-
dirents = []
411+
dirents: list[DirectoryEntry | DirectoryEntry2] = []
405412
offset = 0
406413
data = self._open().read()
407414
while offset < len(data):
@@ -424,8 +431,8 @@ def _opendir(self):
424431
if dirent.rec_len < expected_rec_len:
425432
warnings.warn(
426433
"Directory entry is too small for name length"
427-
f", expected={expected_rec_len}"
428-
f", actual={dirent.rec_len}",
434+
+ f", expected={expected_rec_len}"
435+
+ f", actual={dirent.rec_len}",
429436
RuntimeWarning,
430437
)
431438
break
@@ -437,13 +444,9 @@ def _opendir(self):
437444

438445
self._dirents = dirents
439446

440-
def _get_file_type(self, dirent):
447+
def _get_file_type(self, dirent: DirectoryEntry | DirectoryEntry2):
441448
offset = self.volume.inodes.offset(dirent.inode)
442-
self.volume.seek(offset + Inode.i_mode.offset)
443-
i_mode = Inode.field_type("i_mode").from_buffer_copy(
444-
self.volume.read(Inode.i_mode.size)
445-
)
446-
self.volume.seek(offset + Inode.i_mode.offset)
449+
_ = self.volume.seek(offset + Inode.i_mode.offset)
447450
i_mode = Inode.field_type("i_mode").from_buffer_copy(
448451
self.volume.read(Inode.i_mode.size)
449452
)

ext4/struct.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
from ctypes import addressof
66
from ctypes import sizeof
77
from crcmod import mkCrcFun
8+
from typing import cast
9+
from typing import Callable
10+
from typing import Any
811

9-
crc32c = mkCrcFun(0x11EDC6F41)
12+
crc32c = cast(Callable[..., int], mkCrcFun(0x11EDC6F41))
1013

1114

1215
class MagicError(Exception):
@@ -17,18 +20,18 @@ class ChecksumError(Exception):
1720
pass
1821

1922

20-
def to_hex(data):
23+
def to_hex(data: Any) -> str:
2124
if isinstance(data, int):
2225
return f"0x{data:02X}"
2326

2427
return "0x" + "".join([f"{x:02X}" for x in data])
2528

2629

2730
class Ext4Struct(LittleEndianStructure):
28-
def __init__(self, volume, offset):
31+
def __init__(self, volume, offset: int):
2932
super().__init__()
3033
self.volume = volume
31-
self.offset = offset
34+
self.offset: int = offset
3235
self.read_from_volume()
3336
self.verify()
3437

ext4/superblock.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,11 @@ def __init__(self, volume, _=None):
125125
super().__init__(volume, 0x400)
126126

127127
@property
128-
def has_hi(self):
128+
def has_hi(self) -> bool:
129129
return (self.s_feature_incompat & EXT4_FEATURE_INCOMPAT.IS64BIT) != 0
130130

131131
@property
132-
def s_blocks_count(self):
132+
def s_blocks_count(self) -> int:
133133
return (
134134
(self.s_blocks_per_group) * len(self.volume.group_descriptors)
135135
- self.s_reserved_gdt_blocks
@@ -158,19 +158,19 @@ def s_free_blocks_count(self):
158158
def metadata_csum(self):
159159
return self.s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT.METADATA_CSUM != 0
160160

161-
@property
161+
@Ext4Struct.expected_magic.getter
162162
def expected_magic(self):
163163
return 0xEF53
164164

165-
@property
165+
@Ext4Struct.magic.getter
166166
def magic(self):
167167
return self.s_magic
168168

169-
@property
169+
@Ext4Struct.expected_checksum.getter
170170
def expected_checksum(self):
171171
return self.s_checksum if self.metadata_csum else None
172172

173-
@property
173+
@Ext4Struct.checksum.getter
174174
def checksum(self):
175175
return (
176176
crc32c(bytes(self)[: Superblock.s_checksum.offset])

0 commit comments

Comments
 (0)