py7zz 0.1.0.dev22__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
py7zz/cli.py ADDED
@@ -0,0 +1,90 @@
1
+ """
2
+ Command Line Interface Module
3
+
4
+ Directly passes through to the official 7zz binary, ensuring users get complete official 7-Zip functionality.
5
+ py7zz's value is in automatic binary management and providing Python API.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import subprocess
11
+ import sys
12
+
13
+ from .bundled_info import get_version_info
14
+ from .core import find_7z_binary
15
+
16
+
17
+ def print_version_info(format_type: str = "human") -> None:
18
+ """Print version information in specified format."""
19
+ try:
20
+ info = get_version_info()
21
+
22
+ if format_type == "json":
23
+ print(json.dumps(info, indent=2))
24
+ else:
25
+ print(f"py7zz version: {info['py7zz_version']}")
26
+ print(f"Bundled 7zz version: {info['bundled_7zz_version']}")
27
+ print(f"Release type: {info['release_type']}")
28
+ print(f"Release date: {info['release_date']}")
29
+ print(f"GitHub tag: {info['github_tag']}")
30
+ print(f"Changelog: {info['changelog_url']}")
31
+ except Exception as e:
32
+ print(f"py7zz error: {e}", file=sys.stderr)
33
+ sys.exit(1)
34
+
35
+
36
+ def main() -> None:
37
+ """
38
+ Main entry point: Handle py7zz-specific commands or pass through to official 7zz
39
+
40
+ This ensures:
41
+ 1. Users get complete official 7zz functionality
42
+ 2. py7zz-specific commands are handled properly
43
+ 3. No need to maintain parameter mapping and feature synchronization
44
+ 4. py7zz focuses on Python API and binary management
45
+ """
46
+ try:
47
+ # Handle py7zz-specific commands
48
+ if len(sys.argv) > 1:
49
+ command = sys.argv[1]
50
+
51
+ if command == "version":
52
+ # Handle version command
53
+ format_type = "human"
54
+ if len(sys.argv) > 2 and sys.argv[2] == "--format":
55
+ if len(sys.argv) > 3:
56
+ format_type = sys.argv[3]
57
+ else:
58
+ print("Error: --format requires a value (human or json)", file=sys.stderr)
59
+ sys.exit(1)
60
+
61
+ print_version_info(format_type)
62
+ return
63
+
64
+ elif command in ["--py7zz-version", "-V"]:
65
+ # Handle quick version command
66
+ print_version_info("human")
67
+ return
68
+
69
+ # Get py7zz-managed 7zz binary
70
+ binary_path = find_7z_binary()
71
+
72
+ # Direct pass-through of all command line arguments
73
+ cmd = [binary_path] + sys.argv[1:]
74
+
75
+ # Use exec to replace current process, ensuring signal handling behavior is consistent with native 7zz
76
+ if os.name == "nt": # Windows
77
+ # Use subprocess on Windows and wait for result
78
+ result = subprocess.run(cmd)
79
+ sys.exit(result.returncode)
80
+ else: # Unix-like systems
81
+ # Use execv to replace process on Unix
82
+ os.execv(binary_path, cmd)
83
+
84
+ except Exception as e:
85
+ print(f"py7zz error: {e}", file=sys.stderr)
86
+ sys.exit(1)
87
+
88
+
89
+ if __name__ == "__main__":
90
+ main()
py7zz/compression.py ADDED
@@ -0,0 +1,128 @@
1
+ """
2
+ Compression Algorithm Interface Module
3
+
4
+ Provides a simple interface similar to modern compression libraries for single-stream compression.
5
+ This is a complement to SevenZipFile archive functionality, not a replacement.
6
+ """
7
+
8
+ import tempfile
9
+ from pathlib import Path
10
+ from typing import Union
11
+
12
+ from .core import run_7z
13
+
14
+
15
+ def compress(data: Union[str, bytes], algorithm: str = "lzma2", level: int = 5) -> bytes:
16
+ """
17
+ Compress a single data block, similar to zstd.compress()
18
+
19
+ Args:
20
+ data: Data to compress
21
+ algorithm: Compression algorithm (lzma2, lzma, ppmd, bzip2, deflate)
22
+ level: Compression level (0-9)
23
+
24
+ Returns:
25
+ Compressed byte data
26
+ """
27
+ if isinstance(data, str):
28
+ data = data.encode("utf-8")
29
+
30
+ with tempfile.TemporaryDirectory() as tmpdir:
31
+ tmpdir_path = Path(tmpdir)
32
+
33
+ # Write to temporary file
34
+ input_file = tmpdir_path / "input.dat"
35
+ input_file.write_bytes(data)
36
+
37
+ # Compress as 7z single-file archive
38
+ output_file = tmpdir_path / "output.7z"
39
+ args = [
40
+ "a",
41
+ str(output_file),
42
+ str(input_file),
43
+ f"-mx{level}",
44
+ f"-m0={algorithm}",
45
+ "-ms=off", # Disable solid mode
46
+ ]
47
+
48
+ run_7z(args)
49
+
50
+ # Read compressed result
51
+ return output_file.read_bytes()
52
+
53
+
54
+ def decompress(data: bytes) -> bytes:
55
+ """
56
+ Decompress a single data block, similar to zstd.decompress()
57
+
58
+ Args:
59
+ data: Compressed byte data
60
+
61
+ Returns:
62
+ Decompressed byte data
63
+ """
64
+ with tempfile.TemporaryDirectory() as tmpdir:
65
+ tmpdir_path = Path(tmpdir)
66
+
67
+ # Write compressed data
68
+ input_file = tmpdir_path / "input.7z"
69
+ input_file.write_bytes(data)
70
+
71
+ # Decompress
72
+ output_dir = tmpdir_path / "output"
73
+ args = ["x", str(input_file), f"-o{output_dir}", "-y"]
74
+
75
+ run_7z(args)
76
+
77
+ # Find decompressed files
78
+ output_files = list(output_dir.glob("*"))
79
+ if not output_files:
80
+ raise ValueError("No files found in archive")
81
+
82
+ # Return first file's content
83
+ return output_files[0].read_bytes()
84
+
85
+
86
+ class Compressor:
87
+ """
88
+ Compressor class, provides an interface similar to zstd.ZstdCompressor
89
+ """
90
+
91
+ def __init__(self, algorithm: str = "lzma2", level: int = 5):
92
+ self.algorithm = algorithm
93
+ self.level = level
94
+
95
+ def compress(self, data: Union[str, bytes]) -> bytes:
96
+ """Compress data"""
97
+ return compress(data, self.algorithm, self.level)
98
+
99
+
100
+ class Decompressor:
101
+ """
102
+ Decompressor class, provides an interface similar to zstd.ZstdDecompressor
103
+ """
104
+
105
+ def decompress(self, data: bytes) -> bytes:
106
+ """Decompress data"""
107
+ return decompress(data)
108
+
109
+
110
+ # Convenience functions, mimicking modern compression libraries
111
+ def lzma2_compress(data: Union[str, bytes], level: int = 5) -> bytes:
112
+ """LZMA2 compression"""
113
+ return compress(data, "lzma2", level)
114
+
115
+
116
+ def lzma2_decompress(data: bytes) -> bytes:
117
+ """LZMA2 decompression"""
118
+ return decompress(data)
119
+
120
+
121
+ def bzip2_compress(data: Union[str, bytes], level: int = 5) -> bytes:
122
+ """BZIP2 compression"""
123
+ return compress(data, "bzip2", level)
124
+
125
+
126
+ def bzip2_decompress(data: bytes) -> bytes:
127
+ """BZIP2 decompression"""
128
+ return decompress(data)
py7zz/config.py ADDED
@@ -0,0 +1,260 @@
1
+ """
2
+ Configuration and Preset System
3
+
4
+ Provides advanced configuration options and preset configurations
5
+ for different use cases.
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from typing import Any, List, Optional
10
+
11
+
12
+ @dataclass
13
+ class Config:
14
+ """
15
+ Advanced configuration for 7z operations.
16
+
17
+ This class allows fine-grained control over compression parameters.
18
+ """
19
+
20
+ # Compression settings
21
+ compression: str = "lzma2" # lzma2, lzma, ppmd, bzip2, deflate
22
+ level: int = 5 # 0-9, higher = better compression
23
+ solid: bool = True # Solid archive (better compression)
24
+
25
+ # Performance settings
26
+ threads: Optional[int] = None # Number of threads (None = auto)
27
+ memory_limit: Optional[str] = None # Memory limit (e.g., "1g", "512m")
28
+
29
+ # Security settings
30
+ password: Optional[str] = None # Archive password
31
+ encrypt_filenames: bool = False # Encrypt file names
32
+
33
+ # Advanced options
34
+ dictionary_size: Optional[str] = None # Dictionary size (e.g., "32m")
35
+ word_size: Optional[int] = None # Word size for LZMA
36
+ fast_bytes: Optional[int] = None # Fast bytes for LZMA
37
+
38
+ def to_7z_args(self) -> List[str]:
39
+ """Convert config to 7z command line arguments."""
40
+ args = []
41
+
42
+ # Compression level
43
+ args.append(f"-mx{self.level}")
44
+
45
+ # Compression method
46
+ args.append(f"-m0={self.compression}")
47
+
48
+ # Solid archive
49
+ if not self.solid:
50
+ args.append("-ms=off")
51
+
52
+ # Threads
53
+ if self.threads is not None:
54
+ args.append(f"-mmt{self.threads}")
55
+
56
+ # Memory limit
57
+ if self.memory_limit:
58
+ args.append(f"-mmemuse={self.memory_limit}")
59
+
60
+ # Dictionary size
61
+ if self.dictionary_size:
62
+ args.append(f"-md={self.dictionary_size}")
63
+
64
+ # Word size
65
+ if self.word_size:
66
+ args.append(f"-mfb={self.word_size}")
67
+
68
+ # Fast bytes
69
+ if self.fast_bytes:
70
+ args.append(f"-mfb={self.fast_bytes}")
71
+
72
+ # Password
73
+ if self.password:
74
+ args.append(f"-p{self.password}")
75
+
76
+ # Encrypt filenames
77
+ if self.encrypt_filenames and self.password:
78
+ args.append("-mhe")
79
+
80
+ return args
81
+
82
+
83
+ class Presets:
84
+ """
85
+ Predefined configurations for common use cases.
86
+ """
87
+
88
+ @staticmethod
89
+ def fast() -> Config:
90
+ """
91
+ Fast compression preset.
92
+
93
+ Optimized for speed over compression ratio.
94
+ Good for temporary files or when time is critical.
95
+ """
96
+ return Config(
97
+ compression="lzma2",
98
+ level=1,
99
+ solid=False,
100
+ threads=None, # Use all available threads
101
+ )
102
+
103
+ @staticmethod
104
+ def balanced() -> Config:
105
+ """
106
+ Balanced preset (default).
107
+
108
+ Good balance between compression ratio and speed.
109
+ Suitable for most general-purpose compression tasks.
110
+ """
111
+ return Config(
112
+ compression="lzma2",
113
+ level=5,
114
+ solid=True,
115
+ threads=None,
116
+ )
117
+
118
+ @staticmethod
119
+ def backup() -> Config:
120
+ """
121
+ Backup preset.
122
+
123
+ Optimized for maximum compression ratio.
124
+ Good for long-term storage where space matters more than time.
125
+ """
126
+ return Config(
127
+ compression="lzma2",
128
+ level=7,
129
+ solid=True,
130
+ dictionary_size="64m",
131
+ threads=None,
132
+ )
133
+
134
+ @staticmethod
135
+ def ultra() -> Config:
136
+ """
137
+ Ultra compression preset.
138
+
139
+ Maximum compression ratio at the cost of speed.
140
+ Use when storage space is extremely limited.
141
+ """
142
+ return Config(
143
+ compression="lzma2",
144
+ level=9,
145
+ solid=True,
146
+ dictionary_size="128m",
147
+ word_size=64,
148
+ fast_bytes=64,
149
+ threads=1, # Single thread for maximum compression
150
+ )
151
+
152
+ @staticmethod
153
+ def secure() -> Config:
154
+ """
155
+ Secure preset with encryption.
156
+
157
+ Balanced compression with password protection.
158
+ Note: Password must be set separately.
159
+ """
160
+ return Config(
161
+ compression="lzma2",
162
+ level=5,
163
+ solid=True,
164
+ encrypt_filenames=True,
165
+ # password must be set by user
166
+ )
167
+
168
+ @staticmethod
169
+ def compatibility() -> Config:
170
+ """
171
+ Compatibility preset.
172
+
173
+ Uses widely supported compression methods.
174
+ Good for archives that need to be opened on older systems.
175
+ """
176
+ return Config(
177
+ compression="deflate",
178
+ level=6,
179
+ solid=False,
180
+ )
181
+
182
+ @classmethod
183
+ def get_preset(cls, name: str) -> Config:
184
+ """
185
+ Get a preset configuration by name.
186
+
187
+ Args:
188
+ name: Preset name ("fast", "balanced", "backup", "ultra", "secure", "compatibility")
189
+
190
+ Returns:
191
+ Config object for the specified preset
192
+
193
+ Raises:
194
+ ValueError: If preset name is not recognized
195
+ """
196
+ presets = {
197
+ "fast": cls.fast,
198
+ "balanced": cls.balanced,
199
+ "backup": cls.backup,
200
+ "ultra": cls.ultra,
201
+ "secure": cls.secure,
202
+ "compatibility": cls.compatibility,
203
+ }
204
+
205
+ if name not in presets:
206
+ available = ", ".join(presets.keys())
207
+ raise ValueError(f"Unknown preset '{name}'. Available presets: {available}")
208
+
209
+ return presets[name]()
210
+
211
+ @classmethod
212
+ def list_presets(cls) -> List[str]:
213
+ """List all available preset names."""
214
+ return ["fast", "balanced", "backup", "ultra", "secure", "compatibility"]
215
+
216
+
217
+ def create_custom_config(**kwargs: Any) -> Config:
218
+ """
219
+ Create a custom configuration with specified parameters.
220
+
221
+ Args:
222
+ **kwargs: Any Config parameters to override
223
+
224
+ Returns:
225
+ Config object with specified parameters
226
+
227
+ Example:
228
+ >>> config = create_custom_config(level=9, threads=4, password="secret")
229
+ >>> # Use with SevenZipFile or create_archive
230
+ """
231
+ return Config(**kwargs)
232
+
233
+
234
+ def get_recommended_preset(purpose: str) -> Config:
235
+ """
236
+ Get recommended preset based on intended purpose.
237
+
238
+ Args:
239
+ purpose: Intended use ("temp", "backup", "distribution", "secure", "fast")
240
+
241
+ Returns:
242
+ Recommended Config object
243
+ """
244
+ recommendations = {
245
+ "temp": Presets.fast(),
246
+ "temporary": Presets.fast(),
247
+ "backup": Presets.backup(),
248
+ "archive": Presets.backup(),
249
+ "distribution": Presets.balanced(),
250
+ "share": Presets.balanced(),
251
+ "secure": Presets.secure(),
252
+ "encrypted": Presets.secure(),
253
+ "fast": Presets.fast(),
254
+ "quick": Presets.fast(),
255
+ "max": Presets.ultra(),
256
+ "maximum": Presets.ultra(),
257
+ "ultra": Presets.ultra(),
258
+ }
259
+
260
+ return recommendations.get(purpose.lower(), Presets.balanced())