Skip to content

Commit a3df7db

Browse files
committed
Replace zstd with stdlib and backports zstd
1 parent 0c89766 commit a3df7db

File tree

5 files changed

+15
-14
lines changed

5 files changed

+15
-14
lines changed

dissect/hypervisor/disk/qcow2.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
# - https://github.com/qemu/qemu/blob/master/docs/interop/qcow2.txt
44
from __future__ import annotations
55

6+
import sys
67
import zlib
78
from functools import cached_property, lru_cache
8-
from io import BytesIO
99
from pathlib import Path
1010
from typing import TYPE_CHECKING, BinaryIO
1111

@@ -28,8 +28,10 @@
2828
from collections.abc import Iterator
2929

3030
try:
31-
import zstandard as zstd
32-
31+
if sys.version_info >= (3, 14):
32+
from compression import zstd
33+
else:
34+
from backports import zstd
3335
HAS_ZSTD = True
3436
except ImportError:
3537
HAS_ZSTD = False
@@ -384,16 +386,8 @@ def _decompress(self, buf: bytes) -> bytes:
384386
return dctx.decompress(buf, self.qcow2.cluster_size)
385387

386388
if self.qcow2.compression_type == c_qcow2.QCOW2_COMPRESSION_TYPE_ZSTD:
387-
result = []
388-
389389
dctx = zstd.ZstdDecompressor()
390-
reader = dctx.stream_reader(BytesIO(buf))
391-
while reader.tell() < self.qcow2.cluster_size:
392-
chunk = reader.read(self.qcow2.cluster_size - reader.tell())
393-
if not chunk:
394-
break
395-
result.append(chunk)
396-
return b"".join(result)
390+
return dctx.decompress(buf, self.qcow2.cluster_size)
397391

398392
raise Error(f"Invalid compression type: {self.qcow2.compression_type}")
399393

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ repository = "https://github.com/fox-it/dissect.hypervisor"
3939
[project.optional-dependencies]
4040
full = [
4141
"pycryptodome",
42+
"backports.zstd; python_version < '3.14'",
4243
]
4344
dev = [
4445
"dissect.hypervisor[full]",
11.1 KB
Binary file not shown.

tests/conftest.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ def basic_qcow2() -> Iterator[BinaryIO]:
8989
yield from open_file_gz("_data/disk/qcow2/basic.qcow2.gz")
9090

9191

92+
@pytest.fixture
93+
def basic_zstd_qcow2() -> Iterator[BinaryIO]:
94+
yield from open_file_gz("_data/disk/qcow2/basic-zstd.qcow2.gz")
95+
96+
9297
@pytest.fixture
9398
def data_file_qcow2() -> Path:
9499
return absolute_path("_data/disk/qcow2/data-file.qcow2.gz")

tests/disk/test_qcow2.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ def mock_open_gz(self: Path, *args, **kwargs) -> BinaryIO:
1616
return gzip.open(self if self.suffix.lower() == ".gz" else self.with_suffix(self.suffix + ".gz"))
1717

1818

19-
def test_basic(basic_qcow2: BinaryIO) -> None:
20-
qcow2 = QCow2(basic_qcow2)
19+
@pytest.mark.parametrize("name", ["basic_qcow2", "basic_zstd_qcow2"])
20+
def test_basic(name: str, request: pytest.FixtureRequest) -> None:
21+
qcow2 = QCow2(request.getfixturevalue(name))
2122

2223
assert qcow2.backing_file is None
2324
assert qcow2.data_file is qcow2.fh

0 commit comments

Comments
 (0)