From fb2559f297fadef9b9b1a708f8ee2b6b51b4945d Mon Sep 17 00:00:00 2001 From: JSCU-CNI <121175071+JSCU-CNI@users.noreply.github.com> Date: Thu, 30 Apr 2026 10:57:46 +0200 Subject: [PATCH] Update ruff and explicitly specify supported cpython versions --- .gitignore | 1 + dissect/target/helpers/keychain.py | 4 ++-- dissect/target/helpers/regutil.py | 4 ++-- dissect/target/plugins/apps/container/docker.py | 2 +- dissect/target/plugins/apps/editor/windowsnotepad.py | 2 +- dissect/target/plugins/os/unix/shadow.py | 4 ++-- dissect/target/plugins/os/windows/dpapi/crypto.py | 2 +- dissect/target/plugins/os/windows/regf/shellbags.py | 2 +- dissect/target/plugins/os/windows/tasks/job.py | 2 +- dissect/target/plugins/os/windows/wer.py | 2 +- dissect/target/tools/diff.py | 2 +- dissect/target/tools/shell.py | 4 ++-- pyproject.toml | 4 ++-- tox.ini | 3 ++- 14 files changed, 20 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 2e944b3e83..c71f72ff66 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ dist/ *.pyc __pycache__/ .pytest_cache/ +.ruff_cache/ tests/_docs/api tests/_docs/build .tox/ diff --git a/dissect/target/helpers/keychain.py b/dissect/target/helpers/keychain.py index 9225f7340c..d797839e95 100644 --- a/dissect/target/helpers/keychain.py +++ b/dissect/target/helpers/keychain.py @@ -116,8 +116,8 @@ def register_keychain_file(keychain_path: Path) -> None: log.warning("No value provided in row %s", row) continue - identifier = identifier if identifier else None - provider = provider if provider else None + identifier = identifier or None + provider = provider or None register_key( key_type=key_type, diff --git a/dissect/target/helpers/regutil.py b/dissect/target/helpers/regutil.py index 16b581329a..06529079d9 100644 --- a/dissect/target/helpers/regutil.py +++ b/dissect/target/helpers/regutil.py @@ -336,7 +336,7 @@ def __init__(self, hive: RegistryHive, path: str, class_name: str | None = None) if not path.strip("\\"): self._name = "VROOT" else: - self._name = path.split("\\")[-1] + self._name = path.rsplit("\\", maxsplit=1)[-1] self._class_name = class_name self._values: dict[str, RegistryValue] = {} self._subkeys: dict[str, RegistryKey] = {} @@ -845,7 +845,7 @@ def parse_flex_value(value: str) -> tuple[RegistryValueType, ValueType]: # These values match regf type values vtype = int(vtype[4:5], 16) if vtype == regf.REG_NONE: - decoded = value if value else None + decoded = value or None elif vtype == regf.REG_SZ or vtype == regf.REG_EXPAND_SZ: decoded = regf.try_decode_sz(value) elif vtype == regf.REG_BINARY: diff --git a/dissect/target/plugins/apps/container/docker.py b/dissect/target/plugins/apps/container/docker.py index 2e4a2d2b70..08cf912952 100644 --- a/dissect/target/plugins/apps/container/docker.py +++ b/dissect/target/plugins/apps/container/docker.py @@ -409,7 +409,7 @@ def convert_ports(ports: dict[str, list | dict]) -> Iterator[str]: def hash_to_image_id(hash: str) -> str: """Convert the hash to an abbrevated docker image id.""" - return hash.split(":")[-1][:12] + return hash.rsplit(":", maxsplit=1)[-1][:12] def strip_log(input: str | bytes, exc_backspace: bool = False) -> str: diff --git a/dissect/target/plugins/apps/editor/windowsnotepad.py b/dissect/target/plugins/apps/editor/windowsnotepad.py index d4bdc066b8..43c86bf02a 100644 --- a/dissect/target/plugins/apps/editor/windowsnotepad.py +++ b/dissect/target/plugins/apps/editor/windowsnotepad.py @@ -273,7 +273,7 @@ def _process_tab_file(self) -> None: self.content += text # Set None if no deleted content was found - self.deleted_content = deleted_content if deleted_content else None + self.deleted_content = deleted_content or None class WindowsNotepadPlugin(EditorPlugin): diff --git a/dissect/target/plugins/os/unix/shadow.py b/dissect/target/plugins/os/unix/shadow.py index e282105bf0..957e360b7e 100644 --- a/dissect/target/plugins/os/unix/shadow.py +++ b/dissect/target/plugins/os/unix/shadow.py @@ -114,8 +114,8 @@ def passwords(self) -> Iterator[UnixShadowRecord]: last_change=epoch_days_to_datetime(last_change) if last_change else None, min_age=epoch_days_to_datetime(last_change + min_age) if last_change and min_age else None, max_age=epoch_days_to_datetime(last_change + max_age) if last_change and max_age else None, - warning_period=shent.get(5) if shent.get(5) else None, - inactivity_period=shent.get(6) if shent.get(6) else None, + warning_period=shent.get(5) or None, + inactivity_period=shent.get(6) or None, expiration_date=epoch_days_to_datetime(expiration_date) if expiration_date else None, unused_field=shent.get(8), _target=self.target, diff --git a/dissect/target/plugins/os/windows/dpapi/crypto.py b/dissect/target/plugins/os/windows/dpapi/crypto.py index 70ff5d0880..5c293dbceb 100644 --- a/dissect/target/plugins/os/windows/dpapi/crypto.py +++ b/dissect/target/plugins/os/windows/dpapi/crypto.py @@ -143,7 +143,7 @@ def decrypt(self, data: bytes, key: bytes, iv: bytes | None = None) -> bytes: if len(key) != 24: raise ValueError(f"Invalid DES3 CBC key length {len(key)}") - cipher = DES3.new(key, DES3.MODE_CBC, iv=iv if iv else b"\x00" * 8) + cipher = DES3.new(key, DES3.MODE_CBC, iv=iv or b"\x00" * 8) return cipher.decrypt(data) diff --git a/dissect/target/plugins/os/windows/regf/shellbags.py b/dissect/target/plugins/os/windows/regf/shellbags.py index 8897fcd06a..4b9203026c 100644 --- a/dissect/target/plugins/os/windows/regf/shellbags.py +++ b/dissect/target/plugins/os/windows/regf/shellbags.py @@ -614,7 +614,7 @@ def __init__(self, buf: bytes): @property def name(self) -> str: GUID_name = shell_folder_ids.DESCRIPTIONS.get(str(self.shell_identifier)) - return GUID_name if GUID_name else f"{{{self.shell_identifier}}}" + return GUID_name or f"{{{self.shell_identifier}}}" class EXTENSION_BLOCK: diff --git a/dissect/target/plugins/os/windows/tasks/job.py b/dissect/target/plugins/os/windows/tasks/job.py index c10f9c8a97..4fa89aced2 100644 --- a/dissect/target/plugins/os/windows/tasks/job.py +++ b/dissect/target/plugins/os/windows/tasks/job.py @@ -241,7 +241,7 @@ def get_triggers(self) -> Iterator[GroupedRecord]: 7: "EVENT_AT_LOGON", } for trigger in self.at_data.task_triggers: - trigger_type = TRIGGER_TYPE_NAMES.get(trigger.trigger_type, None) + trigger_type = TRIGGER_TYPE_NAMES.get(trigger.trigger_type) trigger_enabled = not trigger.trigger_flags.trigger_disabled s_year = trigger.begin_year diff --git a/dissect/target/plugins/os/windows/wer.py b/dissect/target/plugins/os/windows/wer.py index 57c9961337..77ace21d01 100644 --- a/dissect/target/plugins/os/windows/wer.py +++ b/dissect/target/plugins/os/windows/wer.py @@ -115,7 +115,7 @@ def _collect_wer_data(self, wer_file: Path) -> tuple[list[tuple[str, str]], dict record_type = "datetime" key = "ts" - key = self._sanitize_key(key if key else name) + key = self._sanitize_key(key or name) if not key: self.target.log.warning("Sanitizing key resulted in empty key, skipping line %r", line) key = None diff --git a/dissect/target/tools/diff.py b/dissect/target/tools/diff.py index 7c21f739ae..235d252105 100644 --- a/dissect/target/tools/diff.py +++ b/dissect/target/tools/diff.py @@ -511,7 +511,7 @@ def _write_entry_contents_to_stdout(self, entry: FilesystemEntry, stdout: TextIO def completedefault(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: """Autocomplete based on files / directories found in the current path.""" - path = line[:begidx].rsplit(" ")[-1] + path = line[:begidx].rsplit(" ", maxsplit=1)[-1] textlower = text.lower() path = fsutil.abspath(path, cwd=str(self.cwd), alt_separator=self.alt_separator) diff --git a/dissect/target/tools/shell.py b/dissect/target/tools/shell.py index 57d3aa1606..21130d7929 100644 --- a/dissect/target/tools/shell.py +++ b/dissect/target/tools/shell.py @@ -688,7 +688,7 @@ def prompt(self) -> str: return self.prompt_ps1.format(base=self.prompt_base, cwd=self.cwd, **ANSI_COLORS) def completedefault(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: - path = self.resolve_path(line[:begidx].rsplit(" ")[-1]) + path = self.resolve_path(line[:begidx].rsplit(" ", maxsplit=1)[-1]) textlower = text.lower() suggestions = [] @@ -1518,7 +1518,7 @@ def prompt(self) -> str: return "(registry) " + self.prompt_ps1.format(base=self.prompt_base, cwd=self.cwd, **ANSI_COLORS) def completedefault(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: - path = line[:begidx].rsplit(" ")[-1] + path = line[:begidx].rsplit(" ", maxsplit=1)[-1] return [fname for _, fname in self.scandir(path) if fname.lower().startswith(text.lower())] def resolve_key(self, path: str) -> regutil.RegistryKey: diff --git a/pyproject.toml b/pyproject.toml index 0a69d47494..84c5bc3337 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" name = "dissect.target" description = "This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)" readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.10,<3.14" license = "AGPL-3.0-or-later" license-files = ["LICENSE", "COPYRIGHT"] authors = [ @@ -136,7 +136,7 @@ test = [ ] lint = [ "typing_extensions", - "ruff==0.13.1", + "ruff==0.15.12", "vermin", ] build = [ diff --git a/tox.ini b/tox.ini index 093c597bba..02cb10897a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,6 @@ [tox] -envlist = lint, py3, pypy3 +# Specifiy usage of CPython versions >= 3.10 but < 3.14 (currently unsupported) and pypy >= 3.10 but <= 3.11 +envlist = lint, py3{10,11,12,13}, pypy3{10,11} # This version of tox will autoprovision itself and the requirements defined in # requires if they are not available on the host system. This requires the # locally installed tox to have a minimum version 3.3.0. This means the names