Skip to content

Commit c9bac13

Browse files
committed
Change linter to Ruff
1 parent 16bc3de commit c9bac13

9 files changed

+116
-86
lines changed

dissect/squashfs/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
__all__ = [
1111
"Error",
1212
"FileNotFoundError",
13+
"FileStream",
14+
"INode",
1315
"NotADirectoryError",
1416
"NotAFileError",
1517
"NotASymlinkError",
16-
"FileStream",
17-
"INode",
1818
"SquashFS",
1919
]

dissect/squashfs/c_squashfs.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import stat
24

35
from dissect.cstruct import cstruct

dissect/squashfs/compression.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
from __future__ import annotations
22

33
import importlib
4-
from typing import Optional
54

65
from dissect.squashfs.c_squashfs import c_squashfs
76

87

9-
def initialize(id: int, options: Optional[bytes]) -> Compression:
8+
def initialize(id: int, options: bytes | None) -> Compression:
109
# Options have no effect on decompression, so ignore for now
1110
modules = {
1211
c_squashfs.ZLIB_COMPRESSION: (NativeZlib,),
@@ -21,10 +20,10 @@ def initialize(id: int, options: Optional[bytes]) -> Compression:
2120
for mod in modules[id]:
2221
try:
2322
return mod()
24-
except ModuleNotFoundError:
23+
except ModuleNotFoundError: # noqa: PERF203
2524
pass
2625
else:
27-
raise ImportError(f"No modules available ({modules[id]})")
26+
raise ImportError(f"No modules available ({modules[id]})") # noqa: TRY301
2827
except ImportError:
2928
raise ValueError(f"Compression ID {id} requested but module ({modules[id]}) is not available")
3029
except KeyError:
@@ -38,10 +37,10 @@ def __init__(self):
3837
self._module = importlib.import_module(self.module)
3938

4039
def compress(self, data: bytes) -> bytes:
41-
raise NotImplementedError()
40+
raise NotImplementedError
4241

4342
def decompress(self, data: bytes, expected: int) -> bytes:
44-
raise NotImplementedError()
43+
raise NotImplementedError
4544

4645

4746
class NativeZlib(Compression):

dissect/squashfs/squashfs.py

+24-35
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
import stat
88
import struct
99
from bisect import bisect_right
10-
from datetime import datetime
1110
from functools import cache, cached_property, lru_cache
12-
from typing import BinaryIO, Iterator, Optional, Union
11+
from typing import TYPE_CHECKING, BinaryIO
1312

1413
from dissect.util import ts
1514
from dissect.util.stream import RunlistStream
@@ -23,6 +22,10 @@
2322
NotASymlinkError,
2423
)
2524

25+
if TYPE_CHECKING:
26+
from collections.abc import Iterator
27+
from datetime import datetime
28+
2629

2730
class SquashFS:
2831
def __init__(self, fh: BinaryIO):
@@ -68,15 +71,15 @@ def inode(
6871
self,
6972
block: int,
7073
offset: int,
71-
name: Optional[str] = None,
72-
type: Optional[int] = None,
73-
inode_number: Optional[int] = None,
74-
parent: Optional[INode] = None,
74+
name: str | None = None,
75+
type: int | None = None,
76+
inode_number: int | None = None,
77+
parent: INode | None = None,
7578
) -> INode:
7679
# squashfs inode numbers consist of a block number and offset in that block
7780
return INode(self, block, offset, name, type, inode_number, parent)
7881

79-
def get(self, path: Union[str, int], node: Optional[INode] = None) -> INode:
82+
def get(self, path: str | int, node: INode | None = None) -> INode:
8083
if isinstance(path, int):
8184
return self.inode(path >> 16, path & 0xFFFF)
8285

