Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change linter to Ruff #30

Merged
merged 1 commit into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions dissect/squashfs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
__all__ = [
"Error",
"FileNotFoundError",
"FileStream",
"INode",
"NotADirectoryError",
"NotAFileError",
"NotASymlinkError",
"FileStream",
"INode",
"SquashFS",
]
2 changes: 2 additions & 0 deletions dissect/squashfs/c_squashfs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import stat

from dissect.cstruct import cstruct
Expand Down
11 changes: 5 additions & 6 deletions dissect/squashfs/compression.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from __future__ import annotations

import importlib
from typing import Optional

from dissect.squashfs.c_squashfs import c_squashfs


def initialize(id: int, options: Optional[bytes]) -> Compression:
def initialize(id: int, options: bytes | None) -> Compression:
# Options have no effect on decompression, so ignore for now
modules = {
c_squashfs.ZLIB_COMPRESSION: (NativeZlib,),
Expand All @@ -21,10 +20,10 @@
for mod in modules[id]:
try:
return mod()
except ModuleNotFoundError:
except ModuleNotFoundError: # noqa: PERF203

Check warning on line 23 in dissect/squashfs/compression.py

View check run for this annotation

Codecov / codecov/patch

dissect/squashfs/compression.py#L23

Added line #L23 was not covered by tests
pass
else:
raise ImportError(f"No modules available ({modules[id]})")
raise ImportError(f"No modules available ({modules[id]})") # noqa: TRY301

Check warning on line 26 in dissect/squashfs/compression.py

View check run for this annotation

Codecov / codecov/patch

dissect/squashfs/compression.py#L26

Added line #L26 was not covered by tests
except ImportError:
raise ValueError(f"Compression ID {id} requested but module ({modules[id]}) is not available")
except KeyError:
Expand All @@ -38,10 +37,10 @@
self._module = importlib.import_module(self.module)

def compress(self, data: bytes) -> bytes:
raise NotImplementedError()
raise NotImplementedError

Check warning on line 40 in dissect/squashfs/compression.py

View check run for this annotation

Codecov / codecov/patch

dissect/squashfs/compression.py#L40

Added line #L40 was not covered by tests

def decompress(self, data: bytes, expected: int) -> bytes:
raise NotImplementedError()
raise NotImplementedError

Check warning on line 43 in dissect/squashfs/compression.py

View check run for this annotation

Codecov / codecov/patch

dissect/squashfs/compression.py#L43

Added line #L43 was not covered by tests


class NativeZlib(Compression):
Expand Down
59 changes: 24 additions & 35 deletions dissect/squashfs/squashfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
import stat
import struct
from bisect import bisect_right
from datetime import datetime
from functools import cache, cached_property, lru_cache
from typing import BinaryIO, Iterator, Optional, Union
from typing import TYPE_CHECKING, BinaryIO

from dissect.util import ts
from dissect.util.stream import RunlistStream
Expand All @@ -23,6 +22,10 @@
NotASymlinkError,
)

if TYPE_CHECKING:
from collections.abc import Iterator
from datetime import datetime

Check warning on line 27 in dissect/squashfs/squashfs.py

View check run for this annotation

Codecov / codecov/patch

dissect/squashfs/squashfs.py#L26-L27

Added lines #L26 - L27 were not covered by tests


class SquashFS:
def __init__(self, fh: BinaryIO):
Expand Down Expand Up @@ -68,15 +71,15 @@
self,
block: int,
offset: int,
name: Optional[str] = None,
type: Optional[int] = None,
inode_number: Optional[int] = None,
parent: Optional[INode] = None,
name: str | None = None,
type: int | None = None,
inode_number: int | None = None,
parent: INode | None = None,
) -> INode:
# squashfs inode numbers consist of a block number and offset in that block
return INode(self, block, offset, name, type, inode_number, parent)

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

Expand Down Expand Up @@ -135,7 +138,7 @@

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

def _read_block(self, block: int, length: Optional[int] = None) -> tuple[int, bytes]:
def _read_block(self, block: int, length: int | None = None) -> tuple[int, bytes]:
if length is not None:
# Data block
compressed = length & c_squashfs.SQUASHFS_COMPRESSED_BIT_BLOCK == 0
Expand Down Expand Up @@ -177,13 +180,12 @@
_, _, data = self._read_metadata(self.lookup_table[block], offset, 8)
return self.get(struct.unpack("<Q", data)[0])

