Coverage for src / c41811 / config / processor / zipfile.py: 100%
59 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-06 06:04 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-06 06:04 +0000
1# cython: language_level = 3 # noqa: ERA001
4"""
5Zip压缩配置文件处理器
7.. versionadded:: 0.2.0
8"""
10import itertools
11import os
12import zipfile
13from dataclasses import dataclass
14from enum import ReprEnum
15from typing import Literal
16from typing import cast
17from typing import override
19from ..basic.core import ConfigFile
20from ..main import BasicCompressedConfigSL
21from ..safe_writer import safe_open
24@dataclass(frozen=True)
25class ZipCompressionType:
26 """压缩类型数据结构"""
28 full_name: str
29 short_name: str | None
30 zipfile_constant: int
33class ZipCompressionTypes(ZipCompressionType, ReprEnum):
34 """压缩类型"""
36 ONLY_STORAGE = ("only-storage", None, zipfile.ZIP_STORED)
38 ZIP = ("zip", "zip", zipfile.ZIP_DEFLATED)
39 BZIP2 = ("bzip2", "bz2", zipfile.ZIP_BZIP2)
40 LZMA = ("lzma", "xz", zipfile.ZIP_LZMA)
43class ZipFileSL(BasicCompressedConfigSL):
44 """zip格式处理器"""
46 def __init__(
47 self,
48 *,
49 reg_alias: str | None = None,
50 create_dir: bool = True,
51 compression: ZipCompressionTypes | str | int | None = ZipCompressionTypes.ONLY_STORAGE,
52 compress_level: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | int | None = None,
53 ):
54 """
55 :param reg_alias: sl处理器注册别名
56 :type reg_alias: str | None
57 :param create_dir: 是否创建目录
58 :type create_dir: bool
59 :param compression: 压缩类型
60 :type compression: ZipCompressionTypes | str | int | None
61 :param compress_level: 压缩等级
62 :type compress_level: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | int | None
63 """ # noqa: D205
64 super().__init__(reg_alias=reg_alias, create_dir=create_dir)
66 if compression is None:
67 compression = ZipCompressionTypes.ONLY_STORAGE
68 elif isinstance(compression, str | int):
69 for compression_type in ZipCompressionTypes:
70 if compression in (
71 compression_type.full_name,
72 compression_type.short_name,
73 compression_type.zipfile_constant,
74 ):
75 compression = compression_type
76 break
78 self._compression: ZipCompressionType = cast(ZipCompressionTypes, compression)
79 self._compress_level: int | None = compress_level
80 self._short_name = "" if self._compression.short_name is None else self._compression.short_name
82 @property
83 @override
84 def processor_reg_name(self) -> str:
85 return f"zipfile:{self._short_name}-{self._compress_level}"
87 @property
88 @override
89 def namespace_suffix(self) -> str:
90 safe_name = self.processor_reg_name.replace(":", "-")
91 return os.path.join(super().namespace_suffix, f"${safe_name}~")
93 @property
94 @override
95 def supported_file_patterns(self) -> tuple[str, ...]:
96 if self._compression.short_name is None:
97 return f".{self._compress_level}.zip", ".zip"
98 return (
99 f".{self._compress_level}.{self._compression.full_name}",
100 f".{self._compress_level}.{self._compression.short_name}",
101 f".{self._compression.short_name}",
102 f".{self._compression.full_name}",
103 )
105 supported_file_classes = [ConfigFile] # noqa: RUF012
107 @override
108 def compress_file(self, file_path: str, extract_dir: str) -> None:
109 with (
110 safe_open(file_path, "wb") as file,
111 zipfile.ZipFile(
112 file, mode="w", compression=self._compression.zipfile_constant, compresslevel=self._compress_level
113 ) as zip_file,
114 ):
115 for root, dirs, files in os.walk(extract_dir):
116 for item in itertools.chain(dirs, files):
117 path = os.path.normpath(os.path.join(root, item))
118 zip_file.write(path, arcname=os.path.relpath(path, extract_dir))
120 @override
121 def extract_file(self, file_path: str, extract_dir: str) -> None:
122 with safe_open(file_path, "rb") as file, zipfile.ZipFile(file) as zip_file:
123 zip_file.extractall(extract_dir)
126__all__ = (
127 "ZipCompressionType",
128 "ZipCompressionTypes",
129 "ZipFileSL",
130)