nltfile 1.0.28__py3-none-any.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.
- nltfile/__init__.py +18 -0
- nltfile/compress/__init__.py +6 -0
- nltfile/compress/allfile.py +12 -0
- nltfile/compress/tarfile.py +105 -0
- nltfile/compress/utils.py +31 -0
- nltfile/compress/zipfile.py +6 -0
- nltfile/file/__init__.py +4 -0
- nltfile/file/concurrent.py +113 -0
- nltfile/file/core.py +5 -0
- nltfile/funos.py +11 -0
- nltfile/pickle/__init__.py +3 -0
- nltfile/pickle/core.py +19 -0
- nltfile/utils/__init__.py +13 -0
- nltfile/utils/hash.py +33 -0
- nltfile/utils/size.py +16 -0
- nltfile/utils/tqdm_bar.py +17 -0
- nltfile-1.0.28.dist-info/METADATA +178 -0
- nltfile-1.0.28.dist-info/RECORD +20 -0
- nltfile-1.0.28.dist-info/WHEEL +5 -0
- nltfile-1.0.28.dist-info/top_level.txt +1 -0
nltfile/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from .compress import tarfile, zipfile
|
|
2
|
+
from .file import ConcurrentFile
|
|
3
|
+
from .utils import file_size, file_tqdm_bar
|
|
4
|
+
from .utils import file_hash, file_sha1, file_sha256, file_sha512, file_md5
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"tarfile",
|
|
8
|
+
"zipfile",
|
|
9
|
+
"file_tqdm_bar",
|
|
10
|
+
"file_size",
|
|
11
|
+
"ConcurrentFile",
|
|
12
|
+
"file_size",
|
|
13
|
+
"file_hash",
|
|
14
|
+
"file_md5",
|
|
15
|
+
"file_sha1",
|
|
16
|
+
"file_sha512",
|
|
17
|
+
"file_sha256",
|
|
18
|
+
]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from nltfile.compress import tarfile, zipfile
|
|
2
|
+
|
|
3
|
+
_TAR_EXTENSIONS = (".tar", ".tar.gz", ".tgz", ".tar.bz2", ".tar.xz", ".txz")
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def extractall(archive_path: str, path: str = "."):
|
|
7
|
+
if archive_path.endswith(".zip"):
|
|
8
|
+
with zipfile.ZipFile(archive_path) as zf:
|
|
9
|
+
zf.extractall(path=path)
|
|
10
|
+
elif archive_path.endswith(_TAR_EXTENSIONS):
|
|
11
|
+
with tarfile.TarFile(archive_path) as tf:
|
|
12
|
+
tf.extractall(path=path)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import os
|
|
3
|
+
import tarfile
|
|
4
|
+
|
|
5
|
+
from nltfile.utils import file_tqdm_bar
|
|
6
|
+
from tqdm import tqdm
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ProgressFileIO(io.FileIO):
|
|
10
|
+
def __init__(self, path, progress=None, *args, **kwargs):
|
|
11
|
+
super(ProgressFileIO, self).__init__(path, *args, **kwargs)
|
|
12
|
+
self._progress = progress
|
|
13
|
+
|
|
14
|
+
def read(self, size=None) -> bytes:
|
|
15
|
+
self._progress.update(self.tell() - self._progress.n)
|
|
16
|
+
return io.FileIO.read(self, size)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FileWrapper(object):
|
|
20
|
+
def __init__(self, fileobj, progress: tqdm, *args, **kwargs):
|
|
21
|
+
super(FileWrapper, self).__init__(*args, **kwargs)
|
|
22
|
+
self._fileobj = fileobj
|
|
23
|
+
self._progress = progress
|
|
24
|
+
|
|
25
|
+
def _update(self, current):
|
|
26
|
+
if self._progress is not None:
|
|
27
|
+
if current > self._progress.total:
|
|
28
|
+
self._progress.total = current
|
|
29
|
+
self._progress.update(current - self._progress.n)
|
|
30
|
+
|
|
31
|
+
def read(self, size=-1):
|
|
32
|
+
self._update(self._fileobj.tell())
|
|
33
|
+
return self._fileobj.read(size)
|
|
34
|
+
|
|
35
|
+
def readline(self, size=-1):
|
|
36
|
+
self._update(self._fileobj.tell())
|
|
37
|
+
return self._fileobj.readline(size)
|
|
38
|
+
|
|
39
|
+
def __getattr__(self, name):
|
|
40
|
+
return getattr(self._fileobj, name)
|
|
41
|
+
|
|
42
|
+
def __del__(self):
|
|
43
|
+
if self._fileobj is not None:
|
|
44
|
+
self._update(self._fileobj.tell())
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TarFile(tarfile.TarFile):
|
|
48
|
+
def __init__(self, name=None, mode="r", fileobj=None, **kwargs):
|
|
49
|
+
self._progress = None
|
|
50
|
+
if "r" in mode:
|
|
51
|
+
self._progress = file_tqdm_bar(name, prefix="解压")
|
|
52
|
+
if fileobj is not None:
|
|
53
|
+
fileobj = FileWrapper(fileobj, progress=self._progress)
|
|
54
|
+
else:
|
|
55
|
+
fileobj = ProgressFileIO(name, progress=self._progress)
|
|
56
|
+
super(TarFile, self).__init__(name=name, mode=mode, fileobj=fileobj, **kwargs)
|
|
57
|
+
if "r" in mode and self._progress is not None:
|
|
58
|
+
self._progress.total = self.tar_size()
|
|
59
|
+
|
|
60
|
+
def _check_progress_available(self) -> bool:
|
|
61
|
+
if self._progress is None:
|
|
62
|
+
return False
|
|
63
|
+
return self._progress.n < self._progress.total
|
|
64
|
+
|
|
65
|
+
def addfile(self, tarinfo, fileobj=None):
|
|
66
|
+
if fileobj is not None:
|
|
67
|
+
fileobj = FileWrapper(fileobj, progress=self._progress)
|
|
68
|
+
return super(TarFile, self).addfile(tarinfo, fileobj)
|
|
69
|
+
|
|
70
|
+
def add(
|
|
71
|
+
self,
|
|
72
|
+
name,
|
|
73
|
+
arcname=None,
|
|
74
|
+
recursive=True,
|
|
75
|
+
filter=None,
|
|
76
|
+
progress=None,
|
|
77
|
+
*args,
|
|
78
|
+
**kwargs,
|
|
79
|
+
):
|
|
80
|
+
self._progress = progress or self._progress or file_tqdm_bar(name)
|
|
81
|
+
return super(TarFile, self).add(
|
|
82
|
+
name=name, arcname=arcname, recursive=recursive, filter=filter
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def tar_size(self):
|
|
86
|
+
return sum(m.size for m in self.getmembers())
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
tar_open: TarFile = TarFile.open
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def file_entar(src_path, dst_path=None):
|
|
93
|
+
if dst_path is None:
|
|
94
|
+
dst_path = src_path + ".tar"
|
|
95
|
+
with tar_open(dst_path, "w:xz") as fw:
|
|
96
|
+
fw.add(src_path, arcname=os.path.basename(src_path))
|
|
97
|
+
return dst_path
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def file_detar(src_path, dst_path=None):
|
|
101
|
+
if dst_path is None:
|
|
102
|
+
dst_path = os.path.dirname(src_path)
|
|
103
|
+
with tar_open(src_path, "r:xz") as fr:
|
|
104
|
+
fr.extractall(path=dst_path)
|
|
105
|
+
return os.path.join(dst_path, os.listdir(dst_path)[0])
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from tqdm import tqdm
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_size(path, recursive=False) -> int:
|
|
7
|
+
if not os.path.exists(path):
|
|
8
|
+
raise FileNotFoundError(f"Path not found: {path}")
|
|
9
|
+
if os.path.isfile(path):
|
|
10
|
+
return os.path.getsize(path)
|
|
11
|
+
size = 0
|
|
12
|
+
for filename in os.listdir(path):
|
|
13
|
+
path2 = os.path.join(path, filename)
|
|
14
|
+
if os.path.isfile(path2):
|
|
15
|
+
size += os.path.getsize(path2)
|
|
16
|
+
elif recursive:
|
|
17
|
+
size += get_size(path2, recursive=recursive)
|
|
18
|
+
return size
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def file_tqdm_bar(path, prefix="", total=None, ncols=120, recursive=False) -> tqdm:
|
|
22
|
+
prefix = f"{prefix}: " if prefix is not None and len(prefix) > 0 else ""
|
|
23
|
+
return tqdm(
|
|
24
|
+
total=total or get_size(path, recursive=recursive),
|
|
25
|
+
desc=f"{prefix}{os.path.basename(path)}"[:20],
|
|
26
|
+
ncols=ncols,
|
|
27
|
+
ascii=True,
|
|
28
|
+
unit="B",
|
|
29
|
+
unit_scale=True,
|
|
30
|
+
unit_divisor=1024,
|
|
31
|
+
)
|
nltfile/file/__init__.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import os.path
|
|
2
|
+
import pickle
|
|
3
|
+
import time
|
|
4
|
+
import traceback
|
|
5
|
+
from queue import Empty, Queue
|
|
6
|
+
from threading import Thread
|
|
7
|
+
|
|
8
|
+
from funlog import getLogger
|
|
9
|
+
|
|
10
|
+
logger = getLogger("nltfile")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ConcurrentWriteFile:
|
|
14
|
+
def __init__(self, filepath, mode="w", capacity=200, timeout=3):
|
|
15
|
+
self.filepath = filepath
|
|
16
|
+
self.mode = mode
|
|
17
|
+
self.timeout = timeout
|
|
18
|
+
self._write_queue = Queue(capacity)
|
|
19
|
+
self._close = False
|
|
20
|
+
self._writen_data = []
|
|
21
|
+
self._writen_size = 0
|
|
22
|
+
self._flush_size = 0
|
|
23
|
+
thread = Thread(target=self._write)
|
|
24
|
+
thread.start()
|
|
25
|
+
|
|
26
|
+
def write(self, chunk, offset=None):
|
|
27
|
+
self._write_queue.put((offset, chunk))
|
|
28
|
+
size = len(chunk)
|
|
29
|
+
self.curser_add(offset, size)
|
|
30
|
+
return size
|
|
31
|
+
|
|
32
|
+
def _write(self):
|
|
33
|
+
with open(self.filepath, self.mode) as fw:
|
|
34
|
+
while True:
|
|
35
|
+
try:
|
|
36
|
+
try:
|
|
37
|
+
offset, chunk = self._write_queue.get(timeout=self.timeout)
|
|
38
|
+
except Empty:
|
|
39
|
+
continue
|
|
40
|
+
if chunk is None:
|
|
41
|
+
self._write_queue.task_done()
|
|
42
|
+
continue
|
|
43
|
+
if offset is not None:
|
|
44
|
+
fw.seek(offset)
|
|
45
|
+
self._writen_size += fw.write(chunk)
|
|
46
|
+
self._write_queue.task_done()
|
|
47
|
+
if self._writen_size > self._flush_size + 50 * 1024 * 1024:
|
|
48
|
+
fw.flush()
|
|
49
|
+
self._flush_size = self._writen_size
|
|
50
|
+
self.curser_merge()
|
|
51
|
+
except Exception as e:
|
|
52
|
+
logger.error(f"write error: {e}:{traceback.format_exc()}")
|
|
53
|
+
if self._close:
|
|
54
|
+
break
|
|
55
|
+
fw.flush()
|
|
56
|
+
self._flush_size = self._writen_size
|
|
57
|
+
self.curser_merge()
|
|
58
|
+
|
|
59
|
+
def close(self):
|
|
60
|
+
self._close = True
|
|
61
|
+
|
|
62
|
+
def wait_for_all_done(self):
|
|
63
|
+
self._write_queue.join()
|
|
64
|
+
|
|
65
|
+
def empty(self):
|
|
66
|
+
return self._write_queue.empty()
|
|
67
|
+
|
|
68
|
+
def curser_add(self, offset, size):
|
|
69
|
+
if offset is None:
|
|
70
|
+
return
|
|
71
|
+
for record in self._writen_data:
|
|
72
|
+
if record[0] <= offset <= record[1]:
|
|
73
|
+
if record[1] < offset + size:
|
|
74
|
+
record[1] = offset + size
|
|
75
|
+
return
|
|
76
|
+
self._writen_data.append([offset, offset + size])
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
def _process_filepath(self):
|
|
80
|
+
return f"{self.filepath}.process"
|
|
81
|
+
|
|
82
|
+
def curser_merge(self):
|
|
83
|
+
self._writen_data.sort(key=lambda x: x[0])
|
|
84
|
+
merged = []
|
|
85
|
+
for interval in self._writen_data:
|
|
86
|
+
if not merged or interval[0] > merged[-1][1]:
|
|
87
|
+
merged.append(interval)
|
|
88
|
+
else:
|
|
89
|
+
merged[-1][1] = max(merged[-1][1], interval[1])
|
|
90
|
+
self._writen_data = merged
|
|
91
|
+
with open(self._process_filepath(), "wb") as fw:
|
|
92
|
+
pickle.dump(self._writen_data, fw)
|
|
93
|
+
|
|
94
|
+
def __enter__(self):
|
|
95
|
+
self._handle = self
|
|
96
|
+
if os.path.exists(self._process_filepath()):
|
|
97
|
+
with open(self._process_filepath(), "rb") as fr:
|
|
98
|
+
self._writen_data = pickle.load(fr)
|
|
99
|
+
return self._handle
|
|
100
|
+
|
|
101
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
102
|
+
while not self.empty():
|
|
103
|
+
time.sleep(1)
|
|
104
|
+
self.wait_for_all_done()
|
|
105
|
+
self.close()
|
|
106
|
+
if os.path.exists(self._process_filepath()):
|
|
107
|
+
os.remove(self._process_filepath())
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class ConcurrentFile(ConcurrentWriteFile):
|
|
112
|
+
def __init__(self, *args, **kwargs):
|
|
113
|
+
super(ConcurrentFile, self).__init__(*args, **kwargs)
|
nltfile/file/core.py
ADDED
nltfile/funos.py
ADDED
nltfile/pickle/core.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import pickle
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def dump(data, path):
|
|
5
|
+
with open(path, "wb") as fw:
|
|
6
|
+
pickle.dump(data, fw)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def load(path):
|
|
10
|
+
with open(path, "rb") as fr:
|
|
11
|
+
return pickle.load(fr)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def dumps(obj):
|
|
15
|
+
return pickle.dumps(obj)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def loads(obj):
|
|
19
|
+
return pickle.loads(obj)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .tqdm_bar import file_tqdm_bar
|
|
2
|
+
from .size import file_size
|
|
3
|
+
from .hash import file_hash, file_sha1, file_sha256, file_sha512, file_md5
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"file_tqdm_bar",
|
|
7
|
+
"file_size",
|
|
8
|
+
"file_hash",
|
|
9
|
+
"file_md5",
|
|
10
|
+
"file_sha1",
|
|
11
|
+
"file_sha512",
|
|
12
|
+
"file_sha256",
|
|
13
|
+
]
|
nltfile/utils/hash.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def file_hash(file_path, algorithm):
|
|
5
|
+
"""
|
|
6
|
+
计算文件的哈希值
|
|
7
|
+
:param file_path: 文件路径
|
|
8
|
+
:param algorithm: 哈希算法('md5', 'sha1', 'sha256', 'sha512')
|
|
9
|
+
:return: 文件的哈希值
|
|
10
|
+
"""
|
|
11
|
+
hash_func = hashlib.new(algorithm)
|
|
12
|
+
with open(file_path, "rb") as f:
|
|
13
|
+
chunk = f.read(8192)
|
|
14
|
+
while chunk:
|
|
15
|
+
hash_func.update(chunk)
|
|
16
|
+
chunk = f.read(8192)
|
|
17
|
+
return hash_func.hexdigest()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def file_md5(filepath):
|
|
21
|
+
return file_hash(filepath, "md5")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def file_sha1(filepath):
|
|
25
|
+
return file_hash(filepath, "sha1")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def file_sha256(filepath):
|
|
29
|
+
return file_hash(filepath, "sha256")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def file_sha512(filepath):
|
|
33
|
+
return file_hash(filepath, "sha512")
|
nltfile/utils/size.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def file_size(path, recursive=False) -> int:
|
|
5
|
+
if not os.path.exists(path):
|
|
6
|
+
raise FileNotFoundError(f"Path not found: {path}")
|
|
7
|
+
if os.path.isfile(path):
|
|
8
|
+
return os.path.getsize(path)
|
|
9
|
+
size = 0
|
|
10
|
+
for filename in os.listdir(path):
|
|
11
|
+
path2 = os.path.join(path, filename)
|
|
12
|
+
if os.path.isfile(path2):
|
|
13
|
+
size += os.path.getsize(path2)
|
|
14
|
+
elif recursive:
|
|
15
|
+
size += file_size(path2, recursive=recursive)
|
|
16
|
+
return size
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from tqdm import tqdm
|
|
4
|
+
from .size import file_size
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def file_tqdm_bar(path, prefix="", total=None, ncols=120, recursive=False) -> tqdm:
|
|
8
|
+
prefix = f"{prefix}: " if prefix is not None and len(prefix) > 0 else ""
|
|
9
|
+
return tqdm(
|
|
10
|
+
total=total or file_size(path, recursive=recursive),
|
|
11
|
+
desc=f"{prefix}{os.path.basename(path)}"[:20],
|
|
12
|
+
ncols=ncols,
|
|
13
|
+
ascii=True,
|
|
14
|
+
unit="B",
|
|
15
|
+
unit_scale=True,
|
|
16
|
+
unit_divisor=1024,
|
|
17
|
+
)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nltfile
|
|
3
|
+
Version: 1.0.28
|
|
4
|
+
Summary: nltfile
|
|
5
|
+
Author-email: 牛哥 <niuliangtao@qq.com>, farfarfun <farfarfun@qq.com>
|
|
6
|
+
Maintainer-email: 牛哥 <niuliangtao@qq.com>, farfarfun <farfarfun@qq.com>
|
|
7
|
+
Project-URL: Organization, https://github.com/farfarfun
|
|
8
|
+
Project-URL: Repository, https://github.com/farfarfun/nltfile
|
|
9
|
+
Project-URL: Releases, https://github.com/farfarfun/nltfile/releases
|
|
10
|
+
Requires-Python: >=3.7
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: funlog-tau>=1.0.0
|
|
13
|
+
Requires-Dist: tqdm>=4.66.5
|
|
14
|
+
|
|
15
|
+
# nltfile
|
|
16
|
+
|
|
17
|
+
一个实用的 Python 文件操作工具库,提供带进度条的压缩/解压、并发写入、pickle 序列化等功能的增强封装。
|
|
18
|
+
|
|
19
|
+
## 特性
|
|
20
|
+
|
|
21
|
+
- 带进度条的 tar/zip 压缩与解压,用法与标准库一致
|
|
22
|
+
- 线程安全的并发文件写入,支持断点续写
|
|
23
|
+
- pickle 序列化/反序列化的便捷封装
|
|
24
|
+
- 常用文件系统操作(创建目录、删除、复制)
|
|
25
|
+
|
|
26
|
+
## 安装
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install nltfile
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 依赖
|
|
33
|
+
|
|
34
|
+
- Python >= 3.7
|
|
35
|
+
- [funutil](https://pypi.org/project/funutil/) >= 1.0.15
|
|
36
|
+
- [tqdm](https://pypi.org/project/tqdm/) >= 4.66.5
|
|
37
|
+
|
|
38
|
+
## 使用
|
|
39
|
+
|
|
40
|
+
### tar 压缩/解压(带进度条)
|
|
41
|
+
|
|
42
|
+
用法与标准库 `tarfile` 一致,自动显示进度条:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from nltfile import tarfile
|
|
46
|
+
|
|
47
|
+
# 压缩
|
|
48
|
+
with tarfile.open("results.tar", "w|xz") as tar:
|
|
49
|
+
tar.add("a.txt")
|
|
50
|
+
|
|
51
|
+
# 解压
|
|
52
|
+
with tarfile.open("results.tar", "r|xz") as tar:
|
|
53
|
+
tar.extractall("local")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
也可以使用快捷函数:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from nltfile.compress.tarfile import file_entar, file_detar
|
|
60
|
+
|
|
61
|
+
# 一键压缩
|
|
62
|
+
file_entar("mydir", "mydir.tar")
|
|
63
|
+
|
|
64
|
+
# 一键解压
|
|
65
|
+
file_detar("mydir.tar", "output")
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### zip 解压
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from nltfile import zipfile
|
|
72
|
+
|
|
73
|
+
with zipfile.ZipFile("archive.zip") as zf:
|
|
74
|
+
zf.extractall("output")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 通用解压
|
|
78
|
+
|
|
79
|
+
根据文件后缀自动选择解压方式,支持 `.zip`、`.tar`、`.tar.gz`、`.tgz`、`.tar.bz2`、`.tar.xz`、`.txz`:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from nltfile.compress.allfile import extractall
|
|
83
|
+
|
|
84
|
+
extractall("archive.tar.gz", "output")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 并发文件写入
|
|
88
|
+
|
|
89
|
+
线程安全的异步写入,适用于多线程场景。支持指定偏移量写入,并在写入过程中自动记录进度,异常中断后可断点续写:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from nltfile import ConcurrentFile
|
|
93
|
+
|
|
94
|
+
with ConcurrentFile("output.txt", mode="w") as fw:
|
|
95
|
+
fw.write("hello, nltfile.")
|
|
96
|
+
fw.write("another line.")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
支持指定偏移量的随机写入:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
with ConcurrentFile("output.bin", mode="wb") as fw:
|
|
103
|
+
fw.write(b"chunk1", offset=0)
|
|
104
|
+
fw.write(b"chunk2", offset=1024)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### pickle 序列化
|
|
108
|
+
|
|
109
|
+
便捷的 pickle 读写封装:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from nltfile.pickle import dump, load, dumps, loads
|
|
113
|
+
|
|
114
|
+
# 写入文件
|
|
115
|
+
dump({"key": "value"}, "data.pkl")
|
|
116
|
+
|
|
117
|
+
# 从文件读取
|
|
118
|
+
data = load("data.pkl")
|
|
119
|
+
|
|
120
|
+
# 序列化为 bytes
|
|
121
|
+
raw = dumps({"key": "value"})
|
|
122
|
+
|
|
123
|
+
# 从 bytes 反序列化
|
|
124
|
+
obj = loads(raw)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 文件系统工具
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from nltfile.funos import makedirs, delete
|
|
131
|
+
|
|
132
|
+
# 递归创建目录(已存在不报错)
|
|
133
|
+
makedirs("path/to/dir")
|
|
134
|
+
|
|
135
|
+
# 递归删除目录
|
|
136
|
+
delete("path/to/dir")
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from nltfile.file import copy
|
|
141
|
+
|
|
142
|
+
# 复制文件
|
|
143
|
+
copy("src.txt", "dst.txt")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 获取文件/目录大小
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from nltfile import get_size
|
|
150
|
+
|
|
151
|
+
# 获取文件大小(字节)
|
|
152
|
+
size = get_size("large_file.bin")
|
|
153
|
+
|
|
154
|
+
# 递归获取目录大小
|
|
155
|
+
size = get_size("mydir", recursive=True)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## 项目结构
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
src/nltfile/
|
|
162
|
+
├── __init__.py # 顶层导出
|
|
163
|
+
├── funos.py # 文件系统工具(makedirs, delete)
|
|
164
|
+
├── file/
|
|
165
|
+
│ ├── core.py # 文件复制
|
|
166
|
+
│ └── concurrent.py # 并发文件写入
|
|
167
|
+
├── compress/
|
|
168
|
+
│ ├── utils.py # 进度条、文件大小计算
|
|
169
|
+
│ ├── tarfile.py # 带进度条的 tar 操作
|
|
170
|
+
│ ├── zipfile.py # zip 操作封装
|
|
171
|
+
│ └── allfile.py # 通用解压
|
|
172
|
+
└── pickle/
|
|
173
|
+
└── core.py # pickle 序列化封装
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## 许可证
|
|
177
|
+
|
|
178
|
+
[Apache License 2.0](LICENSE)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
nltfile/__init__.py,sha256=kAHph8CeliQQGUqHepjLhmlPJ3jVd7GS49ZRBcaBX8E,402
|
|
2
|
+
nltfile/funos.py,sha256=qQK2qJuMEwbUR1D2i6saY6uNQ-_To6dnuITTfMox5Lg,180
|
|
3
|
+
nltfile/compress/__init__.py,sha256=-vQPrErKZ5OWqmCepQ0a0mumkxiDL7jUMoMvQbpZxGo,148
|
|
4
|
+
nltfile/compress/allfile.py,sha256=TbH0Wp0U9RNIgtq_7Wb5ver2Yfrd3hlDz8U0WVvCzBc,439
|
|
5
|
+
nltfile/compress/tarfile.py,sha256=llS5Upid4s5hU0APCVVhh8iRTQ3NAETvciPHFwZ1_r8,3267
|
|
6
|
+
nltfile/compress/utils.py,sha256=D3O4-Ek90WKE8wBhVnQXdnW0DE7snzeAqYh3Q7eIwTw,919
|
|
7
|
+
nltfile/compress/zipfile.py,sha256=DZexJ-tv05FgM2tu-pjM2knevz5cK04iFsh8HMrjIcI,183
|
|
8
|
+
nltfile/file/__init__.py,sha256=j1z19wQkVnVvQsaP9OELFx4hquLFERWdFR3OF1rwA7g,100
|
|
9
|
+
nltfile/file/concurrent.py,sha256=UOiGp4PVdL9C3w3vf-vY4AEcuqIQZQ8cyvl4KijePLo,3642
|
|
10
|
+
nltfile/file/core.py,sha256=mTyS1VF5vTH22gjdea2WJ3qFu9Smc1MU8oNB8nkn6Yw,117
|
|
11
|
+
nltfile/pickle/__init__.py,sha256=s_T_2MuvRjlIsZGlRDmkSTjimljB32K588fPWrBbIgw,89
|
|
12
|
+
nltfile/pickle/core.py,sha256=PWmIfrk__OWaNn_7k1G65bb2oXPZMhMUZ0hU0vr3c34,277
|
|
13
|
+
nltfile/utils/__init__.py,sha256=JWIXl95pOFwRyH_T3hqy8xevdswH148wEhHNLwipJ0Q,280
|
|
14
|
+
nltfile/utils/hash.py,sha256=soV1QgOLth6oJ5R_YLH3JyLKKdDr8arnoX8KK31jU_4,738
|
|
15
|
+
nltfile/utils/size.py,sha256=ABgEbXYUhrxOcU1IagoFPvkoo41oPgw1dRn2w1ULVvY,486
|
|
16
|
+
nltfile/utils/tqdm_bar.py,sha256=QsicPBLB13y34e3kHyGME6vIWe_tTXJdtECVDLDlHpc,474
|
|
17
|
+
nltfile-1.0.28.dist-info/METADATA,sha256=u9vuPEv5nSyWbCzV6tjbpNEa55A2u5PSRVyYqYzmJv4,4023
|
|
18
|
+
nltfile-1.0.28.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
19
|
+
nltfile-1.0.28.dist-info/top_level.txt,sha256=HRKWMQx8AAhq80WKdtJAqks4xvFtiqWpHKa7qH0eT9k,8
|
|
20
|
+
nltfile-1.0.28.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nltfile
|