A Python port of the Invisible Internet Project (I2P)
1"""File utility operations.
2
3Copy, delete, zip extraction, checksums, read/write.
4
5Ported from net.i2p.util.FileUtil.
6"""
7
8from __future__ import annotations
9
10import hashlib
11import os
12import shutil
13import zipfile
14
15
16def copy_file(src: str, dst: str) -> bool:
17 """Copy a file. Returns True on success."""
18 try:
19 shutil.copy2(src, dst)
20 return True
21 except (OSError, FileNotFoundError):
22 return False
23
24
25def rm_dir(path: str, recursive: bool = True) -> bool:
26 """Remove a directory. Returns True on success."""
27 try:
28 if recursive:
29 shutil.rmtree(path)
30 else:
31 os.rmdir(path)
32 return True
33 except (OSError, FileNotFoundError):
34 return False
35
36
37def extract_zip(zip_path: str, dest_dir: str) -> bool:
38 """Extract a ZIP archive to dest_dir. Returns True on success."""
39 try:
40 with zipfile.ZipFile(zip_path, "r") as zf:
41 zf.extractall(dest_dir)
42 return True
43 except (zipfile.BadZipFile, OSError):
44 return False
45
46
47def verify_zip(zip_path: str) -> bool:
48 """Verify a ZIP archive is valid."""
49 try:
50 with zipfile.ZipFile(zip_path, "r") as zf:
51 return zf.testzip() is None
52 except (zipfile.BadZipFile, OSError):
53 return False
54
55
56def file_checksum(path: str, algorithm: str = "sha256") -> str | None:
57 """Compute hex checksum of a file. Returns None on error."""
58 try:
59 h = hashlib.new(algorithm)
60 with open(path, "rb") as f:
61 for chunk in iter(lambda: f.read(65536), b""):
62 h.update(chunk)
63 return h.hexdigest()
64 except (OSError, ValueError):
65 return None
66
67
68def read_file(path: str) -> bytes | None:
69 """Read file contents. Returns None on error."""
70 try:
71 with open(path, "rb") as f:
72 return f.read()
73 except OSError:
74 return None
75
76
77def write_file(path: str, data: bytes) -> bool:
78 """Write data to file. Returns True on success."""
79 try:
80 with open(path, "wb") as f:
81 f.write(data)
82 return True
83 except OSError:
84 return False