def _lookup_fragment(self, fragment: int) -> bytes:
def _lookup_fragment(self, fragment: int) -> c_squashfs.squashfs_fragment_entry:
fragment_offset = fragment * len(c_squashfs.squashfs_fragment_entry)
block, offset = divmod(fragment_offset, c_squashfs.SQUASHFS_METADATA_SIZE)

_, _, data = self._read_metadata(self.fragment_table[block], offset, len(c_squashfs.squashfs_fragment_entry))
entry = c_squashfs.squashfs_fragment_entry(data)
return entry
return c_squashfs.squashfs_fragment_entry(data)

def iter_inodes(self) -> Iterator[INode]:
for inum in range(1, self.sb.inodes + 1):
Expand All @@ -196,10 +198,10 @@
fs: SquashFS,
block: int,
offset: int,
name: Optional[str] = None,
type: Optional[int] = None,
inode_number: Optional[int] = None,
parent: Optional[INode] = None,
name: str | None = None,
type: int | None = None,
inode_number: int | None = None,
parent: INode | None = None,
):
self.fs = fs
self.block = block
Expand All @@ -209,6 +211,8 @@
self._inode_number = inode_number
self.parent = parent

self.listdir = cache(self.listdir)

def __repr__(self) -> str:
return f"<inode {self.inode_number} ({self.block}, {self.offset})>"

Expand All @@ -220,15 +224,9 @@
| c_squashfs.squashfs_reg_inode_header
| c_squashfs.squashfs_symlink_inode_header
| c_squashfs.squashfs_dev_inode_header
| c_squashfs.squashfs_dev_inode_header
| c_squashfs.squashfs_base_inode_header
| c_squashfs.squashfs_base_inode_header
| c_squashfs.squashfs_ldir_inode_header
| c_squashfs.squashfs_lreg_inode_header
| c_squashfs.squashfs_symlink_inode_header
| c_squashfs.squashfs_ldev_inode_header
| c_squashfs.squashfs_ldev_inode_header
| c_squashfs.squashfs_lipc_inode_header
| c_squashfs.squashfs_lipc_inode_header,
int,
int,
Expand Down Expand Up @@ -262,16 +260,10 @@
| c_squashfs.squashfs_reg_inode_header
| c_squashfs.squashfs_symlink_inode_header
| c_squashfs.squashfs_dev_inode_header
| c_squashfs.squashfs_dev_inode_header
| c_squashfs.squashfs_base_inode_header
| c_squashfs.squashfs_base_inode_header
| c_squashfs.squashfs_ldir_inode_header
| c_squashfs.squashfs_lreg_inode_header
| c_squashfs.squashfs_symlink_inode_header
| c_squashfs.squashfs_ldev_inode_header
| c_squashfs.squashfs_ldev_inode_header
| c_squashfs.squashfs_lipc_inode_header
| c_squashfs.squashfs_lipc_inode_header
):
header, _, _ = self._metadata()
return header
Expand Down Expand Up @@ -315,11 +307,12 @@
return ts.from_unix(self.header.mtime)

@property
def size(self) -> Optional[int]:
def size(self) -> int | None:
if self.is_dir() or self.is_file():
return self.header.file_size
elif self.is_symlink():
if self.is_symlink():

Check warning on line 313 in dissect/squashfs/squashfs.py

View check run for this annotation

Codecov / codecov/patch

dissect/squashfs/squashfs.py#L313

Added line #L313 was not covered by tests
return self.header.symlink_size
return None

Check warning on line 315 in dissect/squashfs/squashfs.py

View check run for this annotation

Codecov / codecov/patch

dissect/squashfs/squashfs.py#L315

Added line #L315 was not covered by tests

def is_dir(self) -> bool:
return self.type == stat.S_IFDIR
Expand Down Expand Up @@ -363,13 +356,9 @@
@cached_property
def link_inode(self) -> INode:
link = self.link
if link.startswith("/"):
relnode = None
else:
relnode = self.parent
relnode = None if link.startswith("/") else self.parent
return self.fs.get(self.link, relnode)

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

