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 #25

Merged
merged 1 commit into from
Jan 23, 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: 3 additions & 1 deletion dissect/ole/c_ole.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

from dissect.cstruct import cstruct

ole_def = """
Expand Down Expand Up @@ -34,7 +36,7 @@
char _abSig[8]; // [000H,08] 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1
// for current version, was 0x0e, 0x11, 0xfc, 0x0d,
// 0xd0, 0xcf, 0x11, 0xe0 on old, beta 2 files
// (late 92) which are also supported by the
// (late '92) which are also supported by the
// reference implementation
char _clid[16]; // [008H,16] class id (set with WriteClassStg, retrieved with
// GetClassFile/ReadClassStg)
Expand Down
84 changes: 44 additions & 40 deletions dissect/ole/ole.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
from __future__ import annotations

from typing import TYPE_CHECKING, BinaryIO

from dissect.util.stream import AlignedStream
from dissect.util.ts import wintimestamp

from dissect.ole.c_ole import DECOLOR, SIGNATURE, SIGNATURE_BETA, STGTY, c_ole
from dissect.ole.exceptions import Error, InvalidFileError, NotFoundError

if TYPE_CHECKING:
from collections.abc import Iterator


class OLE:
def __init__(self, fh):
def __init__(self, fh: BinaryIO):
self.fh = fh
self.header = c_ole.StructuredStorageHeader(fh)

Expand Down Expand Up @@ -37,7 +44,7 @@ def __init__(self, fh):
self.root = self.directory(0)
self.ministream = self.root.open()

def get(self, path, root=None):
def get(self, path: str, root: DirectoryEntry | None = None) -> DirectoryEntry:
root = root or self.root

search_path = path.replace("\\", "/")
Expand All @@ -56,15 +63,15 @@ def get(self, path, root=None):

return node

def directory(self, sid):
def directory(self, sid: int) -> DirectoryEntry:
try:
return self._dircache[sid]
except KeyError:
entry = DirectoryEntry(self, sid)
self._dircache[sid] = entry
return entry

def fat(self, sect):
def fat(self, sect: int) -> int:
idx, offset = divmod(sect, self.num_fat_entries)

try:
Expand Down Expand Up @@ -107,18 +114,18 @@ def fat(self, sect):

return table[offset]

def minifat(self, sect):
def minifat(self, sect: int) -> int:
return self._minifat[sect]

def chain(self, sect, size=None):
def chain(self, sect: int, size: int | None = None) -> MiniChain:
try:
return self._chaincache[sect]
except KeyError:
chain = Chain(self, sect, size)
self._chaincache[sect] = chain
return chain

def minichain(self, sect, size=None):
def minichain(self, sect: int, size: int | None = None) -> MiniChain:
try:
return self._minichaincache[sect]
except KeyError:
Expand All @@ -128,7 +135,7 @@ def minichain(self, sect, size=None):


class DirectoryEntry:
def __init__(self, ole, sid):
def __init__(self, ole: OLE, sid: int):
self.ole = ole
self.sid = sid

Expand All @@ -152,20 +159,20 @@ def __init__(self, ole, sid):

self._dirlist = {}

def __repr__(self):
return "<DirectoryEntry sid={} name={} type={} size=0x{:x}>".format(self.sid, self.name, self.type, self.size)
def __repr__(self) -> str:
return f"<DirectoryEntry sid={self.sid} name={self.name} type={self.type} size=0x{self.size:x}>"

def open(self):
def open(self) -> ChainStream:
return self.chain.open()

def listdir(self):
def listdir(self) -> dict[str, DirectoryEntry]:
if not self._dirlist:
for entry in self.walk():
self._dirlist[entry.name] = entry

return self._dirlist

def walk(self):
def walk(self) -> Iterator[DirectoryEntry]:
if self.has_left_sibling:
yield self.left_sibling
yield from self.left_sibling.walk()
Expand All @@ -181,77 +188,77 @@ def walk(self):
yield from self.child.walk()

@property
def child(self):
def child(self) -> DirectoryEntry | None:
if not self.has_child:
return None
return self.ole.directory(self.entry._sidChild)

@property
def left_sibling(self):
def left_sibling(self) -> DirectoryEntry | None:
if not self.has_left_sibling:
return None
return self.ole.directory(self.entry._sidLeftSib)

@property
def right_sibling(self):
def right_sibling(self) -> DirectoryEntry | None:
if not self.has_right_sibling:
return None
return self.ole.directory(self.entry._sidRightSib)

@property
def has_child(self):
def has_child(self) -> bool:
return self.entry._sidChild != 0xFFFFFFFF

@property
def has_left_sibling(self):
def has_left_sibling(self) -> bool:
return self.entry._sidLeftSib != 0xFFFFFFFF

@property
def has_right_sibling(self):
def has_right_sibling(self) -> bool:
return self.entry._sidRightSib != 0xFFFFFFFF

@property
def is_minifat(self):
def is_minifat(self) -> bool:
return self.is_stream and self.size < self.ole.mini_cutoff

@property
def is_red(self):
def is_red(self) -> bool:
return self.entry._bflags == DECOLOR.DE_RED

@property
def is_black(self):
def is_black(self) -> bool:
return self.entry._bflags == DECOLOR.DE_BLACK

@property
def is_valid(self):
def is_valid(self) -> bool:
return self.entry._mse == STGTY.STGTY_INVALID

@property
def is_stream(self):
def is_stream(self) -> bool:
return self.entry._mse == STGTY.STGTY_STREAM

@property
def is_storage(self):
def is_storage(self) -> bool:
return self.entry._mse == STGTY.STGTY_STORAGE


class Chain:
def __init__(self, ole, sect, size=None):
def __init__(self, ole: OLE, sect: int, size: int | None = None):
self.ole = ole
self.sect = sect
self.size = size
self.chain = [sect]
self.ended = False

def __len__(self):
def __len__(self) -> int:
self.fill()
return len(self.chain)

def __iter__(self):
def __iter__(self) -> Iterator[int]:
self.fill()
return iter(self.chain)

def __getitem__(self, i):
def __getitem__(self, i: int) -> int:
cur = len(self.chain)
tail = self.chain[-1]

Expand All @@ -271,10 +278,10 @@ def __getitem__(self, i):

return self.chain[i]

def open(self):
def open(self) -> ChainStream:
return ChainStream(self.ole.fh, self, self.ole.sector_size, offset=self.ole.sector_size)

def fill(self):
def fill(self) -> None:
if self.ended:
return

Expand All @@ -283,28 +290,28 @@ def fill(self):
except IndexError:
pass

def _lookup(self, sect):
def _lookup(self, sect: int) -> int:
return self.ole.fat(sect)


class MiniChain(Chain):
def open(self):
def open(self) -> ChainStream:
return ChainStream(self.ole.ministream, self, self.ole.mini_sector_size)

def _lookup(self, sect):
def _lookup(self, sect: int) -> int:
return self.ole.minifat(sect)


class ChainStream(AlignedStream):
def __init__(self, stream, chain, sector_size, offset=0):
def __init__(self, stream: BinaryIO, chain: Chain, sector_size: int, offset: int = 0):
self._fh = stream
self.chain = chain
self.sector_size = sector_size
self.offset = offset

super().__init__(chain.size)

def _read(self, offset, length):
def _read(self, offset: int, length: int) -> bytes:
r = []

if not self.size and length == -1:
Expand All @@ -321,10 +328,7 @@ def _read(self, offset, length):
except IndexError:
break

if self.size:
sectread = min(self.size - offset, sector_size)
else:
sectread = sector_size
sectread = min(self.size - offset, sector_size) if self.size else sector_size
fileoffset = self.offset + sectnum * sector_size

self._fh.seek(fileoffset)
Expand Down
53 changes: 48 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,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.ole"]
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.ole"]
known-third-party = ["dissect"]

[tool.setuptools]
license-files = ["LICENSE", "COPYRIGHT"]
Expand Down
23 changes: 5 additions & 18 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,18 @@ commands =
[testenv:fix]
package = skip
deps =
black==23.1.0
isort==5.11.4
ruff==0.9.2
commands =
black dissect
isort dissect
ruff format dissect tests

[testenv:lint]
package = skip
deps =
black==23.1.0
flake8
flake8-black
flake8-isort
isort==5.11.4
ruff==0.9.2
vermin
commands =
flake8 dissect
vermin -t=3.9- --no-tips --lint dissect

[flake8]
max-line-length = 120
extend-ignore =
# See https://github.com/PyCQA/pycodestyle/issues/373
E203,
statistics = True
ruff check dissect tests
vermin -t=3.9- --no-tips --lint dissect tests

[testenv:docs-build]
allowlist_externals = make
Expand Down
Loading