-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patharchive_info.py
76 lines (65 loc) · 2.61 KB
/
archive_info.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import dataclasses
import datetime
import functools
import locale
import re
import shutil
import subprocess
from pathlib import Path, PurePath
from typing import Optional, Union
@dataclasses.dataclass
class ArchiveFileInfo:
path: PurePath
st_size: int
st_mtime: float
@property
def name(self) -> str:
return self.path.name
class ArchiveFileReader:
_specify_7z_program = None
@classmethod
@functools.cache
def _find_7z_program(cls) -> Path:
seven_zip = shutil.which('7z') or shutil.which('7za') or shutil.which('7zz')
return Path(seven_zip) if seven_zip else None
@classmethod
def _7z_program(cls) -> Path:
return cls._specify_7z_program if cls._specify_7z_program else cls._find_7z_program()
@classmethod
def specify_7z_program(cls, path: Optional[Union[Path, str, bytes]]):
cls._specify_7z_program = Path(path) if path else None
def __init__(self, file: Path):
self.file = file
@property
def filelist(self):
return self.infolist
@property
@functools.cache
def infolist(self):
process = subprocess.Popen([self._7z_program(), 'l', '-ba', '-slt', self.file], stdout=subprocess.PIPE)
output, _ = process.communicate()
assert process.wait() == 0
infos = []
for line in output.splitlines():
mat = re.match(rb'(\w+) = (.*)', line)
if mat:
field_name, value = mat[1].decode(), mat[2].decode(locale.getpreferredencoding())
if field_name == 'Path':
infos.append({})
current_info = infos[-1]
if field_name == 'Path':
current_info['path'] = PurePath(value)
elif field_name == 'Size':
current_info['st_size'] = int(value)
elif field_name == 'Attributes':
current_info['is_file'] = 'A' in value
elif field_name == 'Modified':
if '.' in value:
datetime_part, nanoseconds_str = value.split('.')
else:
datetime_part, nanoseconds_str = value, '0'
nanoseconds = float('0.' + nanoseconds_str)
timestamp = datetime.datetime.fromisoformat(datetime_part).timestamp() + nanoseconds
current_info['st_mtime'] = timestamp
return list(filter(None, map(lambda info: (info.pop('is_file'), ArchiveFileInfo(**info))[1] if info[
'is_file'] else None, infos)))