Skip to content

Commit 803c69d

Browse files
authored
Add more type hints (#11)
* Add more type hints * Force typing linting on whole codebase * Fix override import * Use assert_type * Fixup assert_type * Fix * Fix * Fix * Fix 3.10 * Bump version * Rename assert_type to assert_cast * Fix ge * Fix .. check in DotDirectoryEntry2.verify() * Raise warning for DotDirectoryEntry2.verify() on ignore * Fix annotation * Fix Ext4Struct.field_type() * Fix commented out code * Fix annotations * Fix volume seek/read bugs * Fix bit shifting * Fix ge's other branch * Fix expected_magic typing * Fix .. checking * Fix seeking out of bounds * Different cache per volume * Fix linting and caching * Get info_length from DXRoot * Don't hide invalid dirents * Clean up inode flag checking * Fix is_inline vs has_extents * Fix info_length * Fix checksum calculation * Fix cache key * Fix ExtentBlocks.__iter__ * Stacklevel changes * Fix Directory._get_file_type()
1 parent 7a66cec commit 803c69d

15 files changed

Lines changed: 826 additions & 462 deletions

‎Makefile‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ ${VENV_BIN_ACTIVATE}: requirements.txt
4949
@echo "Setting up development virtual env in .venv"
5050
$(PYTHON) -m venv .venv
5151
. ${VENV_BIN_ACTIVATE}; \
52-
$(PYTHON) -m pip install wheel build ruff; \
52+
$(PYTHON) -m pip install \
53+
wheel \
54+
build \
55+
ruff \
56+
basedpyright; \
5357
$(PYTHON) -m pip install \
5458
-r requirements.txt
5559

@@ -60,11 +64,13 @@ all: release
6064

6165
lint: $(VENV_BIN_ACTIVATE)
6266
. $(VENV_BIN_ACTIVATE); \
63-
$(PYTHON) -m ruff check
67+
$(PYTHON) -m ruff check; \
68+
$(PYTHON) -m basedpyright
6469

6570
lint-fix: $(VENV_BIN_ACTIVATE)
6671
. $(VENV_BIN_ACTIVATE); \
67-
$(PYTHON) -m ruff check --fix
72+
$(PYTHON) -m ruff check --fix; \
73+
$(PYTHON) -m basedpyright
6874

6975
format: $(VENV_BIN_ACTIVATE)
7076
. $(VENV_BIN_ACTIVATE); \

‎ext4/_compat.py‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import os
22
from typing import Protocol
33
from typing import runtime_checkable
4+
from typing import TypeVar
5+
from typing import Any
46

57
# Added in python 3.12
68
try:
79
from typing import override # pyright: ignore[reportAssignmentType]
810

911
except ImportError:
1012
from typing import Callable
11-
from typing import Any
1213

1314
def override(fn: Callable[..., Any]): # pyright: ignore[reportExplicitAny]
1415
return fn
@@ -28,4 +29,12 @@ class PeekableStream(ReadableStream, Protocol):
2829
def peek(self, size: int = 0, /) -> bytes: ...
2930

3031

31-
__all__ = ["override", "ReadableStream"]
32+
T = TypeVar("T")
33+
34+
35+
def assert_cast(obj: Any, t: type[T], /) -> T: # pyright: ignore[reportExplicitAny, reportAny]
36+
assert isinstance(obj, t), f"Object is: {type(obj)} not {t}" # pyright: ignore[reportAny]
37+
return obj
38+
39+
40+
__all__ = ["override", "ReadableStream", "PeekableStream", "assert_cast"]

‎ext4/block.py‎

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
# pyright: reportImportCycles=false
12
import io
23
import errno
34

45
from ._compat import override
56

7+
from typing import TYPE_CHECKING
8+
9+
if TYPE_CHECKING:
10+
from .inode import Inode
11+
612

713
class BlockIOBlocks(object):
814
def __init__(self, blockio: "BlockIO"):
@@ -17,43 +23,27 @@ def block_size(self):
1723
def volume(self):
1824
return self.blockio.inode.volume
1925

20-
@property
21-
def ee_start(self):
22-
return self.blockio.ee_start
23-
24-
@property
25-
def ee_block(self):
26-
return self.blockio.ee_block
27-
28-
@property
29-
def ee_len(self):
30-
return self.blockio.ee_len
31-
32-
def __contains__(self, ee_block):
26+
def __contains__(self, ee_block: int):
3327
for extent in self.blockio.extents:
3428
if ee_block in extent.blocks:
3529
return True
3630

3731
return False
3832

39-
def __getitem__(self, ee_block):
33+
def __getitem__(self, ee_block: int):
4034
for extent in self.blockio.extents:
4135
if ee_block not in extent.blocks:
4236
continue
4337

44-
block = extent.blocks[ee_block]
45-
if block is None:
46-
break
47-
48-
return block
38+
return extent.blocks[ee_block]
4939

5040
return self._null_block
5141

5242

5343
class BlockIO(io.RawIOBase):
54-
def __init__(self, inode):
44+
def __init__(self, inode: "Inode"):
5545
super().__init__()
56-
self.inode = inode
46+
self.inode: "Inode" = inode
5747
self.cursor: int = 0
5848
self.blocks: BlockIOBlocks = BlockIOBlocks(self)
5949

‎ext4/blockdescriptor.py‎

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1+
# pyright: reportImportCycles=false
12
from ctypes import c_uint32
23
from ctypes import c_uint16
34

5+
from typing import final
6+
from typing import TYPE_CHECKING
7+
48
from .enum import EXT4_BG
59
from .struct import Ext4Struct
610
from .struct import crc32c
11+
from ._compat import assert_cast
12+
13+
if TYPE_CHECKING:
14+
from .volume import Volume
715

816

17+
@final
918
class BlockDescriptor(Ext4Struct):
1019
_pack_ = 1
1120
# _anonymous_ = ("bg_reserved",)
@@ -35,85 +44,105 @@ class BlockDescriptor(Ext4Struct):
3544
("bg_reserved", c_uint32),
3645
]
3746

