From f8b1ddf8447dbedf0ede5152741166a6911a2fee Mon Sep 17 00:00:00 2001 From: Rogdham Date: Sat, 20 Sep 2025 11:13:38 +0200 Subject: [PATCH 1/2] chore: use ruff linter --- .pylintrc | 22 ------- .vscode/settings.json | 1 - CHANGELOG.md | 2 +- dev-requirements.txt | 1 - pyproject.toml | 38 +++++++++++ src/xz/__init__.py | 10 +-- src/xz/block.py | 7 +-- src/xz/common.py | 18 +++--- src/xz/file.py | 10 +-- src/xz/io.py | 18 +++--- src/xz/open.py | 8 +-- src/xz/strategy.py | 4 +- src/xz/stream.py | 4 +- src/xz/typing.py | 2 +- src/xz/utils.py | 22 +++---- tests/conftest.py | 11 ++-- tests/integration/conftest.py | 8 +-- tests/integration/test_file_read.py | 12 ++-- tests/integration/test_file_write.py | 4 +- tests/integration/test_generate_files.py | 6 +- tests/integration/test_ram_usage.py | 24 +++---- tests/integration/test_readme.py | 8 +-- tests/unit/test_attr_proxy.py | 2 +- tests/unit/test_block.py | 57 ++++++++--------- tests/unit/test_common.py | 51 ++++++++------- tests/unit/test_file.py | 61 +++++++++--------- tests/unit/test_floordict.py | 20 +++--- tests/unit/test_ioabstract.py | 44 +++++-------- tests/unit/test_iocombiner.py | 28 ++++----- tests/unit/test_open.py | 80 +++++++++++++----------- tests/unit/test_parse_mode.py | 13 ++-- tests/unit/test_stream.py | 28 ++++----- tox.ini | 7 +-- 33 files changed, 295 insertions(+), 336 deletions(-) delete mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 3244d1a..0000000 --- a/.pylintrc +++ /dev/null @@ -1,22 +0,0 @@ -[BASIC] -good-names = - i, - j, - k, - ex, - _, - T, - -[MESSAGES CONTROL] -disable = - missing-class-docstring, - missing-function-docstring, - missing-module-docstring, - too-few-public-methods, - too-many-arguments, - too-many-branches, - too-many-instance-attributes, - too-many-locals, - -[SIMILARITIES] -ignore-imports=yes diff --git a/.vscode/settings.json b/.vscode/settings.json index 7915221..da33dbd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -35,6 +35,5 @@ "editor.tabSize": 2 }, "python.envFile": "${workspaceFolder}/.vscode/env", - "python.linting.pylintEnabled": true, "python.testing.pytestEnabled": true } diff --git a/CHANGELOG.md b/CHANGELOG.md index ba60f22..23c74e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ adheres to [Semantic Versioning](https://semver.org/). - Update GitHub actions dependencies - Add tests for PyPy 3.10 and 3.11 - Improve tox & CI pipelines -- Use ruff as formatter +- Use ruff as linter and formatter ## [0.5.0] - 2023-02-27 diff --git a/dev-requirements.txt b/dev-requirements.txt index cac6104..24227bb 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -5,7 +5,6 @@ build==1.3.0 # lint -pylint==2.16.2 ruff==0.13.1 # typing diff --git a/pyproject.toml b/pyproject.toml index 35e6c9c..8e133d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,3 +5,41 @@ [tool.ruff] src = ["src"] target-version = "py39" + +[tool.ruff.lint] +select = ["ALL"] +ignore = [ + "C901", + "COM812", + "D", + "E501", + "EM", + "ERA001", + "FA100", + "PLR0912", + "PLR0913", + "TRY003", + "TRY301", +] +allowed-confusables = ["α", "β", "γ", "δ", "ε", "ζ", "η", "θ"] + +[tool.ruff.lint.per-file-ignores] +"tests/**" = [ + "B018", + "FBT", + "INP001", + "PLR0915", + "PLR2004", + "S101", + "SLF001", + "TC001", +] + +[tool.ruff.lint.isort] +force-sort-within-sections = true +known-first-party = ["xz"] + +[tool.ruff.lint.flake8-pytest-style] +fixture-parentheses = false +mark-parentheses = false +parametrize-names-type = "list" diff --git a/src/xz/__init__.py b/src/xz/__init__.py index e085ff3..28481be 100644 --- a/src/xz/__init__.py +++ b/src/xz/__init__.py @@ -39,18 +39,18 @@ from xz.open import xz_open from xz.strategy import KeepBlockReadStrategy, RollingBlockReadStrategy -# pylint: disable=redefined-builtin -open = xz_open -# pylint: enable=redefined-builtin +open = xz_open # noqa: A001 -__all__ = ( - "__version__", +__all__: tuple[str, ...] = ( "KeepBlockReadStrategy", "RollingBlockReadStrategy", "XZError", "XZFile", + "__version__", "open", +) +__all__ += ( # re-export from lzma for easy access "CHECK_CRC32", "CHECK_CRC64", diff --git a/src/xz/block.py b/src/xz/block.py index 00abdd3..6b4a00a 100644 --- a/src/xz/block.py +++ b/src/xz/block.py @@ -1,6 +1,6 @@ from io import DEFAULT_BUFFER_SIZE, SEEK_SET from lzma import FORMAT_XZ, LZMACompressor, LZMADecompressor, LZMAError -from typing import Optional, Tuple, Union +from typing import Optional, Union from xz.common import ( XZError, @@ -45,7 +45,6 @@ def decompress(self, pos: int, size: int) -> bytes: skip_before = pos - self.pos - # pylint: disable=using-constant-test if self.decompressor.eof: raise XZError("block: decompressor eof") @@ -95,7 +94,7 @@ def _write(self, data: bytes) -> None: def compress(self, data: bytes) -> None: self._write(self.compressor.compress(data)) - def finish(self) -> Tuple[int, int]: + def finish(self) -> tuple[int, int]: data = self.compressor.flush() # footer @@ -124,7 +123,7 @@ def __init__( preset: _LZMAPresetType = None, filters: _LZMAFiltersType = None, block_read_strategy: Optional[_BlockReadStrategyType] = None, - ): + ) -> None: super().__init__(uncompressed_size) self.fileobj = fileobj self.check = check diff --git a/src/xz/common.py b/src/xz/common.py index 9c25011..1321418 100644 --- a/src/xz/common.py +++ b/src/xz/common.py @@ -1,7 +1,9 @@ +# ruff: noqa: PLR2004 + from binascii import crc32 as crc32int import lzma from struct import pack, unpack -from typing import List, Tuple, cast +from typing import cast HEADER_MAGIC = b"\xfd7zXZ\x00" FOOTER_MAGIC = b"YZ" @@ -20,7 +22,7 @@ def encode_mbi(value: int) -> bytes: return data -def decode_mbi(data: bytes) -> Tuple[int, int]: +def decode_mbi(data: bytes) -> tuple[int, int]: value = 0 for size, byte in enumerate(data): value |= (byte & 0x7F) << (size * 7) @@ -52,7 +54,7 @@ def create_xz_header(check: int) -> bytes: return HEADER_MAGIC + flags + crc32(flags) -def create_xz_index_footer(check: int, records: List[Tuple[int, int]]) -> bytes: +def create_xz_index_footer(check: int, records: list[tuple[int, int]]) -> bytes: if not 0 <= check <= 0xF: raise XZError("footer check") # index @@ -79,7 +81,7 @@ def parse_xz_header(header: bytes) -> int: if crc32(header[6:8]) != header[8:12]: raise XZError("header crc32") flag_first_byte, check = cast( - Tuple[int, int], + "tuple[int, int]", unpack(" int: return check -def parse_xz_index(index: bytes) -> List[Tuple[int, int]]: +def parse_xz_index(index: bytes) -> list[tuple[int, int]]: if len(index) < 8 or len(index) % 4: raise XZError("index length") index = memoryview(index) @@ -119,7 +121,7 @@ def parse_xz_index(index: bytes) -> List[Tuple[int, int]]: return records -def parse_xz_footer(footer: bytes) -> Tuple[int, int]: +def parse_xz_footer(footer: bytes) -> tuple[int, int]: if len(footer) != 12: raise XZError("footer length") if footer[10:12] != FOOTER_MAGIC: @@ -127,7 +129,7 @@ def parse_xz_footer(footer: bytes) -> Tuple[int, int]: if crc32(footer[4:10]) != footer[:4]: raise XZError("footer crc32") backward_size, flag_first_byte, check = cast( - Tuple[int, int, int], + "tuple[int, int, int]", unpack(" Tuple[int, int]: return (check, backward_size) -# find default value for check implicitely used by lzma +# find default value for check implicitly used by lzma DEFAULT_CHECK = parse_xz_header(lzma.compress(b"")[:12]) diff --git a/src/xz/file.py b/src/xz/file.py index fa79ffc..e81ccb3 100644 --- a/src/xz/file.py +++ b/src/xz/file.py @@ -1,7 +1,7 @@ from io import SEEK_CUR, SEEK_END import os import sys -from typing import BinaryIO, List, Optional, cast +from typing import BinaryIO, Optional, cast import warnings from xz.common import DEFAULT_CHECK, XZError @@ -83,8 +83,7 @@ def __init__( # get fileobj if isinstance(filename, (str, bytes, os.PathLike)): - # pylint: disable=consider-using-with, unspecified-encoding - self.fileobj = cast(BinaryIO, open(filename, self._mode + "b")) + self.fileobj = cast("BinaryIO", open(filename, self._mode + "b")) # noqa: PTH123, SIM115 self._close_fileobj = True elif hasattr(filename, "read"): # weak check but better than nothing self.fileobj = filename @@ -141,6 +140,7 @@ def close(self) -> None: "Empty XZFile: nothing was written, " "so output is empty (and not a valid xz file).", RuntimeWarning, + stacklevel=2, ) finally: if self._close_fileobj: @@ -151,11 +151,11 @@ def close(self) -> None: pass @property - def stream_boundaries(self) -> List[int]: + def stream_boundaries(self) -> list[int]: return list(self._fileobjs) @property - def block_boundaries(self) -> List[int]: + def block_boundaries(self) -> list[int]: return [ stream_pos + block_boundary for stream_pos, stream in self._fileobjs.items() diff --git a/src/xz/io.py b/src/xz/io.py index 9f1a71f..8dcd1fc 100644 --- a/src/xz/io.py +++ b/src/xz/io.py @@ -34,16 +34,14 @@ def __len__(self) -> int: return self._length def _check_not_closed(self) -> None: - # https://github.com/PyCQA/pylint/issues/3484 - # pylint: disable=using-constant-test if self.closed: raise ValueError("I/O operation on closed file") def fileno(self) -> int: try: - return cast(BinaryIO, self.fileobj).fileno() # type: ignore[attr-defined] + return cast("BinaryIO", self.fileobj).fileno() # type: ignore[attr-defined] except AttributeError: - raise UnsupportedOperation("fileno") # pylint: disable=raise-missing-from + raise UnsupportedOperation("fileno") from None def seekable(self) -> bool: """Return a bool indicating whether object supports random access.""" @@ -188,7 +186,7 @@ def close(self) -> None: # the methods below are expected to be implemented by subclasses - def _read(self, size: int) -> bytes: # pragma: no cover + def _read(self, size: int) -> bytes: # pragma: no cover # noqa: ARG002 """Read and return up to size bytes, where size is an int. The size will not exceed the number of bytes between self._pos and @@ -205,7 +203,7 @@ def _write_before(self) -> None: def _write_after(self) -> None: """This method is called after the last write operation (usually on file close).""" - def _write(self, data: bytes) -> int: # pragma: no cover + def _write(self, data: bytes) -> int: # pragma: no cover # noqa: ARG002 """Writes as many bytes from data as possible, and return the number of bytes written. @@ -217,7 +215,7 @@ def _write(self, data: bytes) -> int: # pragma: no cover """ raise UnsupportedOperation("write") - def _truncate(self, size: int) -> None: # pragma: no cover + def _truncate(self, size: int) -> None: # pragma: no cover # noqa: ARG002 """Truncate the file to the given size. This resizing can extend or reduce the current file size. @@ -286,7 +284,7 @@ def _write_after(self) -> None: if self._fileobjs: last_fileobj = self._fileobjs.last_item if last_fileobj: - last_fileobj._write_end() # pylint: disable=protected-access + last_fileobj._write_end() # noqa: SLF001 else: del self._fileobjs[self._fileobjs.last_key] @@ -301,7 +299,7 @@ def _write(self, data: bytes) -> int: fileobj = self._get_fileobj() # newly created fileobj should be writable - # otherwire this will raise UnsupportedOperation + # otherwise this will raise UnsupportedOperation return fileobj.write(data) def _truncate(self, size: int) -> None: @@ -329,7 +327,7 @@ def _change_fileobj(self) -> None: last_fileobj = self._fileobjs.last_item if last_fileobj: if last_fileobj.writable(): - last_fileobj._write_end() # pylint: disable=protected-access + last_fileobj._write_end() # noqa: SLF001 else: del self._fileobjs[self._fileobjs.last_key] diff --git a/src/xz/open.py b/src/xz/open.py index 4ffb49f..3c5d854 100644 --- a/src/xz/open.py +++ b/src/xz/open.py @@ -1,6 +1,6 @@ from functools import wraps from io import TextIOWrapper -from typing import BinaryIO, List, Optional, Union, cast, overload +from typing import BinaryIO, Optional, Union, cast, overload from xz.file import XZFile from xz.typing import ( @@ -37,7 +37,7 @@ def __init__( block_read_strategy=block_read_strategy, ) super().__init__( - cast(BinaryIO, self.xz_file), + cast("BinaryIO", self.xz_file), encoding, errors, newline, @@ -46,8 +46,8 @@ def __init__( check = AttrProxy[int]("xz_file") preset = AttrProxy[_LZMAPresetType]("xz_file") filters = AttrProxy[_LZMAFiltersType]("xz_file") - stream_boundaries = AttrProxy[List[int]]("xz_file") - block_boundaries = AttrProxy[List[int]]("xz_file") + stream_boundaries = AttrProxy[list[int]]("xz_file") + block_boundaries = AttrProxy[list[int]]("xz_file") block_read_strategy = AttrProxy[_BlockReadStrategyType]("xz_file") @property diff --git a/src/xz/strategy.py b/src/xz/strategy.py index e1da785..1479a5a 100644 --- a/src/xz/strategy.py +++ b/src/xz/strategy.py @@ -1,5 +1,5 @@ import time -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING if TYPE_CHECKING: # pragma: no cover # avoid circular dependency @@ -19,7 +19,7 @@ def on_read(self, block: "XZBlock") -> None: class RollingBlockReadStrategy: def __init__(self, max_block_read_nb: int = 8) -> None: - self.block_reads: Dict["XZBlock", float] = {} + self.block_reads: dict[XZBlock, float] = {} self.max_block_read_nb = max_block_read_nb def _freshly_used(self, block: "XZBlock") -> None: diff --git a/src/xz/stream.py b/src/xz/stream.py index 80d107e..49992b8 100644 --- a/src/xz/stream.py +++ b/src/xz/stream.py @@ -1,5 +1,5 @@ from io import SEEK_CUR -from typing import BinaryIO, List, Optional +from typing import BinaryIO, Optional from xz.block import XZBlock from xz.common import ( @@ -36,7 +36,7 @@ def check(self) -> int: return self._check @property - def block_boundaries(self) -> List[int]: + def block_boundaries(self) -> list[int]: return list(self._fileobjs) @property diff --git a/src/xz/typing.py b/src/xz/typing.py index dfbfc7f..a8fdeee 100644 --- a/src/xz/typing.py +++ b/src/xz/typing.py @@ -23,7 +23,7 @@ _XZModesTextType = Literal["rt", "rt+", "wt", "wt+", "xt", "xt+"] -class _BlockReadStrategyType(Protocol): +class _BlockReadStrategyType(Protocol): # noqa: PYI046 def on_create(self, block: "XZBlock") -> None: ... # pragma: no cover def on_delete(self, block: "XZBlock") -> None: ... # pragma: no cover diff --git a/src/xz/utils.py b/src/xz/utils.py index 20220c0..056169a 100644 --- a/src/xz/utils.py +++ b/src/xz/utils.py @@ -1,6 +1,6 @@ from bisect import bisect_right, insort_right from collections.abc import Iterator, MutableMapping -from typing import Any, Dict, Generic, List, Tuple, TypeVar, cast +from typing import Generic, TypeVar, cast T = TypeVar("T") @@ -14,8 +14,8 @@ class FloorDict(MutableMapping[int, T]): """ def __init__(self) -> None: - self._dict: Dict[int, T] = {} - self._keys: List[int] = [] # sorted + self._dict: dict[int, T] = {} + self._keys: list[int] = [] # sorted def __repr__(self) -> str: return f"FloorDict<{self._dict!r}>" @@ -35,7 +35,7 @@ def _key_index(self, key: int) -> int: raise KeyError(key) return index - def get_with_index(self, key: int) -> Tuple[int, T]: + def get_with_index(self, key: int) -> tuple[int, T]: if not isinstance(key, int): raise TypeError("Invalid key") index = self._keys[self._key_index(key)] @@ -68,7 +68,7 @@ def last_item(self) -> T: return self._dict[self.last_key] -def parse_mode(mode: str) -> Tuple[str, bool, bool]: +def parse_mode(mode: str) -> tuple[str, bool, bool]: """Parse a mode used in open. Order is not considered at all. @@ -79,12 +79,12 @@ def parse_mode(mode: str) -> Tuple[str, bool, bool]: """ mode_set = set(mode) if len(mode_set) != len(mode): - raise ValueError(f"invalid mode: {mode}") + raise ValueError(f"Invalid mode: {mode}") mode_plus = "+" in mode_set mode_set -= {"b", "+"} mode_base = mode_set.pop() if mode_set else "invalid" if mode_set or mode_base not in "rwx": - raise ValueError(f"invalid mode: {mode}") + raise ValueError(f"Invalid mode: {mode}") if mode_plus: return (f"{mode_base}+", True, True) return (mode_base, mode_base == "r", mode_base != "r") @@ -122,10 +122,10 @@ class Foo: def __init__(self, proxy: str) -> None: self.proxy = proxy - def __set_name__(self, klass: Any, name: str) -> None: + def __set_name__(self, klass: type[object], name: str) -> None: self.attribute = name - def __get__(self, instance: Any, klass: Any) -> T: + def __get__(self, instance: object, klass: type[object]) -> T: dest = getattr(instance, self.proxy) if dest is None: try: @@ -135,9 +135,9 @@ def __get__(self, instance: Any, klass: Any) -> T: f"'{klass.__name__}' object has not attribute '{self.attribute}'" f" until its attribute '{self.proxy}' is defined" ) from ex - return cast(T, getattr(dest, self.attribute)) + return cast("T", getattr(dest, self.attribute)) - def __set__(self, instance: Any, value: T) -> None: + def __set__(self, instance: object, value: T) -> None: dest = getattr(instance, self.proxy) if dest is None: self.not_proxied_value = value diff --git a/tests/conftest.py b/tests/conftest.py index 5586dae..511c250 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,6 @@ -from collections.abc import Callable, Iterator +from collections.abc import Callable from itertools import chain, product from pathlib import Path -from typing import List, Tuple import pytest @@ -16,7 +15,7 @@ def pytest_addoption(parser: pytest.Parser) -> None: def pytest_collection_modifyitems( - config: pytest.Config, items: List[pytest.Item] + config: pytest.Config, items: list[pytest.Item] ) -> None: root = Path(__file__).parent.parent for item in items: @@ -51,10 +50,10 @@ def data_pattern() -> bytes: @pytest.fixture(scope="session") -def data_pattern_locate() -> Iterator[Callable[[bytes], Tuple[int, int]]]: - def locate(data: bytes) -> Tuple[int, int]: +def data_pattern_locate() -> Callable[[bytes], tuple[int, int]]: + def locate(data: bytes) -> tuple[int, int]: if len(data) < 3: raise ValueError("data to short") return (_DATA_PATTERN.index(data), len(data)) - yield locate + return locate diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index ffff362..165b56f 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,6 +1,6 @@ import json from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, Tuple, cast +from typing import TYPE_CHECKING, Any, cast import pytest @@ -10,15 +10,15 @@ class _Request(pytest.FixtureRequest): param: Path -_IntegrationCase = Tuple[Path, Dict[str, Any]] +_IntegrationCase = tuple[Path, dict[str, Any]] @pytest.fixture( params=(Path(__file__).parent / "files").rglob("*.json"), - ids=lambda path: cast(Path, path).name, + ids=lambda path: cast("Path", path).name, ) def integration_case(request: "_Request") -> _IntegrationCase: json_path = request.param with json_path.open() as json_file: - metadata = cast(Dict[str, Any], json.load(json_file)) + metadata = cast("dict[str, Any]", json.load(json_file)) return (json_path.with_suffix(".xz"), metadata) diff --git a/tests/integration/test_file_read.py b/tests/integration/test_file_read.py index 816a49e..302a4f1 100644 --- a/tests/integration/test_file_read.py +++ b/tests/integration/test_file_read.py @@ -1,17 +1,15 @@ from pathlib import Path -from typing import Any, Dict, Tuple +from typing import Any from xz import XZFile -_IntegrationCase = Tuple[Path, Dict[str, Any]] +_IntegrationCase = tuple[Path, dict[str, Any]] def test_read_all(integration_case: _IntegrationCase, data_pattern: bytes) -> None: xz_path, metadata = integration_case with XZFile(xz_path) as xzfile: - streams_items = list( - xzfile._fileobjs.items() # pylint: disable=protected-access - ) + streams_items = list(xzfile._fileobjs.items()) assert len(streams_items) == len(metadata["streams"]) pos = 0 stream_boundaries = [] @@ -21,9 +19,7 @@ def test_read_all(integration_case: _IntegrationCase, data_pattern: bytes) -> No stream_pos, stream = stream_item assert stream_pos == pos assert stream.check == metadata_stream["check"] - block_items = list( - stream._fileobjs.items() # pylint: disable=protected-access - ) + block_items = list(stream._fileobjs.items()) assert len(block_items) == len(metadata_stream["blocks"]) for block_item, metadata_block in zip( block_items, metadata_stream["blocks"] diff --git a/tests/integration/test_file_write.py b/tests/integration/test_file_write.py index 7e820a9..396096c 100644 --- a/tests/integration/test_file_write.py +++ b/tests/integration/test_file_write.py @@ -1,12 +1,12 @@ from hashlib import sha256 from pathlib import Path -from typing import Any, Dict, Tuple +from typing import Any import pytest import xz -_IntegrationCase = Tuple[Path, Dict[str, Any]] +_IntegrationCase = tuple[Path, dict[str, Any]] def test( diff --git a/tests/integration/test_generate_files.py b/tests/integration/test_generate_files.py index 6288910..66c6076 100644 --- a/tests/integration/test_generate_files.py +++ b/tests/integration/test_generate_files.py @@ -1,11 +1,11 @@ from hashlib import sha256 from pathlib import Path import subprocess -from typing import Any, Dict, Tuple +from typing import Any import pytest -_IntegrationCase = Tuple[Path, Dict[str, Any]] +_IntegrationCase = tuple[Path, dict[str, Any]] @pytest.mark.generate_integration_files @@ -23,7 +23,7 @@ def test(integration_case: _IntegrationCase, data_pattern: bytes) -> None: step_data = data[:step_data_len] data = data[step_data_len:] fout.write( - subprocess.run( + subprocess.run( # noqa: S603 step["cmd"].split(" "), input=step_data, stdout=subprocess.PIPE, diff --git a/tests/integration/test_ram_usage.py b/tests/integration/test_ram_usage.py index 1363810..f6e99a0 100644 --- a/tests/integration/test_ram_usage.py +++ b/tests/integration/test_ram_usage.py @@ -15,7 +15,7 @@ @pytest.fixture def ram_usage() -> Iterator[Callable[[], int]]: try: - import tracemalloc # pylint: disable=import-outside-toplevel + import tracemalloc # noqa: PLC0415 except ImportError: # e.g. PyPy pytest.skip("tracemalloc module not available") @@ -35,7 +35,7 @@ def fileobj() -> BinaryIO: nb_blocks = 50 seed(0) - data = compress(randbytes(BLOCK_SIZE)) + data = compress(randbytes(BLOCK_SIZE)) # noqa: S311 header = data[:12] footer = data[-12:] check, backward_size = parse_xz_footer(footer) @@ -44,7 +44,7 @@ def fileobj() -> BinaryIO: index_footer = create_xz_index_footer(check, records * nb_blocks) return cast( - BinaryIO, + "BinaryIO", IOCombiner( IOStatic(header), *[IOStatic(block)] * nb_blocks, @@ -53,11 +53,7 @@ def fileobj() -> BinaryIO: ) -def test_read_linear( - # pylint: disable=redefined-outer-name - fileobj: BinaryIO, - ram_usage: Callable[[], int], -) -> None: +def test_read_linear(fileobj: BinaryIO, ram_usage: Callable[[], int]) -> None: with XZFile(fileobj) as xz_file: # read almost one block xz_file.read(BLOCK_SIZE - 1) @@ -72,9 +68,7 @@ def test_read_linear( def test_partial_read_each_block( - # pylint: disable=redefined-outer-name - fileobj: BinaryIO, - ram_usage: Callable[[], int], + fileobj: BinaryIO, ram_usage: Callable[[], int] ) -> None: one_block_memory: Optional[int] = None @@ -94,11 +88,7 @@ def test_partial_read_each_block( ) -def test_write( - tmp_path: Path, - # pylint: disable=redefined-outer-name - ram_usage: Callable[[], int], -) -> None: +def test_write(tmp_path: Path, ram_usage: Callable[[], int]) -> None: nb_blocks = 10 seed(0) @@ -108,7 +98,7 @@ def test_write( with XZFile(tmp_path / "archive.xz", "w") as xz_file: for i in range(nb_blocks): xz_file.change_block() - xz_file.write(randbytes(BLOCK_SIZE)) + xz_file.write(randbytes(BLOCK_SIZE)) # noqa: S311 if one_block_memory is None: one_block_memory = ram_usage() diff --git a/tests/integration/test_readme.py b/tests/integration/test_readme.py index a122e59..095f37b 100644 --- a/tests/integration/test_readme.py +++ b/tests/integration/test_readme.py @@ -3,7 +3,7 @@ import os from pathlib import Path import shutil -from typing import List, Optional, Tuple +from typing import Optional import pytest @@ -12,14 +12,14 @@ @pytest.fixture(autouse=True) def change_dir(tmp_path: Path) -> Iterator[None]: - old_dir = os.getcwd() + old_dir = Path.cwd() shutil.copy(Path(__file__).parent / "files" / "example.xz", tmp_path) os.chdir(tmp_path) yield os.chdir(old_dir) -def _parse_readme() -> List[Tuple[int, str]]: +def _parse_readme() -> list[tuple[int, str]]: code_blocks = [] current_code_block = "" current_code_block_line: Optional[int] = None @@ -48,7 +48,7 @@ def _parse_readme() -> List[Tuple[int, str]]: for line_no, code_block in _README_CODE_BLOCKS ], ) -def test_readme(code_block: str, tmp_path: Path) -> None: # pylint: disable=redefined-outer-name +def test_readme(code_block: str, tmp_path: Path) -> None: path = tmp_path / "block.txt" path.write_text(code_block) failure_count, test_count = doctest.testfile( diff --git a/tests/unit/test_attr_proxy.py b/tests/unit/test_attr_proxy.py index 2279d2a..46508c6 100644 --- a/tests/unit/test_attr_proxy.py +++ b/tests/unit/test_attr_proxy.py @@ -20,7 +20,7 @@ def test_direct() -> None: # not proxied with pytest.raises(AttributeError) as exc_info: - src.abc # pylint: disable=pointless-statement + src.abc assert ( str(exc_info.value) == "'Src' object has not attribute 'abc' until its attribute 'proxy' is defined" diff --git a/tests/unit/test_block.py b/tests/unit/test_block.py index 1373079..00cc074 100644 --- a/tests/unit/test_block.py +++ b/tests/unit/test_block.py @@ -1,6 +1,6 @@ -from collections.abc import Callable, Iterator +from collections.abc import Callable from io import SEEK_SET, BytesIO, UnsupportedOperation -from typing import Tuple, cast +from typing import cast from unittest.mock import Mock, call import pytest @@ -20,19 +20,19 @@ def create_fileobj(data: bytes) -> Mock: raw = BytesIO(data) mock = Mock(wraps=raw) - mock.__class__ = cast(Mock, IOAbstract) # needs to be subclass of IOAbstract + mock.__class__ = cast("Mock", IOAbstract) # needs to be subclass of IOAbstract mock.__len__ = lambda _: len(raw.getvalue()) return mock @pytest.fixture -def fileobj() -> Iterator[Mock]: - yield create_fileobj(BLOCK_BYTES) +def fileobj() -> Mock: + return create_fileobj(BLOCK_BYTES) @pytest.fixture -def fileobj_empty() -> Iterator[Mock]: - yield create_fileobj(b"") +def fileobj_empty() -> Mock: + return create_fileobj(b"") @pytest.fixture(autouse=True) @@ -41,13 +41,10 @@ def patch_buffer_size(monkeypatch: pytest.MonkeyPatch) -> None: @pytest.fixture -def compressor(monkeypatch: pytest.MonkeyPatch) -> Iterator[Mock]: +def compressor(monkeypatch: pytest.MonkeyPatch) -> Mock: mock = Mock() monkeypatch.setattr(block_module, "LZMACompressor", mock) - yield mock.return_value - - -# pylint: disable=redefined-outer-name + return mock.return_value # type: ignore[no-any-return] # @@ -56,7 +53,7 @@ def compressor(monkeypatch: pytest.MonkeyPatch) -> Iterator[Mock]: def test_read_all( - fileobj: Mock, data_pattern_locate: Callable[[bytes], Tuple[int, int]] + fileobj: Mock, data_pattern_locate: Callable[[bytes], tuple[int, int]] ) -> None: block = XZBlock(fileobj, 1, 89, 100) assert block.tell() == 0 @@ -88,7 +85,7 @@ def test_read_all( def test_read_seek_forward( - fileobj: Mock, data_pattern_locate: Callable[[bytes], Tuple[int, int]] + fileobj: Mock, data_pattern_locate: Callable[[bytes], tuple[int, int]] ) -> None: block = XZBlock(fileobj, 1, 89, 100) assert block.tell() == 0 @@ -139,7 +136,7 @@ def test_read_seek_forward( def test_read_seek_backward( - fileobj: Mock, data_pattern_locate: Callable[[bytes], Tuple[int, int]] + fileobj: Mock, data_pattern_locate: Callable[[bytes], tuple[int, int]] ) -> None: block = XZBlock(fileobj, 1, 89, 100) assert block.tell() == 0 @@ -185,7 +182,7 @@ def test_read_seek_backward( def test_read_wrong_uncompressed_size_too_small( - fileobj: Mock, data_pattern_locate: Callable[[bytes], Tuple[int, int]] + fileobj: Mock, data_pattern_locate: Callable[[bytes], tuple[int, int]] ) -> None: block = XZBlock(fileobj, 1, 89, 99) @@ -199,7 +196,7 @@ def test_read_wrong_uncompressed_size_too_small( def test_read_wrong_uncompressed_size_too_big( - fileobj: Mock, data_pattern_locate: Callable[[bytes], Tuple[int, int]] + fileobj: Mock, data_pattern_locate: Callable[[bytes], tuple[int, int]] ) -> None: block = XZBlock(fileobj, 1, 89, 101) @@ -213,7 +210,7 @@ def test_read_wrong_uncompressed_size_too_big( def test_read_wrong_block_padding( - data_pattern_locate: Callable[[bytes], Tuple[int, int]], + data_pattern_locate: Callable[[bytes], tuple[int, int]], ) -> None: fileobj = IOStatic(BLOCK_BYTES[:-5] + b"\xff" + BLOCK_BYTES[-4:]) block = XZBlock(fileobj, 1, 89, 100) @@ -228,7 +225,7 @@ def test_read_wrong_block_padding( def test_read_wrong_check( - data_pattern_locate: Callable[[bytes], Tuple[int, int]], + data_pattern_locate: Callable[[bytes], tuple[int, int]], ) -> None: fileobj = IOStatic(BLOCK_BYTES[:-4] + b"\xff" * 4) @@ -261,7 +258,7 @@ def test_read_truncated_data() -> None: def test_read_decompressor_eof( - data_pattern_locate: Callable[[bytes], Tuple[int, int]], + data_pattern_locate: Callable[[bytes], tuple[int, int]], ) -> None: fileobj = IOStatic( bytes.fromhex( @@ -362,36 +359,32 @@ def test_write_existing(fileobj: Mock, pos: int) -> None: def test_write_compressor_error_0(fileobj_empty: Mock, compressor: Mock) -> None: compressor.compress.return_value = create_xz_header(0) - with XZBlock(fileobj_empty, 1, 0, 0) as block: - with pytest.raises(XZError) as exc_info: - block.write(b"Hello, world!\n") + with XZBlock(fileobj_empty, 1, 0, 0) as block, pytest.raises(XZError) as exc_info: + block.write(b"Hello, world!\n") assert str(exc_info.value) == "block: compressor header" def test_write_compressor_error_1(fileobj_empty: Mock, compressor: Mock) -> None: compressor.compress.return_value = create_xz_header(1) compressor.flush.return_value = create_xz_index_footer(0, [(13, 37), (4, 2)]) - with pytest.raises(XZError) as exc_info: - with XZBlock(fileobj_empty, 1, 0, 0) as block: - block.write(b"Hello, world!\n") + with pytest.raises(XZError) as exc_info, XZBlock(fileobj_empty, 1, 0, 0) as block: + block.write(b"Hello, world!\n") assert str(exc_info.value) == "block: compressor footer check" def test_write_compressor_error_2(fileobj_empty: Mock, compressor: Mock) -> None: compressor.compress.return_value = create_xz_header(1) compressor.flush.return_value = create_xz_index_footer(1, [(13, 37), (4, 2)]) - with pytest.raises(XZError) as exc_info: - with XZBlock(fileobj_empty, 1, 0, 0) as block: - block.write(b"Hello, world!\n") + with pytest.raises(XZError) as exc_info, XZBlock(fileobj_empty, 1, 0, 0) as block: + block.write(b"Hello, world!\n") assert str(exc_info.value) == "block: compressor index records length" def test_write_compressor_error_3(fileobj_empty: Mock, compressor: Mock) -> None: compressor.compress.return_value = create_xz_header(1) compressor.flush.return_value = create_xz_index_footer(1, [(34, 1337)]) - with pytest.raises(XZError) as exc_info: - with XZBlock(fileobj_empty, 1, 0, 0) as block: - block.write(b"Hello, world!\n") + with pytest.raises(XZError) as exc_info, XZBlock(fileobj_empty, 1, 0, 0) as block: + block.write(b"Hello, world!\n") assert str(exc_info.value) == "block: compressor uncompressed size" diff --git a/tests/unit/test_common.py b/tests/unit/test_common.py index 4cfec73..e81d645 100644 --- a/tests/unit/test_common.py +++ b/tests/unit/test_common.py @@ -1,5 +1,4 @@ from lzma import CHECK_CRC32, CHECK_CRC64, CHECK_NONE, CHECK_SHA256, is_check_supported -from typing import List, Tuple import pytest @@ -43,17 +42,17 @@ ) -@pytest.mark.parametrize("value, data", MBI_CASE) +@pytest.mark.parametrize(["value", "data"], MBI_CASE) def test_encode_mbi(value: int, data: str) -> None: assert encode_mbi(value) == bytes.fromhex(data) -@pytest.mark.parametrize("value, data", MBI_CASE) +@pytest.mark.parametrize(["value", "data"], MBI_CASE) def test_decode_mbi(value: int, data: str) -> None: assert decode_mbi(bytes.fromhex(data) + b"\xff\x00" * 10) == (len(data) // 2, value) -@pytest.mark.parametrize("data", ("", "81828384"), ids=("empty", "truncated")) +@pytest.mark.parametrize("data", ["", "81828384"], ids=("empty", "truncated")) def test_decode_mbi_invalid(data: str) -> None: with pytest.raises(XZError) as exc_info: decode_mbi(bytes.fromhex(data)) @@ -61,16 +60,16 @@ def test_decode_mbi_invalid(data: str) -> None: @pytest.mark.parametrize( - "value, expected", - ((0, 0), (1, 4), (2, 4), (3, 4), (4, 4), (5, 8), (6, 8), (7, 8), (8, 8)), + ["value", "expected"], + [(0, 0), (1, 4), (2, 4), (3, 4), (4, 4), (5, 8), (6, 8), (7, 8), (8, 8)], ) def test_round_up(value: int, expected: int) -> None: assert round_up(value) == expected @pytest.mark.parametrize( - "value, padding", - ( + ["value", "padding"], + [ (0, ""), (1, "000000"), (2, "0000"), @@ -80,7 +79,7 @@ def test_round_up(value: int, expected: int) -> None: (6, "0000"), (7, "00"), (8, ""), - ), + ], ) def test_pad(value: int, padding: str) -> None: assert pad(value) == bytes.fromhex(padding) @@ -97,7 +96,7 @@ def test_pad(value: int, padding: str) -> None: ) -@pytest.mark.parametrize("check, data", XZ_HEADER_CASES) +@pytest.mark.parametrize(["check", "data"], XZ_HEADER_CASES) def test_create_xz_header(check: int, data: str) -> None: assert create_xz_header(check) == bytes.fromhex(data) @@ -108,21 +107,21 @@ def test_create_xz_header_invalid_check() -> None: assert str(exc_info.value) == "header check" -@pytest.mark.parametrize("check, data", XZ_HEADER_CASES) +@pytest.mark.parametrize(["check", "data"], XZ_HEADER_CASES) def test_parse_xz_header(check: int, data: str) -> None: assert parse_xz_header(bytes.fromhex(data)) == check @pytest.mark.parametrize( - "data, message", - ( + ["data", "message"], + [ ("fd377a585a0000016922de3600", "header length"), ("f1377a585a000000ff12d941", "header magic"), ("fd377a585a0000016942de36", "header crc32"), ("fd377a585a0000110d32692b", "header flags"), ("fd377a585a0001012813c52f", "header flags"), ("fd377a585a00100138301c7c", "header flags"), - ), + ], ) def test_parse_xz_header_invalid(data: str, message: str) -> None: with pytest.raises(XZError) as exc_info: @@ -148,8 +147,8 @@ def test_parse_xz_header_invalid(data: str, message: str) -> None: ) -@pytest.mark.parametrize("records, data", XZ_INDEX_CASES) -def test_create_xz_index(records: List[Tuple[int, int]], data: str) -> None: +@pytest.mark.parametrize(["records", "data"], XZ_INDEX_CASES) +def test_create_xz_index(records: list[tuple[int, int]], data: str) -> None: assert create_xz_index_footer(1, records)[:-12] == bytes.fromhex(data) @@ -159,14 +158,14 @@ def test_create_xz_index_invalid() -> None: assert str(exc_info.value) == "index record unpadded size" -@pytest.mark.parametrize("records, data", XZ_INDEX_CASES) -def test_parse_xz_index(records: List[Tuple[int, int]], data: str) -> None: +@pytest.mark.parametrize(["records", "data"], XZ_INDEX_CASES) +def test_parse_xz_index(records: list[tuple[int, int]], data: str) -> None: assert parse_xz_index(bytes.fromhex(data)) == records @pytest.mark.parametrize( - "data, message", - ( + ["data", "message"], + [ ("0000001cdf4421", "index length"), ("420000001cdf4421", "index indicator"), ("000000001cdf4221", "index crc32"), @@ -175,7 +174,7 @@ def test_parse_xz_index(records: List[Tuple[int, int]], data: str) -> None: ("000188047163b1d4", "index size"), ("000104002f70ea44", "index record uncompressed size"), ("000180180400420096a658c0", "index padding"), - ), + ], ) def test_parse_xz_index_invalid(data: str, message: str) -> None: with pytest.raises(XZError) as exc_info: @@ -192,7 +191,7 @@ def test_parse_xz_index_invalid(data: str, message: str) -> None: ) -@pytest.mark.parametrize("check, data", XZ_FOOTER_CASES) +@pytest.mark.parametrize(["check", "data"], XZ_FOOTER_CASES) def test_create_xz_footer(check: int, data: str) -> None: assert create_xz_index_footer(check, [])[-12:] == bytes.fromhex(data) @@ -203,21 +202,21 @@ def test_create_xz_footer_invalid_check() -> None: assert str(exc_info.value) == "footer check" -@pytest.mark.parametrize("check, data", XZ_FOOTER_CASES) +@pytest.mark.parametrize(["check", "data"], XZ_FOOTER_CASES) def test_parse_xz_footer(check: int, data: str) -> None: assert parse_xz_footer(bytes.fromhex(data)) == (check, 8) @pytest.mark.parametrize( - "data, message", - ( + ["data", "message"], + [ ("009042990d010000000001595a", "footer length"), ("9042990d0100000000015959", "footer magic"), ("9042090d010000000001595a", "footer crc32"), ("f4522e10010000000011595a", "footer flags"), ("d1738214010000000101595a", "footer flags"), ("c1505b47010000001001595a", "footer flags"), - ), + ], ) def test_parse_xz_footer_invalid(data: str, message: str) -> None: with pytest.raises(XZError) as exc_info: diff --git a/tests/unit/test_file.py b/tests/unit/test_file.py index 869d0b7..9c7641e 100644 --- a/tests/unit/test_file.py +++ b/tests/unit/test_file.py @@ -2,7 +2,7 @@ from io import SEEK_END, SEEK_SET, BytesIO, UnsupportedOperation import os from pathlib import Path -from typing import Optional, Tuple, Union, cast +from typing import Optional, Union, cast from unittest.mock import Mock, call import pytest @@ -73,7 +73,7 @@ "xb+", ) -EMPTY_XZ_FILE_WARNING_FILTER = "ignore:Empty XZFile*:RuntimeWarning:xz.file" +EMPTY_XZ_FILE_WARNING_FILTER = "ignore:Empty XZFile*:RuntimeWarning" # @@ -82,8 +82,8 @@ @pytest.mark.filterwarnings(EMPTY_XZ_FILE_WARNING_FILTER) -@pytest.mark.parametrize("init_has_ability", (False, True)) -@pytest.mark.parametrize("ability", ("seekable", "readable", "writable")) +@pytest.mark.parametrize("init_has_ability", [False, True]) +@pytest.mark.parametrize("ability", ["seekable", "readable", "writable"]) @pytest.mark.parametrize("mode", SUPPORTED_MODES) def test_required_abilities(mode: str, ability: str, init_has_ability: bool) -> None: fileobj = Mock(wraps=BytesIO(FILE_BYTES)) @@ -96,7 +96,7 @@ def test_required_abilities(mode: str, ability: str, init_has_ability: bool) -> ) if not init_has_ability and expected_ability: - with pytest.raises(ValueError): + with pytest.raises(ValueError, match=rf"^filename is not {ability}$"): XZFile(fileobj, mode=mode) else: with XZFile(fileobj, mode=mode) as xzfile: @@ -109,11 +109,11 @@ def test_required_abilities(mode: str, ability: str, init_has_ability: bool) -> # -@pytest.mark.parametrize("filetype", ("fileobj", "filename", "path")) +@pytest.mark.parametrize("filetype", ["fileobj", "filename", "path"]) def test_read( filetype: str, tmp_path: Path, - data_pattern_locate: Callable[[bytes], Tuple[int, int]], + data_pattern_locate: Callable[[bytes], tuple[int, int]], ) -> None: filename: Union[Path, BytesIO, str] @@ -178,13 +178,13 @@ def test_read( @pytest.mark.filterwarnings(EMPTY_XZ_FILE_WARNING_FILTER) -@pytest.mark.parametrize("from_file", (False, True)) +@pytest.mark.parametrize("from_file", [False, True]) @pytest.mark.parametrize("mode", SUPPORTED_MODES) def test_read_with_mode( mode: str, from_file: bool, tmp_path: Path, - data_pattern_locate: Callable[[bytes], Tuple[int, int]], + data_pattern_locate: Callable[[bytes], tuple[int, int]], ) -> None: filename: Union[Path, BytesIO] @@ -226,7 +226,7 @@ def test_read_invalid_filename_type() -> None: ) -@pytest.mark.parametrize("data", (b"", b"\x00" * 100), ids=("empty", "only-padding")) +@pytest.mark.parametrize("data", [b"", b"\x00" * 100], ids=("empty", "only-padding")) def test_read_no_stream(data: bytes) -> None: filename = BytesIO(data) @@ -243,7 +243,6 @@ def test_read_strategy_calls() -> None: with XZFile(fileobj, block_read_strategy=strategy) as xz_file: blocks = [ block - # pylint: disable=protected-access for stream in xz_file._fileobjs.values() for block in stream._fileobjs.values() ] @@ -268,7 +267,7 @@ def test_read_strategy_calls() -> None: ] -@pytest.mark.parametrize("max_block_read_nb", (None, 1, 2, 7, 100)) +@pytest.mark.parametrize("max_block_read_nb", [None, 1, 2, 7, 100]) def test_read_default_strategy(max_block_read_nb: Optional[int]) -> None: fileobj = Mock(wraps=BytesIO(FILE_BYTES_MANY_SMALL_BLOCKS)) @@ -402,26 +401,26 @@ def test_write() -> None: @pytest.mark.parametrize( - "mode, start_empty", + ["mode", "start_empty"], [ (mode, start_empty) for mode in SUPPORTED_MODES - if not mode[0] == "r" + if mode[0] != "r" for start_empty in ((True,) if mode[0] == "a" else (False, True)) ], ) def test_write_empty(mode: str, start_empty: bool) -> None: filename = BytesIO(b"" if start_empty else FILE_BYTES) - with pytest.warns(RuntimeWarning): + with pytest.warns(RuntimeWarning): # noqa: SIM117 with XZFile(filename, mode=mode): pass assert filename.getvalue() == b"" -@pytest.mark.parametrize("file_exists", (False, True)) -@pytest.mark.parametrize("from_file", (False, True)) +@pytest.mark.parametrize("file_exists", [False, True]) +@pytest.mark.parametrize("from_file", [False, True]) @pytest.mark.parametrize("mode", SUPPORTED_MODES) def test_write_with_mode( mode: str, from_file: bool, file_exists: bool, tmp_path: Path @@ -439,11 +438,10 @@ def test_write_with_mode( filename = tmp_path / "archive.xz" if file_exists: filename.write_bytes(initial_data) + elif file_exists: + filename = BytesIO(initial_data) else: - if file_exists: - filename = BytesIO(initial_data) - else: - filename = BytesIO() + filename = BytesIO() if not file_exists and "r" in mode: if from_file: @@ -475,9 +473,9 @@ def test_write_with_mode( if expected_success: if from_file: - value = cast(Path, filename).read_bytes() + value = cast("Path", filename).read_bytes() else: - value = cast(BytesIO, filename).getvalue() + value = cast("BytesIO", filename).getvalue() if "r" in mode: expected_value = bytes.fromhex( "fd377a585a000004e6d6b446" # header @@ -755,7 +753,7 @@ def test_change_preset_on_existing() -> None: @pytest.mark.parametrize( "mode", - ( + [ "rt", "r+t", "wt", @@ -777,22 +775,20 @@ def test_change_preset_on_existing() -> None: "rxt", "rx+t", "what-is-this", - ), + ], ) def test_invalid_mode(mode: str) -> None: filename = BytesIO(FILE_BYTES) - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^Invalid mode: "): XZFile(filename, mode) - assert str(exc_info.value) == f"invalid mode: {mode}" def test_fileno(tmp_path: Path) -> None: file_path = tmp_path / "file.xz" file_path.write_bytes(FILE_BYTES) - with file_path.open("rb") as fin: - with XZFile(fin) as xzfile: - assert xzfile.fileno() == fin.fileno() + with file_path.open("rb") as fin, XZFile(fin) as xzfile: + assert xzfile.fileno() == fin.fileno() def test_fileno_error(tmp_path: Path) -> None: @@ -802,6 +798,5 @@ def test_fileno_error(tmp_path: Path) -> None: with file_path.open("rb") as fin: mock = Mock(wraps=fin) mock.fileno.side_effect = AttributeError() - with XZFile(mock) as xzfile: - with pytest.raises(UnsupportedOperation): - xzfile.fileno() + with XZFile(mock) as xzfile, pytest.raises(UnsupportedOperation): + xzfile.fileno() diff --git a/tests/unit/test_floordict.py b/tests/unit/test_floordict.py index 9b26691..671af5a 100644 --- a/tests/unit/test_floordict.py +++ b/tests/unit/test_floordict.py @@ -1,18 +1,15 @@ -from typing import Dict - import pytest from xz.utils import FloorDict -def expect_floor_dict(floordict: FloorDict[str], items: Dict[int, str]) -> None: +def expect_floor_dict(floordict: FloorDict[str], items: dict[int, str]) -> None: sorted_keys = sorted(items) assert len(floordict) == len(items) assert list(floordict) == sorted_keys assert list(floordict.keys()) == sorted_keys assert list(floordict.values()) == [items[key] for key in sorted_keys] assert list(floordict.items()) == [(key, items[key]) for key in sorted_keys] - # pylint: disable=protected-access assert floordict._keys == sorted_keys assert floordict._dict == items @@ -23,13 +20,13 @@ def test_empty() -> None: expect_floor_dict(floordict, {}) with pytest.raises(KeyError): - floordict[0] # pylint: disable=pointless-statement + floordict[0] with pytest.raises(KeyError): - floordict[42] # pylint: disable=pointless-statement + floordict[42] with pytest.raises(KeyError): - floordict.last_key # pylint: disable=pointless-statement + floordict.last_key with pytest.raises(KeyError): - floordict.last_item # pylint: disable=pointless-statement + floordict.last_item def test_normal() -> None: @@ -52,14 +49,13 @@ def test_normal() -> None: assert floordict[1337] == "fifty" assert floordict.get(0) is None with pytest.raises(KeyError): - floordict[0] # pylint: disable=pointless-statement + floordict[0] assert floordict.get(7) is None with pytest.raises(KeyError): - floordict[7] # pylint: disable=pointless-statement + floordict[7] with pytest.raises(KeyError): - floordict[-42] # pylint: disable=pointless-statement + floordict[-42] with pytest.raises(TypeError): - # pylint: disable=pointless-statement floordict["wrong type"] # type: ignore[index] diff --git a/tests/unit/test_ioabstract.py b/tests/unit/test_ioabstract.py index 8ea9a07..03d771c 100644 --- a/tests/unit/test_ioabstract.py +++ b/tests/unit/test_ioabstract.py @@ -74,9 +74,8 @@ def test_tell_seek() -> None: assert obj.tell() == 3 assert obj.seek(10) == 10 assert obj.tell() == 10 - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^invalid seek position$"): obj.seek(-1) - assert str(exc_info.value) == "invalid seek position" assert obj.seek(42) == 42 assert obj.tell() == 42 @@ -85,9 +84,8 @@ def test_tell_seek() -> None: assert obj.tell() == 5 assert obj.seek(10, 0) == 10 assert obj.tell() == 10 - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^invalid seek position$"): obj.seek(-1, 0) - assert str(exc_info.value) == "invalid seek position" assert obj.seek(42, 0) == 42 assert obj.tell() == 42 @@ -97,9 +95,8 @@ def test_tell_seek() -> None: assert obj.tell() == 3 assert obj.seek(2, 1) == 5 assert obj.tell() == 5 - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^invalid seek position$"): obj.seek(-6, 1) - assert str(exc_info.value) == "invalid seek position" assert obj.tell() == 5 assert obj.seek(37, 1) == 42 assert obj.tell() == 42 @@ -113,20 +110,17 @@ def test_tell_seek() -> None: assert obj.tell() == 0 assert obj.seek(32, 2) == 42 assert obj.tell() == 42 - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^invalid seek position$"): obj.seek(-11, 2) - assert str(exc_info.value) == "invalid seek position" # from error - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^unsupported whence value$"): obj.seek(42, 3) - assert str(exc_info.value) == "unsupported whence value" # seek after close obj.close() - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^I/O operation on closed file$"): obj.seek(0) - assert str(exc_info.value) == "I/O operation on closed file" # @@ -184,9 +178,8 @@ def _write_after(self) -> None: # read after close obj.close() - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^I/O operation on closed file$"): obj.read(1) - assert str(exc_info.value) == "I/O operation on closed file" def test_tell_read_empty() -> None: @@ -195,7 +188,7 @@ def __init__(self) -> None: super().__init__(10) self.empty_reads = 100 - def _read(self, size: int) -> bytes: + def _read(self, size: int) -> bytes: # noqa: ARG002 self.empty_reads -= 1 if self.empty_reads > 0: return b"" @@ -226,7 +219,7 @@ def writable(self) -> bool: assert str(exc_info.value) == "write" -@pytest.mark.parametrize("write_partial", (True, False)) +@pytest.mark.parametrize("write_partial", [True, False]) def test_write_full(write_partial: bool) -> None: class Impl(IOAbstract): def __init__(self) -> None: @@ -248,9 +241,8 @@ def _write(self, data: bytes) -> int: with Impl() as obj: # write before end obj.seek(5) - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^write is only supported from EOF$"): obj.write(b"abcdef") - assert str(exc_info.value) == "write is only supported from EOF" assert not obj.mock.called # write at end @@ -322,9 +314,8 @@ def _write(self, data: bytes) -> int: obj.close() # write after close - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^I/O operation on closed file$"): obj.write(b"xyz") - assert str(exc_info.value) == "I/O operation on closed file" # @@ -342,12 +333,11 @@ def writable(self) -> bool: with Impl() as obj: assert obj.writable() is False - with pytest.raises(UnsupportedOperation) as exc_info: + with pytest.raises(UnsupportedOperation, match=r"^truncate$"): obj.truncate(4) - assert str(exc_info.value) == "truncate" -@pytest.mark.parametrize("with_size", (True, False)) +@pytest.mark.parametrize("with_size", [True, False]) def test_truncate_with_size(with_size: bool) -> None: class Impl(IOAbstract): def __init__(self) -> None: @@ -360,7 +350,7 @@ def _write_before(self) -> None: def _write_after(self) -> None: self.mock.write_finish() - def _write(self, data: bytes) -> int: + def _write(self, data: bytes) -> int: # noqa: ARG002 raise RuntimeError("should not be called") def _truncate(self, size: int) -> None: @@ -377,9 +367,8 @@ def truncate(size: int) -> int: return obj.truncate() # truncate before start - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^invalid truncate size$"): obj.truncate(-1) - assert str(exc_info.value) == "invalid truncate size" assert not obj.mock.method_calls # truncate before end @@ -411,6 +400,5 @@ def truncate(size: int) -> int: assert not obj.mock.method_calls # truncate after close - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^I/O operation on closed file$"): obj.truncate(5) - assert str(exc_info.value) == "I/O operation on closed file" diff --git a/tests/unit/test_iocombiner.py b/tests/unit/test_iocombiner.py index 8133d9d..7734af9 100644 --- a/tests/unit/test_iocombiner.py +++ b/tests/unit/test_iocombiner.py @@ -1,5 +1,5 @@ from io import SEEK_SET, BytesIO -from typing import List, cast +from typing import cast from unittest.mock import Mock, call import pytest @@ -9,9 +9,9 @@ def generate_mock(length: int) -> Mock: mock = Mock() - mock.__class__ = cast(Mock, IOAbstract) # needs to be subclass of IOAbstract - mock._length = length # pylint: disable=protected-access - mock.__len__ = lambda s: s._length # pylint: disable=protected-access + mock.__class__ = cast("Mock", IOAbstract) # needs to be subclass of IOAbstract + mock._length = length + mock.__len__ = lambda s: s._length def write(data: bytes) -> int: mock._length += len(data) @@ -49,7 +49,7 @@ def test_seek() -> None: def test_read() -> None: - originals: List[IOAbstract] = [ + originals: list[IOAbstract] = [ IOProxy(BytesIO(b"abc"), 0, 3), generate_mock(0), # size 0, will be never used IOProxy(BytesIO(b"defghij"), 0, 7), @@ -94,7 +94,7 @@ def test_read() -> None: assert originals[2].tell() == 3 # never used at all - assert not cast(Mock, originals[1]).method_calls + assert not cast("Mock", originals[1]).method_calls # @@ -173,18 +173,18 @@ def _create_fileobj(self) -> IOAbstract: parts[1].method_calls.clear() # force change fileobj - combiner._change_fileobj() # pylint: disable=protected-access + combiner._change_fileobj() assert len(parts) == 3 assert not parts[0].method_calls assert parts[1].method_calls == [ call.writable(), - call._write_end(), # pylint: disable=protected-access + call._write_end(), ] assert not parts[2].method_calls parts[1].method_calls.clear() # force change fileobj again - combiner._change_fileobj() # pylint: disable=protected-access + combiner._change_fileobj() assert len(parts) == 4 assert not parts[0].method_calls assert not parts[1].method_calls @@ -218,13 +218,13 @@ def _create_fileobj(self) -> IOAbstract: assert not parts[1].method_calls assert not parts[2].method_calls assert parts[3].method_calls == [ - call._write_end(), # pylint: disable=protected-access + call._write_end(), ] # check if last fileobj is empty no calls to _write_end with Combiner() as combiner: combiner.write(b"abc") - combiner._change_fileobj() # pylint: disable=protected-access + combiner._change_fileobj() parts[0].method_calls.clear() assert not parts[1].method_calls assert not parts[0].method_calls @@ -237,7 +237,6 @@ def _create_fileobj(self) -> IOAbstract: def test_truncate() -> None: - # pylint: disable=protected-access originals = [ generate_mock(2), generate_mock(0), @@ -300,9 +299,7 @@ def test_truncate() -> None: def test_append() -> None: combiner = IOCombiner[IOAbstract](generate_mock(13), generate_mock(37)) assert len(combiner) == 50 - combiner._append( # pylint: disable=protected-access - IOProxy(BytesIO(b"abcdefghij"), 0, 10) - ) + combiner._append(IOProxy(BytesIO(b"abcdefghij"), 0, 10)) assert len(combiner) == 60 combiner.seek(54) assert combiner.read(4) == b"efgh" @@ -312,5 +309,4 @@ def test_append_invalid() -> None: combiner = IOCombiner[IOAbstract](generate_mock(13), generate_mock(37)) assert len(combiner) == 50 with pytest.raises(TypeError): - # pylint: disable=protected-access combiner._append(BytesIO(b"abcdefghij")) # type: ignore[arg-type] diff --git a/tests/unit/test_open.py b/tests/unit/test_open.py index 4fc8b6d..31db982 100644 --- a/tests/unit/test_open.py +++ b/tests/unit/test_open.py @@ -1,7 +1,7 @@ from io import BytesIO import lzma from pathlib import Path -from typing import List, Optional +from typing import Optional from unittest.mock import Mock import pytest @@ -58,24 +58,23 @@ def test_mode_rt_file(tmp_path: Path) -> None: file_path = tmp_path / "file.xz" file_path.write_bytes(STREAM_BYTES) - with file_path.open("rb") as fin: - with xz_open(fin, "rt") as xzfile: - assert xzfile.stream_boundaries == [0] - assert xzfile.block_boundaries == [0, 10] - assert xzfile.fileno() == fin.fileno() + with file_path.open("rb") as fin, xz_open(fin, "rt") as xzfile: + assert xzfile.stream_boundaries == [0] + assert xzfile.block_boundaries == [0, 10] + assert xzfile.fileno() == fin.fileno() - assert xzfile.read() == "♥ utf8 ♥\n" + assert xzfile.read() == "♥ utf8 ♥\n" - assert xzfile.seek(9) == 9 - assert xzfile.read() == "♥\n" + assert xzfile.seek(9) == 9 + assert xzfile.read() == "♥\n" @pytest.mark.parametrize( - "encoding, expected", - ( + ["encoding", "expected"], + [ pytest.param("utf8", "еñϲоԺε", id="utf8"), pytest.param("latin1", "еñϲоԺε", id="latin1"), - ), + ], ) def test_mode_rt_encoding(encoding: str, expected: str) -> None: fileobj = BytesIO( @@ -89,8 +88,8 @@ def test_mode_rt_encoding(encoding: str, expected: str) -> None: @pytest.mark.parametrize( - "errors, expected", - ( + ["errors", "expected"], + [ pytest.param(None, None, id="None"), pytest.param("strict", None, id="strict"), pytest.param("ignore", "encoding", id="ignore"), @@ -98,7 +97,7 @@ def test_mode_rt_encoding(encoding: str, expected: str) -> None: pytest.param( "backslashreplace", r"en\x99co\x98di\x97ng", id="backslashreplace" ), - ), + ], ) def test_mode_rt_encoding_errors( errors: Optional[str], expected: Optional[str] @@ -112,23 +111,23 @@ def test_mode_rt_encoding_errors( with xz_open(fileobj, "rt", errors=errors) as xzfile: if expected is None: - with pytest.raises(ValueError): + with pytest.raises(UnicodeDecodeError): xzfile.read() else: assert xzfile.read() == expected @pytest.mark.parametrize( - "newline, expected", - ( + ["newline", "expected"], + [ pytest.param(None, ["a\n", "b\n", "c\n", "d"], id="None"), pytest.param("", ["a\n", "b\r", "c\r\n", "d"], id="''"), pytest.param("\n", ["a\n", "b\rc\r\n", "d"], id="'\n'"), pytest.param("\r", ["a\nb\r", "c\r", "\nd"], id="'\r'"), pytest.param("\r\n", ["a\nb\rc\r\n", "d"], id="'\r\n'"), - ), + ], ) -def test_mode_rt_newline(newline: Optional[str], expected: List[str]) -> None: +def test_mode_rt_newline(newline: Optional[str], expected: list[str]) -> None: fileobj = BytesIO( bytes.fromhex( "fd377a585a000000ff12d9410200210116000000742fe5a3010007610a620d63" @@ -142,19 +141,25 @@ def test_mode_rt_newline(newline: Optional[str], expected: List[str]) -> None: def test_mode_rb_encoding() -> None: fileobj = BytesIO(STREAM_BYTES) - with pytest.raises(ValueError): + with pytest.raises( + ValueError, match=r"^Argument 'encoding' not supported in binary mode$" + ): xz_open(fileobj, "rb", encoding="latin1") def test_mode_rb_encoding_errors() -> None: fileobj = BytesIO(STREAM_BYTES) - with pytest.raises(ValueError): + with pytest.raises( + ValueError, match=r"^Argument 'errors' not supported in binary mode$" + ): xz_open(fileobj, "rb", errors="ignore") def test_mode_rb_newline() -> None: fileobj = BytesIO(STREAM_BYTES) - with pytest.raises(ValueError): + with pytest.raises( + ValueError, match=r"^Argument 'newline' not supported in binary mode$" + ): xz_open(fileobj, "rb", newline="\n") @@ -399,11 +404,11 @@ def test_mode_wt_preset() -> None: @pytest.mark.parametrize( - "encoding, data", - ( + ["encoding", "data"], + [ pytest.param("utf8", "еñϲоԺε", id="utf8"), pytest.param("latin1", "еñϲоԺε", id="latin1"), - ), + ], ) def test_mode_wt_encoding(encoding: str, data: str) -> None: fileobj = BytesIO() @@ -417,8 +422,8 @@ def test_mode_wt_encoding(encoding: str, data: str) -> None: @pytest.mark.parametrize( - "errors, data", - ( + ["errors", "data"], + [ pytest.param(None, None, id="None"), pytest.param("strict", None, id="strict"), pytest.param( @@ -436,7 +441,7 @@ def test_mode_wt_encoding(encoding: str, data: str) -> None: rb"en\udc01co\udc02di\udc03ng", id="backslashreplace", ), - ), + ], ) def test_mode_wt_encoding_errors(errors: Optional[str], data: Optional[bytes]) -> None: fileobj = BytesIO() @@ -444,7 +449,7 @@ def test_mode_wt_encoding_errors(errors: Optional[str], data: Optional[bytes]) - with xz_open(fileobj, "wt", errors=errors) as xzfile: if data is None: xzfile.write("X") # to avoid having an empty file - with pytest.raises(ValueError): + with pytest.raises(UnicodeError): xzfile.write("en\udc01co\udc0di\udc03ng") else: xzfile.write("en\udc01co\udc02di\udc03ng") @@ -454,14 +459,14 @@ def test_mode_wt_encoding_errors(errors: Optional[str], data: Optional[bytes]) - @pytest.mark.parametrize( - "newline, data", - ( + ["newline", "data"], + [ pytest.param(None, b"a\nb\n", id="None"), pytest.param("", b"a\nb\n", id="''"), pytest.param("\n", b"a\nb\n", id="'\n'"), pytest.param("\r", b"a\rb\r", id="'\r'"), pytest.param("\r\n", b"a\r\nb\r\n", id="'\r\n'"), - ), + ], ) def test_mode_wt_newline(newline: Optional[str], data: bytes) -> None: fileobj = BytesIO() @@ -477,16 +482,15 @@ def test_mode_wt_newline(newline: Optional[str], data: bytes) -> None: # -@pytest.mark.parametrize("mode", ("rtb", "rbt", "wtb", "wbt")) +@pytest.mark.parametrize("mode", ["rtb", "rbt", "wtb", "wbt"]) def test_mode_invalid(mode: str) -> None: fileobj = BytesIO(STREAM_BYTES) - with pytest.raises(ValueError) as exc_info: + with pytest.raises(ValueError, match=r"^Invalid mode: "): xz_open(fileobj, mode) - assert str(exc_info.value) == f"Invalid mode: {mode}" -@pytest.mark.parametrize("mode", ("r", "rt")) +@pytest.mark.parametrize("mode", ["r", "rt"]) def test_default_strategy(mode: str) -> None: fileobj = BytesIO(STREAM_BYTES) @@ -494,7 +498,7 @@ def test_default_strategy(mode: str) -> None: assert isinstance(xzfile.block_read_strategy, RollingBlockReadStrategy) -@pytest.mark.parametrize("mode", ("r", "rt")) +@pytest.mark.parametrize("mode", ["r", "rt"]) def test_custom_strategy(mode: str) -> None: fileobj = BytesIO(STREAM_BYTES) strategy = Mock() diff --git a/tests/unit/test_parse_mode.py b/tests/unit/test_parse_mode.py index cda1d73..8fbece2 100644 --- a/tests/unit/test_parse_mode.py +++ b/tests/unit/test_parse_mode.py @@ -1,10 +1,5 @@ from itertools import permutations, product -from typing import Tuple - -try: - from typing import get_args -except ImportError: - pass +from typing import get_args import pytest @@ -38,10 +33,10 @@ def test_known_valid_modes_text() -> None: @pytest.mark.parametrize( - "mode, expected", + ["mode", "expected"], [pytest.param(mode, expected, id=mode) for mode, expected in VALID_MODES.items()], ) -def test_parse_mode_valid(mode: str, expected: Tuple[str, bool, bool]) -> None: +def test_parse_mode_valid(mode: str, expected: tuple[str, bool, bool]) -> None: for parts in permutations(mode): mode_permuted = "".join(parts) assert parse_mode(mode_permuted) == expected, mode_permuted @@ -59,5 +54,5 @@ def test_parse_mode_valid(mode: str, expected: Tuple[str, bool, bool]) -> None: def test_parse_mode_invalid(mode: str) -> None: for parts in permutations(mode): mode_permuted = "".join(parts) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match=r"^Invalid mode: "): parse_mode(mode_permuted) diff --git a/tests/unit/test_stream.py b/tests/unit/test_stream.py index 865f3ef..2faa4d2 100644 --- a/tests/unit/test_stream.py +++ b/tests/unit/test_stream.py @@ -1,6 +1,6 @@ from collections.abc import Callable -from io import SEEK_CUR, SEEK_END, BytesIO -from typing import Tuple, cast +from io import SEEK_CUR, SEEK_END, BytesIO, UnsupportedOperation +from typing import cast from unittest.mock import Mock, call import pytest @@ -26,7 +26,7 @@ ) -def test_parse(data_pattern_locate: Callable[[bytes], Tuple[int, int]]) -> None: +def test_parse(data_pattern_locate: Callable[[bytes], tuple[int, int]]) -> None: fileobj = Mock(wraps=BytesIO(b"\xff" * 1000 + STREAM_BYTES + b"\xee" * 1000)) fileobj.seek(-1000, SEEK_END) fileobj.method_calls.clear() @@ -48,7 +48,7 @@ def test_parse(data_pattern_locate: Callable[[bytes], Tuple[int, int]]) -> None: call.seek(-12, SEEK_CUR), ] - # fileobj should be at the begining of the stream + # fileobj should be at the beginning of the stream assert fileobj.tell() == 1000 # read from start @@ -119,7 +119,7 @@ def test_write(data_pattern: bytes) -> None: fileobj = BytesIO(b"A" * init_size) - with XZStream(cast(IOProxy, fileobj), 1) as stream: + with XZStream(cast("IOProxy", fileobj), 1) as stream: assert fileobj.getvalue() == b"A" * init_size assert stream.block_boundaries == [] @@ -187,24 +187,24 @@ def test_truncate_and_write(data_pattern: bytes) -> None: def test_truncate_middle_block() -> None: fileobj = BytesIO(STREAM_BYTES) fileobj.seek(0, SEEK_END) - with pytest.raises(ValueError) as exc_info: - with XZStream.parse(fileobj) as stream: - stream.truncate(80) - assert str(exc_info.value) == "truncate" + with ( + pytest.raises(UnsupportedOperation, match=r"^truncate$"), + XZStream.parse(fileobj) as stream, + ): + stream.truncate(80) def test_read_only_check() -> None: fileobj = BytesIO() - with XZStream(cast(IOProxy, fileobj), 1) as stream: - with pytest.raises(AttributeError): - stream.check = 4 # type: ignore[misc] + with XZStream(cast("IOProxy", fileobj), 1) as stream, pytest.raises(AttributeError): + stream.check = 4 # type: ignore[misc] def test_change_filters() -> None: fileobj = BytesIO() - with XZStream(cast(IOProxy, fileobj), 1) as stream: + with XZStream(cast("IOProxy", fileobj), 1) as stream: stream.write(b"aa") stream.change_block() stream.filters = [{"id": 3, "dist": 1}, {"id": 33}] @@ -235,7 +235,7 @@ def test_change_filters() -> None: def test_change_preset() -> None: fileobj = BytesIO() - with XZStream(cast(IOProxy, fileobj), 1) as stream: + with XZStream(cast("IOProxy", fileobj), 1) as stream: stream.write(b"aa") stream.change_block() stream.preset = 9 diff --git a/tox.ini b/tox.ini index 2b16a82..e71c71d 100644 --- a/tox.ini +++ b/tox.ini @@ -20,7 +20,6 @@ commands = py: -coverage html [testenv:build] -basepython = python3.11 skip_install = true deps = build==1.3.0 @@ -39,15 +38,11 @@ setenv = commands = pytest -vv -m generate_integration_files --generate-integration-files [testenv:lint] -basepython = python3.11 deps = - pylint==2.16.2 - pytest==7.2.1 # to avoid import errors ruff==0.13.1 commands = + ruff check src tests ruff format --check src tests - pylint src - pylint -d duplicate-code,too-many-statements,use-implicit-booleaness-not-comparison tests [testenv:type] basepython = python3.11 From fc0e183d363fd820d2db20e6e9f2b8bf696713f4 Mon Sep 17 00:00:00 2001 From: Rogdham Date: Sat, 20 Sep 2025 11:30:45 +0200 Subject: [PATCH 2/2] chore: replace confusable chars in tests --- pyproject.toml | 1 - tests/unit/test_open.py | 120 ++++++++++++++++++++-------------------- 2 files changed, 60 insertions(+), 61 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8e133d0..32118f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,6 @@ ignore = [ "TRY003", "TRY301", ] -allowed-confusables = ["α", "β", "γ", "δ", "ε", "ζ", "η", "θ"] [tool.ruff.lint.per-file-ignores] "tests/**" = [ diff --git a/tests/unit/test_open.py b/tests/unit/test_open.py index 31db982..4a377cd 100644 --- a/tests/unit/test_open.py +++ b/tests/unit/test_open.py @@ -170,22 +170,22 @@ def test_mode_rb_newline() -> None: TEST_MODE_W_CHECK_BYTES = bytes.fromhex( # stream 1 "fd377a585a0000016922de36" - "0200210116000000742fe5a3010001ceb1000000256bc6a8" + "0200210116000000742fe5a3010001d4b1000000fe91eb18" "00011602d06110d2" "9042990d010000000001595a" # stream 2 "fd377a585a0000016922de36" - "0200210116000000742fe5a3010001ceb20000009f3acf31" + "0200210116000000742fe5a3010001d4b200000044c0e281" "00011602d06110d2" "9042990d010000000001595a" # stream 3 (changed check) "fd377a585a000004e6d6b446" - "0200210116000000742fe5a3010001ceb3000000ab6cffc6b19a1d23" + "0200210116000000742fe5a3010001d4b30000009872e047a72fa9ba" "00011a02dc2ea57e" "1fb6f37d010000000004595a" # stream 4 (changed check) "fd377a585a000004e6d6b446" - "0200210116000000742fe5a3010001ceb4000000accd9792dc23671f" + "0200210116000000742fe5a3010001d4b40000009fd38813ca96d386" "00011a02dc2ea57e" "1fb6f37d010000000004595a" ) @@ -196,14 +196,14 @@ def test_mode_wb_check() -> None: with xz_open(fileobj, "wb", check=1) as xzfile: assert xzfile.mode == "w" - xzfile.write(b"\xce\xb1") + xzfile.write(b"\xd4\xb1") xzfile.change_stream() xzfile.check = 4 - xzfile.write(b"\xce\xb2") + xzfile.write(b"\xd4\xb2") xzfile.change_stream() - xzfile.write(b"\xce\xb3") + xzfile.write(b"\xd4\xb3") xzfile.change_stream() - xzfile.write(b"\xce\xb4") + xzfile.write(b"\xd4\xb4") assert fileobj.getvalue() == TEST_MODE_W_CHECK_BYTES @@ -213,14 +213,14 @@ def test_mode_wt_check() -> None: with xz_open(fileobj, "wt", check=1) as xzfile: assert xzfile.mode == "wt" - xzfile.write("α") + xzfile.write("Ա") xzfile.change_stream() xzfile.check = 4 - xzfile.write("β") + xzfile.write("Բ") xzfile.change_stream() - xzfile.write("γ") + xzfile.write("Գ") xzfile.change_stream() - xzfile.write("δ") + xzfile.write("Դ") assert fileobj.getvalue() == TEST_MODE_W_CHECK_BYTES @@ -230,13 +230,13 @@ def test_mode_wt_check() -> None: # header "fd377a585a0000016922de36" # block 1 - "0200210116000000742fe5a3010001ceb1000000256bc6a8" + "0200210116000000742fe5a3010001d4b1000000fe91eb18" # block 2 - "0200210116000000742fe5a3010001ceb20000009f3acf31" + "0200210116000000742fe5a3010001d4b200000044c0e281" # block 3 (changed filters) - "02010301002101167920c4ee010001cee5000000090ac846" + "02010301002101167920c4ee010001d4df000000d2f0e5f6" # block 4 (changed filters) - "02010301002101167920c4ee010001cee6000000aa9facd8" + "02010301002101167920c4ee010001d4e000000071658168" # index "0004160216021602160200008a2bb83b" # footer @@ -245,9 +245,9 @@ def test_mode_wt_check() -> None: # header "fd377a585a0000016922de36" # block 1 (changed filters) - "02010301002101167920c4ee010001cee70000003cafabaf" + "02010301002101167920c4ee010001d4e1000000e755861f" # block 2 (changed filters) - "02010301002101167920c4ee010001cee800000086fea236" + "02010301002101167920c4ee010001d4e20000005d048f86" # index "00021602160200008ba0042b" # footer @@ -256,9 +256,9 @@ def test_mode_wt_check() -> None: # header "fd377a585a0000016922de36" # block 1 (changed filters) - "02010301002101167920c4ee010001cee900000010cea541" + "02010301002101167920c4ee010001d4e3000000cb3488f1" # block 2 (changed filters) - "02010301002101167920c4ee010001ceea00000081d31ad1" + "02010301002101167920c4ee010001d4e40000005a293761" # index "00021602160200008ba0042b" # footer @@ -270,22 +270,22 @@ def test_mode_wb_filters() -> None: fileobj = BytesIO() with xz_open(fileobj, "wb", check=1) as xzfile: - xzfile.write(b"\xce\xb1") + xzfile.write(b"\xd4\xb1") xzfile.change_block() xzfile.filters = [{"id": 3, "dist": 1}, {"id": 33}] - xzfile.write(b"\xce\xb2") + xzfile.write(b"\xd4\xb2") xzfile.change_block() - xzfile.write(b"\xce\xb3") + xzfile.write(b"\xd4\xb3") xzfile.change_block() - xzfile.write(b"\xce\xb4") + xzfile.write(b"\xd4\xb4") xzfile.change_stream() - xzfile.write(b"\xce\xb5") + xzfile.write(b"\xd4\xb5") xzfile.change_block() - xzfile.write(b"\xce\xb6") + xzfile.write(b"\xd4\xb6") xzfile.change_stream() - xzfile.write(b"\xce\xb7") + xzfile.write(b"\xd4\xb7") xzfile.change_block() - xzfile.write(b"\xce\xb8") + xzfile.write(b"\xd4\xb8") assert fileobj.getvalue() == TEST_MODE_W_FILTERS_BYTES @@ -294,22 +294,22 @@ def test_mode_wt_filters() -> None: fileobj = BytesIO() with xz_open(fileobj, "wt", check=1) as xzfile: - xzfile.write("α") + xzfile.write("Ա") xzfile.change_block() xzfile.filters = [{"id": 3, "dist": 1}, {"id": 33}] - xzfile.write("β") + xzfile.write("Բ") xzfile.change_block() - xzfile.write("γ") + xzfile.write("Գ") xzfile.change_block() - xzfile.write("δ") + xzfile.write("Դ") xzfile.change_stream() - xzfile.write("ε") + xzfile.write("Ե") xzfile.change_block() - xzfile.write("ζ") + xzfile.write("Զ") xzfile.change_stream() - xzfile.write("η") + xzfile.write("Է") xzfile.change_block() - xzfile.write("θ") + xzfile.write("Ը") assert fileobj.getvalue() == TEST_MODE_W_FILTERS_BYTES @@ -319,13 +319,13 @@ def test_mode_wt_filters() -> None: # header "fd377a585a0000016922de36" # block 1 - "0200210116000000742fe5a3010001ceb1000000256bc6a8" + "0200210116000000742fe5a3010001d4b1000000fe91eb18" # block 2 - "0200210116000000742fe5a3010001ceb20000009f3acf31" + "0200210116000000742fe5a3010001d4b200000044c0e281" # block 3 (changed preset) - "020021011c00000010cf58cc010001ceb3000000090ac846" + "020021011c00000010cf58cc010001d4b3000000d2f0e5f6" # block 4 (changed preset) - "020021011c00000010cf58cc010001ceb4000000aa9facd8" + "020021011c00000010cf58cc010001d4b400000071658168" # index "0004160216021602160200008a2bb83b" # footer @@ -334,9 +334,9 @@ def test_mode_wt_filters() -> None: # header "fd377a585a0000016922de36" # block 1 (changed preset) - "020021011c00000010cf58cc010001ceb50000003cafabaf" + "020021011c00000010cf58cc010001d4b5000000e755861f" # block 2 (changed preset) - "020021011c00000010cf58cc010001ceb600000086fea236" + "020021011c00000010cf58cc010001d4b60000005d048f86" # index "00021602160200008ba0042b" # footer @@ -345,9 +345,9 @@ def test_mode_wt_filters() -> None: # header "fd377a585a0000016922de36" # block 1 (changed preset) - "020021011c00000010cf58cc010001ceb700000010cea541" + "020021011c00000010cf58cc010001d4b7000000cb3488f1" # block 2 (changed preset) - "020021011c00000010cf58cc010001ceb800000081d31ad1" + "020021011c00000010cf58cc010001d4b80000005a293761" # index "00021602160200008ba0042b" # footer @@ -359,22 +359,22 @@ def test_mode_wb_preset() -> None: fileobj = BytesIO() with xz_open(fileobj, "wb", check=1) as xzfile: - xzfile.write(b"\xce\xb1") + xzfile.write(b"\xd4\xb1") xzfile.change_block() xzfile.preset = 9 - xzfile.write(b"\xce\xb2") + xzfile.write(b"\xd4\xb2") xzfile.change_block() - xzfile.write(b"\xce\xb3") + xzfile.write(b"\xd4\xb3") xzfile.change_block() - xzfile.write(b"\xce\xb4") + xzfile.write(b"\xd4\xb4") xzfile.change_stream() - xzfile.write(b"\xce\xb5") + xzfile.write(b"\xd4\xb5") xzfile.change_block() - xzfile.write(b"\xce\xb6") + xzfile.write(b"\xd4\xb6") xzfile.change_stream() - xzfile.write(b"\xce\xb7") + xzfile.write(b"\xd4\xb7") xzfile.change_block() - xzfile.write(b"\xce\xb8") + xzfile.write(b"\xd4\xb8") assert fileobj.getvalue() == TEST_MODE_W_PRESET_BYTES @@ -383,22 +383,22 @@ def test_mode_wt_preset() -> None: fileobj = BytesIO() with xz_open(fileobj, "wt", check=1) as xzfile: - xzfile.write("α") + xzfile.write("Ա") xzfile.change_block() xzfile.preset = 9 - xzfile.write("β") + xzfile.write("Բ") xzfile.change_block() - xzfile.write("γ") + xzfile.write("Գ") xzfile.change_block() - xzfile.write("δ") + xzfile.write("Դ") xzfile.change_stream() - xzfile.write("ε") + xzfile.write("Ե") xzfile.change_block() - xzfile.write("ζ") + xzfile.write("Զ") xzfile.change_stream() - xzfile.write("η") + xzfile.write("Է") xzfile.change_block() - xzfile.write("θ") + xzfile.write("Ը") assert fileobj.getvalue() == TEST_MODE_W_PRESET_BYTES