@@ -135,7 +138,7 @@ def _read_metadata(self, block: int, offset: int, length: int) -> tuple[int, int
135138

136139
return block, offset, b"".join(result)
137140

138-
def _read_block(self, block: int, length: Optional[int] = None) -> tuple[int, bytes]:
141+
def _read_block(self, block: int, length: int | None = None) -> tuple[int, bytes]:
139142
if length is not None:
140143
# Data block
141144
compressed = length & c_squashfs.SQUASHFS_COMPRESSED_BIT_BLOCK == 0
@@ -177,13 +180,12 @@ def _lookup_inode(self, inode_number: int) -> INode:
177180
_, _, data = self._read_metadata(self.lookup_table[block], offset, 8)
178181
return self.get(struct.unpack("<Q", data)[0])
179182

180-
def _lookup_fragment(self, fragment: int) -> bytes:
183+
def _lookup_fragment(self, fragment: int) -> c_squashfs.squashfs_fragment_entry:
181184
fragment_offset = fragment * len(c_squashfs.squashfs_fragment_entry)
182185
block, offset = divmod(fragment_offset, c_squashfs.SQUASHFS_METADATA_SIZE)
183186

184187
_, _, data = self._read_metadata(self.fragment_table[block], offset, len(c_squashfs.squashfs_fragment_entry))
185-
entry = c_squashfs.squashfs_fragment_entry(data)
186-
return entry
188+
return c_squashfs.squashfs_fragment_entry(data)
187189

188190
def iter_inodes(self) -> Iterator[INode]:
189191
for inum in range(1, self.sb.inodes + 1):
@@ -196,10 +198,10 @@ def __init__(
196198
fs: SquashFS,
197199
block: int,
198200
offset: int,
199-
name: Optional[str] = None,
200-
type: Optional[int] = None,
201-
inode_number: Optional[int] = None,
202-
parent: Optional[INode] = None,
201+
name: str | None = None,
202+
type: int | None = None,
203+
inode_number: int | None = None,
204+
parent: INode | None = None,
203205
):
204206
self.fs = fs
205207
self.block = block
@@ -209,6 +211,8 @@ def __init__(
209211
self._inode_number = inode_number
210212
self.parent = parent
211213

214+
self.listdir = cache(self.listdir)
215+
212216
def __repr__(self) -> str:
213217
return f"<inode {self.inode_number} ({self.block}, {self.offset})>"
214218

@@ -220,15 +224,9 @@ def _metadata(
220224
| c_squashfs.squashfs_reg_inode_header
221225
| c_squashfs.squashfs_symlink_inode_header
222226
| c_squashfs.squashfs_dev_inode_header
223-
| c_squashfs.squashfs_dev_inode_header
224-
| c_squashfs.squashfs_base_inode_header
225-
| c_squashfs.squashfs_base_inode_header
226227
| c_squashfs.squashfs_ldir_inode_header
227228
| c_squashfs.squashfs_lreg_inode_header
228-
| c_squashfs.squashfs_symlink_inode_header
229-
| c_squashfs.squashfs_ldev_inode_header
230229
| c_squashfs.squashfs_ldev_inode_header
231-
| c_squashfs.squashfs_lipc_inode_header
232230
| c_squashfs.squashfs_lipc_inode_header,
233231
int,
234232
int,
@@ -262,16 +260,10 @@ def header(
262260
| c_squashfs.squashfs_reg_inode_header
263261
| c_squashfs.squashfs_symlink_inode_header
264262
| c_squashfs.squashfs_dev_inode_header
265-
| c_squashfs.squashfs_dev_inode_header
266-
| c_squashfs.squashfs_base_inode_header
267-
| c_squashfs.squashfs_base_inode_header
268263
| c_squashfs.squashfs_ldir_inode_header
269264
| c_squashfs.squashfs_lreg_inode_header
270-
| c_squashfs.squashfs_symlink_inode_header
271-
| c_squashfs.squashfs_ldev_inode_header
272265
| c_squashfs.squashfs_ldev_inode_header
273266
| c_squashfs.squashfs_lipc_inode_header
274-
| c_squashfs.squashfs_lipc_inode_header
275267
):
276268
header, _, _ = self._metadata()
277269
return header
@@ -315,11 +307,12 @@ def mtime(self) -> datetime:
315307
return ts.from_unix(self.header.mtime)
316308

317309
@property
318-
def size(self) -> Optional[int]:
310+
def size(self) -> int | None:
319311
if self.is_dir() or self.is_file():
320312
return self.header.file_size
321-
elif self.is_symlink():
313+
if self.is_symlink():
322314
return self.header.symlink_size
315+
return None
323316

324317
def is_dir(self) -> bool:
325318
return self.type == stat.S_IFDIR
@@ -363,13 +356,9 @@ def link(self) -> str:
363356
@cached_property
364357
def link_inode(self) -> INode:
365358
link = self.link
366-
if link.startswith("/"):
367-
relnode = None
368-
else:
369-
relnode = self.parent
359+
relnode = None if link.startswith("/") else self.parent
370360
return self.fs.get(self.link, relnode)
371361

372-
@cache
373362
def listdir(self) -> dict[str, INode]:
374363
return {inode.name: inode for inode in self.iterdir()}
375364

@@ -407,7 +396,7 @@ def iterdir(self) -> Iterator[INode]:
407396
)
408397

409398
@cached_property
410-
def block_list(self) -> list[tuple[Optional[int], int]]:
399+
def block_list(self) -> list[tuple[int | None, int]]:
411400
fragment = self.header.fragment
412401
file_size = self.header.file_size
413402
if fragment == c_squashfs.SQUASHFS_INVALID_FRAG:

pyproject.toml

+48-5
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,56 @@ dev = [
5353
"dissect.util>=3.0.dev,<4.0.dev",
5454
]
5555

56-
[tool.black]
56+
[tool.ruff]
5757
line-length = 120
58+
required-version = ">=0.9.0"
5859

59-
[tool.isort]
60-
profile = "black"
61-
known_first_party = ["dissect.squashfs"]
62-
known_third_party = ["dissect"]
60+
[tool.ruff.format]
61+
docstring-code-format = true
62+
63+
[tool.ruff.lint]
64+
select = [
65+
"F",
66+
"E",
67+
"W",
68+
"I",
69+
"UP",
70+
"YTT",
71+
"ANN",
72+
"B",
73+
"C4",
74+
"DTZ",
75+
"T10",
76+
"FA",
77+
"ISC",
78+
"G",
79+
"INP",
80+
"PIE",
81+
"PYI",
82+
"PT",
83+
"Q",
84+
"RSE",
85+
"RET",
86+
"SLOT",
87+
"SIM",
88+
"TID",
89+
"TCH",
90+
"PTH",
91+
"PLC",
92+
"TRY",
93+
"FLY",
94+
"PERF",
95+
"FURB",
96+
"RUF",
97+
]
98+
ignore = ["E203", "B904", "UP024", "ANN002", "ANN003", "ANN204", "ANN401", "SIM105", "TRY003"]
99+
100+
[tool.ruff.lint.per-file-ignores]
101+
"tests/docs/**" = ["INP001"]
102+
103+
[tool.ruff.lint.isort]
104+
known-first-party = ["dissect.squashfs"]
105+
known-third-party = ["dissect"]
63106

64107
[tool.setuptools]
65108
license-files = ["LICENSE", "COPYRIGHT"]

tests/conftest.py

+22-16
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,59 @@
1+
from __future__ import annotations
2+
13
import gzip
2-
import os
4+
from pathlib import Path
5+
from typing import TYPE_CHECKING, BinaryIO
36

47
import pytest
58

9+
if TYPE_CHECKING:
10+
from collections.abc import Iterator
11+
612

7-
def absolute_path(filename):
8-
return os.path.join(os.path.dirname(__file__), filename)
13+
def absolute_path(filename: str) -> Path:
14+
return Path(__file__).parent / filename
915

1016

11-
def open_file(name, mode="rb"):
12-
with open(absolute_path(name), mode) as f:
13-
yield f
17+
def open_file(name: str, mode: str = "rb") -> Iterator[BinaryIO]:
18+
with absolute_path(name).open(mode) as fh:
19+
yield fh
1420

1521

16-
def open_file_gz(name, mode="rb"):
17-
with gzip.GzipFile(absolute_path(name), mode) as f:
18-
yield f
22+
def open_file_gz(name: str, mode: str = "rb") -> Iterator[BinaryIO]:
23+
with gzip.GzipFile(absolute_path(name), mode) as fh:
24+
yield fh
1925

2026

2127
@pytest.fixture
22-
def gzip_sqfs():
28+
def gzip_sqfs() -> Iterator[BinaryIO]:
2329
yield from open_file("data/gzip.sqfs")
2430

2531

2632
@pytest.fixture
27-
def gzip_opts_sqfs():
33+
def gzip_opts_sqfs() -> Iterator[BinaryIO]:
2834
yield from open_file("data/gzip-opts.sqfs")
2935

3036

3137
@pytest.fixture
32-
def lz4_sqfs():
38+
def lz4_sqfs() -> Iterator[BinaryIO]:
3339
yield from open_file("data/lz4.sqfs")
3440

3541

3642
@pytest.fixture
37-
def lzma_sqfs():
43+
def lzma_sqfs() -> Iterator[BinaryIO]:
3844
yield from open_file("data/lzma.sqfs")
3945

4046

4147
@pytest.fixture
42-
def lzo_sqfs():
48+
def lzo_sqfs() -> Iterator[BinaryIO]:
4349
yield from open_file("data/lzo.sqfs")
4450

4551

4652
@pytest.fixture
47-
def xz_sqfs():
53+
def xz_sqfs() -> Iterator[BinaryIO]:
4854
yield from open_file("data/xz.sqfs")
4955

5056

5157
@pytest.fixture
52-
def zstd_sqfs():
58+
def zstd_sqfs() -> Iterator[BinaryIO]:
5359
yield from open_file("data/zstd.sqfs")

tests/test_exceptions.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
from __future__ import annotations
2+
13
import pytest
24

35
from dissect.squashfs import exceptions
46

57

68
@pytest.mark.parametrize(
7-
"exc, std",
9+
("exc", "std"),
810
[
911
(exceptions.FileNotFoundError, FileNotFoundError),
1012
(exceptions.IsADirectoryError, IsADirectoryError),

0 commit comments

Comments
 (0)