38-
def __init__(self, volume, offset, bg_no):
47+
def __init__(self, volume: "Volume", offset: int, bg_no: int):
3948
super().__init__(volume, offset)
40-
self.bg_no = bg_no
49+
self.bg_no: int = bg_no
4150

4251
@property
43-
def bg_block_bitmap(self):
52+
def bg_block_bitmap(self) -> int:
53+
bg_block_bitmap_lo = assert_cast(self.bg_block_bitmap_lo, int) # pyright: ignore[reportAny]
54+
bg_block_bitmap_hi = assert_cast(self.bg_block_bitmap_hi, int) # pyright: ignore[reportAny]
4455
if self.volume.has_hi:
45-
return self.bg_block_bitmap_hi << 32 | self.bg_block_bitmap_lo
56+
return bg_block_bitmap_hi << 32 | bg_block_bitmap_lo
4657

47-
return self.bg_block_bitmap_lo
58+
return bg_block_bitmap_lo
4859

4960
@property
50-
def bg_inode_bitmap(self):
61+
def bg_inode_bitmap(self) -> int:
62+
bg_inode_bitmap_lo = assert_cast(self.bg_inode_bitmap_lo, int) # pyright: ignore[reportAny]
63+
bg_inode_bitmap_hi = assert_cast(self.bg_inode_bitmap_hi, int) # pyright: ignore[reportAny]
5164
if self.volume.has_hi:
52-
return self.bg_inode_bitmap_hi << 32 | self.bg_inode_bitmap_lo
65+
return bg_inode_bitmap_hi << 32 | bg_inode_bitmap_lo
5366

54-
return self.bg_inode_bitmap_lo
67+
return bg_inode_bitmap_lo
5568

5669
@property
57-
def bg_free_blocks_count(self):
70+
def bg_free_blocks_count(self) -> int:
71+
bg_free_blocks_count_lo = assert_cast(self.bg_free_blocks_count_lo, int) # pyright: ignore[reportAny]
72+
bg_free_blocks_count_hi = assert_cast(self.bg_free_blocks_count_hi, int) # pyright: ignore[reportAny]
5873
if self.volume.has_hi:
59-
return self.bg_free_blocks_count_hi << 32 | self.bg_free_blocks_count_lo
74+
return bg_free_blocks_count_hi << 16 | bg_free_blocks_count_lo
6075

61-
return self.bg_free_blocks_count_lo
76+
return bg_free_blocks_count_lo
6277

6378
@property
64-
def bg_free_inodes_count(self):
79+
def bg_free_inodes_count(self) -> int:
80+
bg_free_inodes_count_lo = assert_cast(self.bg_free_inodes_count_lo, int) # pyright: ignore[reportAny]
81+
bg_free_inodes_count_hi = assert_cast(self.bg_free_inodes_count_hi, int) # pyright: ignore[reportAny]
6582
if self.volume.has_hi:
66-
return self.bg_free_inodes_count_hi << 32 | self.bg_free_inodes_count_lo
83+
return bg_free_inodes_count_hi << 16 | bg_free_inodes_count_lo
6784

68-
return self.bg_free_inodes_count_lo
85+
return bg_free_inodes_count_lo
6986

7087
@property
71-
def bg_exclude_bitmap(self):
88+
def bg_exclude_bitmap(self) -> int:
89+
bg_exclude_bitmap_lo = assert_cast(self.bg_exclude_bitmap_lo, int) # pyright: ignore[reportAny]
90+
bg_exclude_bitmap_hi = assert_cast(self.bg_exclude_bitmap_hi, int) # pyright: ignore[reportAny]
7291
if self.volume.has_hi:
73-
return self.bg_exclude_bitmap_hi << 32 | self.bg_exclude_bitmap_lo
92+
return bg_exclude_bitmap_hi << 32 | bg_exclude_bitmap_lo
7493