Expand Down Expand Up @@ -407,7 +396,7 @@
)

@cached_property
def block_list(self) -> list[tuple[Optional[int], int]]:
def block_list(self) -> list[tuple[int | None, int]]:
fragment = self.header.fragment
file_size = self.header.file_size
if fragment == c_squashfs.SQUASHFS_INVALID_FRAG:
Expand Down
53 changes: 48 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,56 @@ dev = [
"dissect.util>=3.0.dev,<4.0.dev",
]

[tool.black]
[tool.ruff]
line-length = 120
required-version = ">=0.9.0"

[tool.isort]
profile = "black"
known_first_party = ["dissect.squashfs"]
known_third_party = ["dissect"]
[tool.ruff.format]
docstring-code-format = true

[tool.ruff.lint]
select = [
"F",
"E",
"W",
"I",
"UP",
"YTT",
"ANN",
"B",
"C4",
"DTZ",
"T10",
"FA",
"ISC",
"G",
"INP",
"PIE",
"PYI",
"PT",
"Q",
"RSE",
"RET",
"SLOT",
"SIM",
"TID",
"TCH",
"PTH",
"PLC",
"TRY",
"FLY",
"PERF",
"FURB",
"RUF",
]
ignore = ["E203", "B904", "UP024", "ANN002", "ANN003", "ANN204", "ANN401", "SIM105", "TRY003"]

[tool.ruff.lint.per-file-ignores]
"tests/docs/**" = ["INP001"]

[tool.ruff.lint.isort]
known-first-party = ["dissect.squashfs"]
known-third-party = ["dissect"]

[tool.setuptools]
license-files = ["LICENSE", "COPYRIGHT"]
Expand Down
38 changes: 22 additions & 16 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,59 @@
from __future__ import annotations

import gzip
import os
from pathlib import Path
from typing import TYPE_CHECKING, BinaryIO

import pytest

if TYPE_CHECKING:
from collections.abc import Iterator


def absolute_path(filename):
return os.path.join(os.path.dirname(__file__), filename)
def absolute_path(filename: str) -> Path:
return Path(__file__).parent / filename


def open_file(name, mode="rb"):
with open(absolute_path(name), mode) as f:
yield f
def open_file(name: str, mode: str = "rb") -> Iterator[BinaryIO]:
with absolute_path(name).open(mode) as fh:
yield fh


def open_file_gz(name, mode="rb"):
with gzip.GzipFile(absolute_path(name), mode) as f:
yield f
def open_file_gz(name: str, mode: str = "rb") -> Iterator[BinaryIO]:
with gzip.GzipFile(absolute_path(name), mode) as fh:
yield fh


@pytest.fixture
def gzip_sqfs():
def gzip_sqfs() -> Iterator[BinaryIO]:
yield from open_file("data/gzip.sqfs")


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


@pytest.fixture
def lz4_sqfs():
def lz4_sqfs() -> Iterator[BinaryIO]:
yield from open_file("data/lz4.sqfs")


@pytest.fixture
def lzma_sqfs():
def lzma_sqfs() -> Iterator[BinaryIO]:
yield from open_file("data/lzma.sqfs")


@pytest.fixture
def lzo_sqfs():
def lzo_sqfs() -> Iterator[BinaryIO]:
yield from open_file("data/lzo.sqfs")


@pytest.fixture
def xz_sqfs():
def xz_sqfs() -> Iterator[BinaryIO]:
yield from open_file("data/xz.sqfs")


@pytest.fixture
def zstd_sqfs():
def zstd_sqfs() -> Iterator[BinaryIO]:
yield from open_file("data/zstd.sqfs")
4 changes: 3 additions & 1 deletion tests/test_exceptions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from __future__ import annotations

import pytest

from dissect.squashfs import exceptions


@pytest.mark.parametrize(
"exc, std",
("exc", "std"),
[
(exceptions.FileNotFoundError, FileNotFoundError),
(exceptions.IsADirectoryError, IsADirectoryError),
Expand Down
Loading
Loading