75-
return self.bg_exclude_bitmap_lo
94+
return bg_exclude_bitmap_lo
7695

7796
@property
78-
def bg_used_dirs_count(self):
97+
def bg_used_dirs_count(self) -> int:
98+
bg_used_dirs_count_lo = assert_cast(self.bg_used_dirs_count_lo, int) # pyright: ignore[reportAny]
99+
bg_used_dirs_count_hi = assert_cast(self.bg_used_dirs_count_hi, int) # pyright: ignore[reportAny]
79100
if self.volume.has_hi:
80-
return self.bg_used_dirs_count_hi << 32 | self.bg_used_dirs_count_lo
101+
return bg_used_dirs_count_hi << 16 | bg_used_dirs_count_lo
81102

82-
return self.bg_used_dirs_count_lo
103+
return bg_used_dirs_count_lo
83104

84105
@property
85-
def bg_block_bitmap_csum(self):
106+
def bg_block_bitmap_csum(self) -> int:
107+
bg_block_bitmap_csum_lo = assert_cast(self.bg_block_bitmap_csum_lo, int) # pyright: ignore[reportAny]
108+
bg_block_bitmap_csum_hi = assert_cast(self.bg_block_bitmap_csum_hi, int) # pyright: ignore[reportAny]
86109
if self.volume.has_hi:
87-
return self.bg_block_bitmap_csum_hi << 32 | self.bg_block_bitmap_csum_lo
110+
return bg_block_bitmap_csum_hi << 16 | bg_block_bitmap_csum_lo
88111

89-
return self.bg_block_bitmap_csum_lo
112+
return bg_block_bitmap_csum_lo
90113

91114
@property
92-
def bg_inode_bitmap_csum(self):
115+
def bg_inode_bitmap_csum(self) -> int:
116+
bg_inode_bitmap_csum_lo = assert_cast(self.bg_inode_bitmap_csum_lo, int) # pyright: ignore[reportAny]
117+
bg_inode_bitmap_csum_hi = assert_cast(self.bg_inode_bitmap_csum_hi, int) # pyright: ignore[reportAny]
93118
if self.volume.has_hi:
94-
return self.bg_inode_bitmap_csum_hi << 32 | self.bg_inode_bitmap_csum_lo
119+
return bg_inode_bitmap_csum_hi << 16 | bg_inode_bitmap_csum_lo
95120

96-
return self.bg_inode_bitmap_csum_lo
121+
return bg_inode_bitmap_csum_lo
97122

98123
@property
99-
def bg_itable_unused(self):
124+
def bg_itable_unused(self) -> int:
125+
bg_itable_unused_lo = assert_cast(self.bg_itable_unused_lo, int) # pyright: ignore[reportAny]
126+
bg_itable_unused_hi = assert_cast(self.bg_itable_unused_hi, int) # pyright: ignore[reportAny]
100127
if self.volume.has_hi:
101-
return self.bg_itable_unused_hi << 32 | self.bg_itable_unused_lo
128+
return bg_itable_unused_hi << 16 | bg_itable_unused_lo
102129

103-
return self.bg_itable_unused_lo
130+
return bg_itable_unused_lo
104131

105132
@property
106-
def bg_inode_table(self):
133+
def bg_inode_table(self) -> int:
134+
bg_inode_table_lo = assert_cast(self.bg_inode_table_lo, int) # pyright: ignore[reportAny]
135+
bg_inode_table_hi = assert_cast(self.bg_inode_table_hi, int) # pyright: ignore[reportAny]
107136
if self.volume.has_hi:
108-
return (self.bg_inode_table_hi << 32) + self.bg_inode_table_lo
137+
return (bg_inode_table_hi << 32) + bg_inode_table_lo
109138

110-
return self.bg_inode_table_lo
139+
return bg_inode_table_lo
111140

112141
@property
113142
def superblock(self):
114143
return self.volume.superblock
115144

116-
@property
145+
@Ext4Struct.checksum.getter
117146
def checksum(self):
118147
csum = crc32c(self.bg_no.to_bytes(4, "little"), self.volume.seed)
119148
csum = crc32c(bytes(self)[: BlockDescriptor.bg_checksum.offset], csum)
@@ -124,6 +153,7 @@ def checksum(self):
124153
)
125154
return csum & 0xFFFF
126155

127-
@property
128-
def expected_checksum(self):
129-
return self.bg_checksum
156+
@Ext4Struct.expected_checksum.getter
157+
def expected_checksum(self) -> int:
158+
bg_checksum = assert_cast(self.bg_checksum, int) # pyright: ignore[reportAny]
159+
return bg_checksum

0 commit comments

Comments
 (0)