megfile 2.2.5a3__py3-none-any.whl → 2.2.6a1__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.
- megfile/cli.py +78 -57
- megfile/errors.py +1 -1
- megfile/lib/s3_buffered_writer.py +6 -2
- megfile/lib/s3_cached_handler.py +3 -1
- megfile/lib/s3_limited_seekable_writer.py +4 -2
- megfile/lib/s3_memory_handler.py +12 -2
- megfile/lib/s3_pipe_handler.py +6 -2
- megfile/lib/s3_prefetch_reader.py +6 -2
- megfile/lib/s3_share_cache_reader.py +3 -1
- megfile/s3_path.py +38 -11
- megfile/sftp_path.py +20 -0
- megfile/version.py +1 -1
- {megfile-2.2.5a3.dist-info → megfile-2.2.6a1.dist-info}/METADATA +1 -1
- {megfile-2.2.5a3.dist-info → megfile-2.2.6a1.dist-info}/RECORD +19 -19
- {megfile-2.2.5a3.dist-info → megfile-2.2.6a1.dist-info}/LICENSE +0 -0
- {megfile-2.2.5a3.dist-info → megfile-2.2.6a1.dist-info}/LICENSE.pyre +0 -0
- {megfile-2.2.5a3.dist-info → megfile-2.2.6a1.dist-info}/WHEEL +0 -0
- {megfile-2.2.5a3.dist-info → megfile-2.2.6a1.dist-info}/entry_points.txt +0 -0
- {megfile-2.2.5a3.dist-info → megfile-2.2.6a1.dist-info}/top_level.txt +0 -0
megfile/cli.py
CHANGED
|
@@ -4,13 +4,14 @@ import shutil
|
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
6
|
from concurrent.futures import ThreadPoolExecutor
|
|
7
|
+
from functools import partial
|
|
7
8
|
|
|
8
9
|
import click
|
|
9
10
|
from tqdm import tqdm
|
|
10
11
|
|
|
11
12
|
from megfile.interfaces import FileEntry
|
|
12
13
|
from megfile.lib.glob import get_non_glob_dir, has_magic
|
|
13
|
-
from megfile.smart import smart_copy, smart_getmd5, smart_getmtime, smart_getsize,
|
|
14
|
+
from megfile.smart import _smart_sync_single_file, smart_copy, smart_getmd5, smart_getmtime, smart_getsize, smart_glob_stat, smart_isdir, smart_isfile, smart_makedirs, smart_move, smart_open, smart_path_join, smart_remove, smart_rename, smart_scan_stat, smart_scandir, smart_stat, smart_sync, smart_sync_with_progress, smart_touch, smart_unlink
|
|
14
15
|
from megfile.smart_path import SmartPath
|
|
15
16
|
from megfile.utils import get_human_size
|
|
16
17
|
from megfile.version import VERSION
|
|
@@ -32,34 +33,36 @@ def safe_cli(): # pragma: no cover
|
|
|
32
33
|
click.echo(f"\n[{type(e).__name__}] {e}", err=True)
|
|
33
34
|
|
|
34
35
|
|
|
35
|
-
def get_echo_path(file_stat, base_path: str = ""):
|
|
36
|
+
def get_echo_path(file_stat, base_path: str = "", full_path: bool = False):
|
|
36
37
|
if base_path == file_stat.path:
|
|
37
38
|
path = file_stat.name
|
|
39
|
+
elif full_path:
|
|
40
|
+
path = file_stat.path
|
|
38
41
|
else:
|
|
39
42
|
path = os.path.relpath(file_stat.path, start=base_path)
|
|
40
43
|
return path
|
|
41
44
|
|
|
42
45
|
|
|
43
|
-
def simple_echo(file_stat, base_path: str = ""):
|
|
44
|
-
click.echo(get_echo_path(file_stat, base_path))
|
|
46
|
+
def simple_echo(file_stat, base_path: str = "", full_path: bool = False):
|
|
47
|
+
click.echo(get_echo_path(file_stat, base_path, full_path))
|
|
45
48
|
|
|
46
49
|
|
|
47
|
-
def long_echo(file_stat, base_path: str = ""):
|
|
50
|
+
def long_echo(file_stat, base_path: str = "", full_path: bool = False):
|
|
48
51
|
click.echo(
|
|
49
52
|
'%12d %s %s' % (
|
|
50
53
|
file_stat.stat.size,
|
|
51
54
|
time.strftime(
|
|
52
55
|
"%Y-%m-%d %H:%M:%S", time.localtime(file_stat.stat.mtime)),
|
|
53
|
-
get_echo_path(file_stat, base_path)))
|
|
56
|
+
get_echo_path(file_stat, base_path, full_path)))
|
|
54
57
|
|
|
55
58
|
|
|
56
|
-
def human_echo(file_stat, base_path: str = ""):
|
|
59
|
+
def human_echo(file_stat, base_path: str = "", full_path: bool = False):
|
|
57
60
|
click.echo(
|
|
58
61
|
'%10s %s %s' % (
|
|
59
62
|
get_human_size(file_stat.stat.size),
|
|
60
63
|
time.strftime(
|
|
61
64
|
"%Y-%m-%d %H:%M:%S", time.localtime(file_stat.stat.mtime)),
|
|
62
|
-
get_echo_path(file_stat, base_path)))
|
|
65
|
+
get_echo_path(file_stat, base_path, full_path)))
|
|
63
66
|
|
|
64
67
|
|
|
65
68
|
def smart_list_stat(path):
|
|
@@ -91,9 +94,11 @@ def smart_list_stat(path):
|
|
|
91
94
|
help='Displays file sizes in human readable format.')
|
|
92
95
|
def ls(path: str, long: bool, recursive: bool, human_readable: bool):
|
|
93
96
|
base_path = path
|
|
97
|
+
full_path = False
|
|
94
98
|
if has_magic(path):
|
|
95
99
|
scan_func = smart_glob_stat
|
|
96
100
|
base_path = get_non_glob_dir(path)
|
|
101
|
+
full_path = True
|
|
97
102
|
elif recursive:
|
|
98
103
|
scan_func = smart_scan_stat
|
|
99
104
|
else:
|
|
@@ -107,7 +112,7 @@ def ls(path: str, long: bool, recursive: bool, human_readable: bool):
|
|
|
107
112
|
echo_func = simple_echo
|
|
108
113
|
|
|
109
114
|
for file_stat in scan_func(path):
|
|
110
|
-
echo_func(file_stat, base_path)
|
|
115
|
+
echo_func(file_stat, base_path, full_path=full_path)
|
|
111
116
|
|
|
112
117
|
|
|
113
118
|
@cli.command(
|
|
@@ -264,65 +269,81 @@ def rm(path: str, recursive: bool):
|
|
|
264
269
|
'--force',
|
|
265
270
|
is_flag=True,
|
|
266
271
|
help='Copy files forcely, ignore same files.')
|
|
272
|
+
@click.option('-q', '--quiet', is_flag=True, help='Not show any progress log.')
|
|
267
273
|
def sync(
|
|
268
274
|
src_path: str, dst_path: str, progress_bar: bool, worker: int,
|
|
269
|
-
force: bool):
|
|
275
|
+
force: bool, quiet: bool):
|
|
270
276
|
with ThreadPoolExecutor(max_workers=worker) as executor:
|
|
271
277
|
if has_magic(src_path):
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
278
|
+
src_root_path = get_non_glob_dir(src_path)
|
|
279
|
+
|
|
280
|
+
def scan_func(path):
|
|
281
|
+
for glob_file_entry in smart_glob_stat(path):
|
|
282
|
+
if glob_file_entry.is_file():
|
|
283
|
+
yield glob_file_entry
|
|
284
|
+
else:
|
|
285
|
+
for file_entry in smart_scan_stat(glob_file_entry.path,
|
|
286
|
+
followlinks=True):
|
|
287
|
+
yield file_entry
|
|
288
|
+
else:
|
|
289
|
+
src_root_path = src_path
|
|
290
|
+
scan_func = partial(smart_scan_stat, followlinks=True)
|
|
291
|
+
|
|
292
|
+
if progress_bar and not quiet:
|
|
293
|
+
print('building progress bar', end='\r')
|
|
294
|
+
file_entries = []
|
|
295
|
+
total_count = total_size = 0
|
|
296
|
+
for total_count, file_entry in enumerate(scan_func(src_path),
|
|
297
|
+
start=1):
|
|
298
|
+
if total_count > 1024 * 128:
|
|
299
|
+
file_entries = []
|
|
300
|
+
else:
|
|
301
|
+
file_entries.append(file_entry)
|
|
302
|
+
total_size += file_entry.stat.size
|
|
303
|
+
print(
|
|
304
|
+
f'building progress bar, find {total_count} files',
|
|
305
|
+
end='\r')
|
|
306
|
+
|
|
307
|
+
if not file_entries:
|
|
308
|
+
file_entries = scan_func(src_path)
|
|
309
|
+
else:
|
|
310
|
+
total_count = total_size = None
|
|
311
|
+
file_entries = scan_func(src_path)
|
|
277
312
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
313
|
+
if quiet:
|
|
314
|
+
callback = callback_after_copy_file = None
|
|
315
|
+
else:
|
|
316
|
+
tbar = tqdm(total=total_count, ascii=True)
|
|
317
|
+
sbar = tqdm(
|
|
318
|
+
unit='B',
|
|
319
|
+
ascii=True,
|
|
320
|
+
unit_scale=True,
|
|
321
|
+
unit_divisor=1024,
|
|
322
|
+
total=total_size)
|
|
282
323
|
|
|
283
|
-
|
|
284
|
-
|
|
324
|
+
def callback(_filename: str, length: int):
|
|
325
|
+
sbar.update(length)
|
|
285
326
|
|
|
286
|
-
|
|
287
|
-
|
|
327
|
+
def callback_after_copy_file(src_file_path, dst_file_path):
|
|
328
|
+
tbar.update(1)
|
|
288
329
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
330
|
+
for file_entry in file_entries:
|
|
331
|
+
executor.submit(
|
|
332
|
+
_smart_sync_single_file,
|
|
333
|
+
dict(
|
|
334
|
+
src_root_path=src_root_path,
|
|
335
|
+
dst_root_path=dst_path,
|
|
336
|
+
src_file_path=file_entry.path,
|
|
292
337
|
callback=callback,
|
|
293
|
-
callback_after_copy_file=callback_after_copy_file,
|
|
294
|
-
src_file_stats=path_stats,
|
|
295
|
-
map_func=executor.map,
|
|
296
|
-
force=force,
|
|
297
338
|
followlinks=True,
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
tbar.close()
|
|
301
|
-
sbar.close()
|
|
302
|
-
else:
|
|
303
|
-
smart_sync(
|
|
304
|
-
root_dir,
|
|
305
|
-
dst_path,
|
|
306
|
-
src_file_stats=path_stats,
|
|
307
|
-
map_func=executor.map,
|
|
339
|
+
callback_after_copy_file=callback_after_copy_file,
|
|
308
340
|
force=force,
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
dst_path,
|
|
316
|
-
followlinks=True,
|
|
317
|
-
map_func=executor.map,
|
|
318
|
-
force=force)
|
|
319
|
-
else:
|
|
320
|
-
smart_sync(
|
|
321
|
-
src_path,
|
|
322
|
-
dst_path,
|
|
323
|
-
followlinks=True,
|
|
324
|
-
map_func=executor.map,
|
|
325
|
-
force=force)
|
|
341
|
+
))
|
|
342
|
+
if not quiet:
|
|
343
|
+
tbar.close()
|
|
344
|
+
if progress_bar:
|
|
345
|
+
sbar.update(sbar.total - sbar.n)
|
|
346
|
+
sbar.close()
|
|
326
347
|
|
|
327
348
|
|
|
328
349
|
@cli.command(short_help="Make the path if it doesn't already exist.")
|
megfile/errors.py
CHANGED
|
@@ -110,7 +110,7 @@ def s3_should_retry(error: Exception) -> bool:
|
|
|
110
110
|
if isinstance(error, botocore.exceptions.ClientError):
|
|
111
111
|
return client_error_code(error) in (
|
|
112
112
|
'500', '501', '502', '503', 'InternalError', 'ServiceUnavailable',
|
|
113
|
-
'SlowDown')
|
|
113
|
+
'SlowDown', 'ContextCanceled')
|
|
114
114
|
return False
|
|
115
115
|
|
|
116
116
|
|
|
@@ -52,11 +52,13 @@ class S3BufferedWriter(Writable):
|
|
|
52
52
|
block_size: int = DEFAULT_BLOCK_SIZE,
|
|
53
53
|
max_block_size: int = DEFAULT_MAX_BLOCK_SIZE,
|
|
54
54
|
max_buffer_size: int = DEFAULT_MAX_BUFFER_SIZE,
|
|
55
|
-
max_workers: Optional[int] = None
|
|
55
|
+
max_workers: Optional[int] = None,
|
|
56
|
+
profile_name: Optional[str] = None):
|
|
56
57
|
|
|
57
58
|
self._bucket = bucket
|
|
58
59
|
self._key = key
|
|
59
60
|
self._client = s3_client
|
|
61
|
+
self._profile_name = profile_name
|
|
60
62
|
|
|
61
63
|
self._block_size = block_size
|
|
62
64
|
self._max_block_size = max_block_size
|
|
@@ -86,7 +88,9 @@ class S3BufferedWriter(Writable):
|
|
|
86
88
|
|
|
87
89
|
@property
|
|
88
90
|
def name(self) -> str:
|
|
89
|
-
return 's3://%s/%s' % (
|
|
91
|
+
return 's3%s://%s/%s' % (
|
|
92
|
+
f"+{self._profile_name}" if self._profile_name else "",
|
|
93
|
+
self._bucket, self._key)
|
|
90
94
|
|
|
91
95
|
@property
|
|
92
96
|
def mode(self) -> str:
|
megfile/lib/s3_cached_handler.py
CHANGED
|
@@ -17,7 +17,8 @@ class S3CachedHandler(S3MemoryHandler):
|
|
|
17
17
|
*,
|
|
18
18
|
s3_client,
|
|
19
19
|
cache_path: Optional[str] = None,
|
|
20
|
-
remove_cache_when_open: bool = True
|
|
20
|
+
remove_cache_when_open: bool = True,
|
|
21
|
+
profile_name: Optional[str] = None):
|
|
21
22
|
|
|
22
23
|
assert mode in ('rb', 'wb', 'ab', 'rb+', 'wb+', 'ab+')
|
|
23
24
|
|
|
@@ -25,6 +26,7 @@ class S3CachedHandler(S3MemoryHandler):
|
|
|
25
26
|
self._key = key
|
|
26
27
|
self._mode = mode
|
|
27
28
|
self._client = s3_client
|
|
29
|
+
self._profile_name = profile_name
|
|
28
30
|
|
|
29
31
|
if cache_path is None:
|
|
30
32
|
cache_path = generate_cache_path(self.name)
|
|
@@ -29,7 +29,8 @@ class S3LimitedSeekableWriter(Seekable, S3BufferedWriter):
|
|
|
29
29
|
tail_block_size: Optional[int] = None,
|
|
30
30
|
max_block_size: int = DEFAULT_MAX_BLOCK_SIZE,
|
|
31
31
|
max_buffer_size: int = DEFAULT_MAX_BUFFER_SIZE,
|
|
32
|
-
max_workers: Optional[int] = None
|
|
32
|
+
max_workers: Optional[int] = None,
|
|
33
|
+
profile_name: Optional[str] = None):
|
|
33
34
|
|
|
34
35
|
super().__init__(
|
|
35
36
|
bucket,
|
|
@@ -38,7 +39,8 @@ class S3LimitedSeekableWriter(Seekable, S3BufferedWriter):
|
|
|
38
39
|
block_size=block_size,
|
|
39
40
|
max_block_size=max_block_size,
|
|
40
41
|
max_buffer_size=max_buffer_size,
|
|
41
|
-
max_workers=max_workers
|
|
42
|
+
max_workers=max_workers,
|
|
43
|
+
profile_name=profile_name)
|
|
42
44
|
|
|
43
45
|
self._head_block_size = head_block_size or block_size
|
|
44
46
|
self._tail_block_size = tail_block_size or block_size
|
megfile/lib/s3_memory_handler.py
CHANGED
|
@@ -8,7 +8,14 @@ from megfile.interfaces import Readable, Seekable, Writable
|
|
|
8
8
|
|
|
9
9
|
class S3MemoryHandler(Readable, Seekable, Writable):
|
|
10
10
|
|
|
11
|
-
def __init__(
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
bucket: str,
|
|
14
|
+
key: str,
|
|
15
|
+
mode: str,
|
|
16
|
+
*,
|
|
17
|
+
s3_client,
|
|
18
|
+
profile_name: Optional[str] = None):
|
|
12
19
|
|
|
13
20
|
assert mode in ('rb', 'wb', 'ab', 'rb+', 'wb+', 'ab+')
|
|
14
21
|
|
|
@@ -16,13 +23,16 @@ class S3MemoryHandler(Readable, Seekable, Writable):
|
|
|
16
23
|
self._key = key
|
|
17
24
|
self._mode = mode
|
|
18
25
|
self._client = s3_client
|
|
26
|
+
self._profile_name = profile_name
|
|
19
27
|
|
|
20
28
|
self._fileobj = BytesIO()
|
|
21
29
|
self._download_fileobj()
|
|
22
30
|
|
|
23
31
|
@property
|
|
24
32
|
def name(self) -> str:
|
|
25
|
-
return 's3://%s/%s' % (
|
|
33
|
+
return 's3%s://%s/%s' % (
|
|
34
|
+
f"+{self._profile_name}" if self._profile_name else "",
|
|
35
|
+
self._bucket, self._key)
|
|
26
36
|
|
|
27
37
|
@property
|
|
28
38
|
def mode(self) -> str:
|
megfile/lib/s3_pipe_handler.py
CHANGED
|
@@ -33,7 +33,8 @@ class S3PipeHandler(Readable, Writable):
|
|
|
33
33
|
mode: str,
|
|
34
34
|
*,
|
|
35
35
|
s3_client,
|
|
36
|
-
join_thread: bool = True
|
|
36
|
+
join_thread: bool = True,
|
|
37
|
+
profile_name: Optional[str] = None):
|
|
37
38
|
|
|
38
39
|
assert mode in ('rb', 'wb')
|
|
39
40
|
|
|
@@ -43,6 +44,7 @@ class S3PipeHandler(Readable, Writable):
|
|
|
43
44
|
self._client = s3_client
|
|
44
45
|
self._join_thread = join_thread
|
|
45
46
|
self._offset = 0
|
|
47
|
+
self._profile_name = profile_name
|
|
46
48
|
|
|
47
49
|
self._exc = None
|
|
48
50
|
self._pipe = os.pipe()
|
|
@@ -59,7 +61,9 @@ class S3PipeHandler(Readable, Writable):
|
|
|
59
61
|
|
|
60
62
|
@property
|
|
61
63
|
def name(self) -> str:
|
|
62
|
-
return 's3://%s/%s' % (
|
|
64
|
+
return 's3%s://%s/%s' % (
|
|
65
|
+
f"+{self._profile_name}" if self._profile_name else "",
|
|
66
|
+
self._bucket, self._key)
|
|
63
67
|
|
|
64
68
|
@property
|
|
65
69
|
def mode(self) -> str:
|
|
@@ -34,11 +34,13 @@ class S3PrefetchReader(BasePrefetchReader):
|
|
|
34
34
|
block_capacity: int = DEFAULT_BLOCK_CAPACITY,
|
|
35
35
|
block_forward: Optional[int] = None,
|
|
36
36
|
max_retries: int = 10,
|
|
37
|
-
max_workers: Optional[int] = None
|
|
37
|
+
max_workers: Optional[int] = None,
|
|
38
|
+
profile_name: Optional[str] = None):
|
|
38
39
|
|
|
39
40
|
self._bucket = bucket
|
|
40
41
|
self._key = key
|
|
41
42
|
self._client = s3_client
|
|
43
|
+
self._profile_name = profile_name
|
|
42
44
|
|
|
43
45
|
super().__init__(
|
|
44
46
|
block_size=block_size,
|
|
@@ -68,7 +70,9 @@ class S3PrefetchReader(BasePrefetchReader):
|
|
|
68
70
|
|
|
69
71
|
@property
|
|
70
72
|
def name(self) -> str:
|
|
71
|
-
return 's3://%s/%s' % (
|
|
73
|
+
return 's3%s://%s/%s' % (
|
|
74
|
+
f"+{self._profile_name}" if self._profile_name else "",
|
|
75
|
+
self._bucket, self._key)
|
|
72
76
|
|
|
73
77
|
def _fetch_response(
|
|
74
78
|
self, start: Optional[int] = None,
|
|
@@ -34,7 +34,8 @@ class S3ShareCacheReader(S3PrefetchReader):
|
|
|
34
34
|
block_forward: Optional[int] = None,
|
|
35
35
|
max_retries: int = 10,
|
|
36
36
|
cache_key: str = 'lru',
|
|
37
|
-
max_workers: Optional[int] = None
|
|
37
|
+
max_workers: Optional[int] = None,
|
|
38
|
+
profile_name: Optional[str] = None):
|
|
38
39
|
|
|
39
40
|
self._cache_key = cache_key
|
|
40
41
|
|
|
@@ -47,6 +48,7 @@ class S3ShareCacheReader(S3PrefetchReader):
|
|
|
47
48
|
block_forward=block_forward,
|
|
48
49
|
max_retries=max_retries,
|
|
49
50
|
max_workers=max_workers,
|
|
51
|
+
profile_name=profile_name,
|
|
50
52
|
)
|
|
51
53
|
|
|
52
54
|
def _get_block_forward(
|
megfile/s3_path.py
CHANGED
|
@@ -598,7 +598,8 @@ def s3_prefetch_open(
|
|
|
598
598
|
s3_client=client,
|
|
599
599
|
max_retries=max_retries,
|
|
600
600
|
max_workers=max_concurrency,
|
|
601
|
-
block_size=max_block_size
|
|
601
|
+
block_size=max_block_size,
|
|
602
|
+
profile_name=s3_url._profile_name)
|
|
602
603
|
|
|
603
604
|
|
|
604
605
|
@_s3_binary_mode
|
|
@@ -649,7 +650,8 @@ def s3_share_cache_open(
|
|
|
649
650
|
s3_client=client,
|
|
650
651
|
max_retries=max_retries,
|
|
651
652
|
max_workers=max_concurrency,
|
|
652
|
-
block_size=max_block_size
|
|
653
|
+
block_size=max_block_size,
|
|
654
|
+
profile_name=s3_url._profile_name)
|
|
653
655
|
|
|
654
656
|
|
|
655
657
|
@_s3_binary_mode
|
|
@@ -696,7 +698,12 @@ def s3_pipe_open(
|
|
|
696
698
|
cache_key='s3_filelike_client',
|
|
697
699
|
profile_name=s3_url._profile_name)
|
|
698
700
|
return S3PipeHandler(
|
|
699
|
-
bucket,
|
|
701
|
+
bucket,
|
|
702
|
+
key,
|
|
703
|
+
mode,
|
|
704
|
+
s3_client=client,
|
|
705
|
+
join_thread=join_thread,
|
|
706
|
+
profile_name=s3_url._profile_name)
|
|
700
707
|
|
|
701
708
|
|
|
702
709
|
@_s3_binary_mode
|
|
@@ -737,7 +744,12 @@ def s3_cached_open(
|
|
|
737
744
|
cache_key='s3_filelike_client',
|
|
738
745
|
profile_name=s3_url._profile_name)
|
|
739
746
|
return S3CachedHandler(
|
|
740
|
-
bucket,
|
|
747
|
+
bucket,
|
|
748
|
+
key,
|
|
749
|
+
mode,
|
|
750
|
+
s3_client=client,
|
|
751
|
+
cache_path=cache_path,
|
|
752
|
+
profile_name=s3_url._profile_name)
|
|
741
753
|
|
|
742
754
|
|
|
743
755
|
@_s3_binary_mode
|
|
@@ -792,9 +804,19 @@ def s3_buffered_open(
|
|
|
792
804
|
|
|
793
805
|
if 'a' in mode or '+' in mode:
|
|
794
806
|
if cache_path is None:
|
|
795
|
-
return S3MemoryHandler(
|
|
807
|
+
return S3MemoryHandler(
|
|
808
|
+
bucket,
|
|
809
|
+
key,
|
|
810
|
+
mode,
|
|
811
|
+
s3_client=client,
|
|
812
|
+
profile_name=s3_url._profile_name)
|
|
796
813
|
return S3CachedHandler(
|
|
797
|
-
bucket,
|
|
814
|
+
bucket,
|
|
815
|
+
key,
|
|
816
|
+
mode,
|
|
817
|
+
s3_client=client,
|
|
818
|
+
cache_path=cache_path,
|
|
819
|
+
profile_name=s3_url._profile_name)
|
|
798
820
|
|
|
799
821
|
if mode == 'rb':
|
|
800
822
|
# A rough conversion algorithm to align 2 types of Reader / Writer parameters
|
|
@@ -813,7 +835,8 @@ def s3_buffered_open(
|
|
|
813
835
|
max_retries=max_retries,
|
|
814
836
|
max_workers=max_concurrency,
|
|
815
837
|
block_size=block_size,
|
|
816
|
-
block_forward=block_forward
|
|
838
|
+
block_forward=block_forward,
|
|
839
|
+
profile_name=s3_url._profile_name)
|
|
817
840
|
else:
|
|
818
841
|
reader = S3PrefetchReader(
|
|
819
842
|
bucket,
|
|
@@ -823,7 +846,8 @@ def s3_buffered_open(
|
|
|
823
846
|
max_workers=max_concurrency,
|
|
824
847
|
block_capacity=block_capacity,
|
|
825
848
|
block_forward=block_forward,
|
|
826
|
-
block_size=block_size
|
|
849
|
+
block_size=block_size,
|
|
850
|
+
profile_name=s3_url._profile_name)
|
|
827
851
|
if buffered:
|
|
828
852
|
reader = io.BufferedReader(reader) # pytype: disable=wrong-arg-types
|
|
829
853
|
return reader
|
|
@@ -835,7 +859,8 @@ def s3_buffered_open(
|
|
|
835
859
|
s3_client=client,
|
|
836
860
|
max_workers=max_concurrency,
|
|
837
861
|
max_buffer_size=max_buffer_size,
|
|
838
|
-
block_size=block_size
|
|
862
|
+
block_size=block_size,
|
|
863
|
+
profile_name=s3_url._profile_name)
|
|
839
864
|
else:
|
|
840
865
|
writer = S3BufferedWriter(
|
|
841
866
|
bucket,
|
|
@@ -843,7 +868,8 @@ def s3_buffered_open(
|
|
|
843
868
|
s3_client=client,
|
|
844
869
|
max_workers=max_concurrency,
|
|
845
870
|
max_buffer_size=max_buffer_size,
|
|
846
|
-
block_size=block_size
|
|
871
|
+
block_size=block_size,
|
|
872
|
+
profile_name=s3_url._profile_name)
|
|
847
873
|
if buffered:
|
|
848
874
|
writer = io.BufferedWriter(writer) # pytype: disable=wrong-arg-types
|
|
849
875
|
return writer
|
|
@@ -880,7 +906,8 @@ def s3_memory_open(
|
|
|
880
906
|
config=config,
|
|
881
907
|
cache_key='s3_filelike_client',
|
|
882
908
|
profile_name=s3_url._profile_name)
|
|
883
|
-
return S3MemoryHandler(
|
|
909
|
+
return S3MemoryHandler(
|
|
910
|
+
bucket, key, mode, s3_client=client, profile_name=s3_url._profile_name)
|
|
884
911
|
|
|
885
912
|
|
|
886
913
|
s3_open = s3_buffered_open
|
megfile/sftp_path.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import atexit
|
|
2
|
+
import fcntl
|
|
2
3
|
import hashlib
|
|
3
4
|
import io
|
|
4
5
|
import os
|
|
6
|
+
import random
|
|
5
7
|
import shlex
|
|
6
8
|
import socket
|
|
7
9
|
import subprocess
|
|
@@ -43,6 +45,7 @@ SFTP_PASSWORD = "SFTP_PASSWORD"
|
|
|
43
45
|
SFTP_PRIVATE_KEY_PATH = "SFTP_PRIVATE_KEY_PATH"
|
|
44
46
|
SFTP_PRIVATE_KEY_TYPE = "SFTP_PRIVATE_KEY_TYPE"
|
|
45
47
|
SFTP_PRIVATE_KEY_PASSWORD = "SFTP_PRIVATE_KEY_PASSWORD"
|
|
48
|
+
SFTP_MAX_UNAUTH_CONN = "SFTP_MAX_UNAUTH_CONN"
|
|
46
49
|
MAX_RETRIES = 10
|
|
47
50
|
DEFAULT_SSH_CONNECT_TIMEOUT = 5
|
|
48
51
|
DEFAULT_SSH_KEEPALIVE_INTERVAL = 15
|
|
@@ -194,6 +197,20 @@ def _get_ssh_client(
|
|
|
194
197
|
|
|
195
198
|
ssh_client = paramiko.SSHClient()
|
|
196
199
|
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
200
|
+
max_unauth_connections = int(os.getenv(SFTP_MAX_UNAUTH_CONN, 10))
|
|
201
|
+
try:
|
|
202
|
+
fd = os.open(
|
|
203
|
+
os.path.join(
|
|
204
|
+
'/tmp',
|
|
205
|
+
f'megfile-sftp-{hostname}-{random.randint(1, max_unauth_connections)}'
|
|
206
|
+
), os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
|
|
207
|
+
except Exception:
|
|
208
|
+
_logger.warning(
|
|
209
|
+
"Can't create file lock in '/tmp', please control the SFTP concurrency count by yourself."
|
|
210
|
+
)
|
|
211
|
+
fd = None
|
|
212
|
+
if fd:
|
|
213
|
+
fcntl.flock(fd, fcntl.LOCK_EX)
|
|
197
214
|
ssh_client.connect(
|
|
198
215
|
hostname=hostname,
|
|
199
216
|
username=username,
|
|
@@ -203,6 +220,9 @@ def _get_ssh_client(
|
|
|
203
220
|
auth_timeout=DEFAULT_SSH_CONNECT_TIMEOUT,
|
|
204
221
|
banner_timeout=DEFAULT_SSH_CONNECT_TIMEOUT,
|
|
205
222
|
)
|
|
223
|
+
if fd:
|
|
224
|
+
fcntl.flock(fd, fcntl.LOCK_UN)
|
|
225
|
+
os.close(fd)
|
|
206
226
|
atexit.register(ssh_client.close)
|
|
207
227
|
return ssh_client
|
|
208
228
|
|
megfile/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = "2.2.
|
|
1
|
+
VERSION = "2.2.6a1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
megfile/__init__.py,sha256=Qsi3XNP_0XYoSol-1AGutZqo0rfBnzaiZ-HVXll4fB0,5721
|
|
2
|
-
megfile/cli.py,sha256=
|
|
3
|
-
megfile/errors.py,sha256=
|
|
2
|
+
megfile/cli.py,sha256=m6nqGc_vjA68SMv0NsqM-R5RpnZNczA4JI4Gt_mcFPw,14743
|
|
3
|
+
megfile/errors.py,sha256=IDqfdz0WjCwvV1Zp8N9iKFE8Y0nrYBH6c7_4EW_CJaM,11953
|
|
4
4
|
megfile/fs.py,sha256=LtrzQsyZgogTJeoRFz4L52gxx0jByzRBLkpWYpvkp5I,11819
|
|
5
5
|
megfile/fs_path.py,sha256=JkY8qGIIboK5MK2rSagYEvnu5FTzmk9OHXIhTO7BjeY,38767
|
|
6
6
|
megfile/http.py,sha256=a3oAuARSSaIU8VMx86Mui0N5Vh-EI0AoHnwxRU5DSMU,2032
|
|
@@ -8,14 +8,14 @@ megfile/http_path.py,sha256=9X9klm6CtEfu4V0_LUWtSXC8JRYiNjVAJjdzEBfxUa8,9272
|
|
|
8
8
|
megfile/interfaces.py,sha256=h3tWE8hVt5S-HopaMAX6lunPJ97vzhv6jH_2HubcDNc,6219
|
|
9
9
|
megfile/pathlike.py,sha256=WpP8zWSOAcAfYrD65hZS08UEi4_iCoEMs2xvfFMwZvY,29264
|
|
10
10
|
megfile/s3.py,sha256=7XZSWjcSY-hoLhLH9dtfyRpokfYH9raTO_Mf69RjpEs,12560
|
|
11
|
-
megfile/s3_path.py,sha256=
|
|
11
|
+
megfile/s3_path.py,sha256=1bxhf8-Lg29oFB6BFbIQGDvKvSIE7ZS-YORHbe6kOiM,88409
|
|
12
12
|
megfile/sftp.py,sha256=CZYv1WKL0d_vuJ5aPgMhm0W8uskzaO5zbYGhGwt_mQs,12771
|
|
13
|
-
megfile/sftp_path.py,sha256=
|
|
13
|
+
megfile/sftp_path.py,sha256=DG-X79bcPc1hikZ_PNha3K_ber8bqjx-KnW3jZG8iCw,50529
|
|
14
14
|
megfile/smart.py,sha256=JH5zed90eQchS8GNQ7mbXvif-pNKjPjvnadMazKfQMs,33278
|
|
15
15
|
megfile/smart_path.py,sha256=Y0UFh4J2ccydRY2W-wX2ubaf9zzJx1M2nf-VLBGe4mk,6749
|
|
16
16
|
megfile/stdio.py,sha256=yRhlfUA2DHi3bq-9cXsSlbLCnHvS_zvglO2IYYyPsGc,707
|
|
17
17
|
megfile/stdio_path.py,sha256=eQulTXUwHvUKA-5PKCGfVNiEPkJhG9YtVhtU58OcmoM,2873
|
|
18
|
-
megfile/version.py,sha256=
|
|
18
|
+
megfile/version.py,sha256=s1UlNdCKmZ0EggJzQ2Wu1Nz0xneCzoLAvSAoUlQHunY,21
|
|
19
19
|
megfile/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
megfile/lib/base_prefetch_reader.py,sha256=SjrBffHVgvJnYtr8HNqiOozP9OJRYS37Eu1KQcZu1Z8,13221
|
|
21
21
|
megfile/lib/combine_reader.py,sha256=XFSqEY5A5X5Uf7eQ6AXAzrvNteESSXvKNVPktGjo3KY,4546
|
|
@@ -26,22 +26,22 @@ megfile/lib/glob.py,sha256=7i9dIput9rI9JIPyTZX-JDmFS7IP_THlX1k-35foAfw,9732
|
|
|
26
26
|
megfile/lib/http_prefetch_reader.py,sha256=YDtQXRX-yxyaFzqI_CL3X73-Idkdz1aPIDL29uY77zw,3326
|
|
27
27
|
megfile/lib/joinpath.py,sha256=D4Px6-lnDDpYs1LMUHkTIGqMPJQ0oCBGfTzREs373iU,929
|
|
28
28
|
megfile/lib/lazy_handler.py,sha256=f1rip2_T57vVo0WRNXve2bAa4LArvVheMfQg1S0vFzg,1915
|
|
29
|
-
megfile/lib/s3_buffered_writer.py,sha256=
|
|
30
|
-
megfile/lib/s3_cached_handler.py,sha256=
|
|
31
|
-
megfile/lib/s3_limited_seekable_writer.py,sha256=
|
|
32
|
-
megfile/lib/s3_memory_handler.py,sha256=
|
|
33
|
-
megfile/lib/s3_pipe_handler.py,sha256=
|
|
34
|
-
megfile/lib/s3_prefetch_reader.py,sha256=
|
|
35
|
-
megfile/lib/s3_share_cache_reader.py,sha256=
|
|
29
|
+
megfile/lib/s3_buffered_writer.py,sha256=9wiOXY7MgHrP7ulrN-QE2GcfwTfsTBniMxL9iDulpFw,7107
|
|
30
|
+
megfile/lib/s3_cached_handler.py,sha256=xuWiThi6pJtGL_ErSBmcu8rDv1XyXNmEhiFBnRF4NWU,1412
|
|
31
|
+
megfile/lib/s3_limited_seekable_writer.py,sha256=Dz4e5Gu-b9BoIzGHh5AAEkIGGUEB9Hg7MDToHS_05gk,6124
|
|
32
|
+
megfile/lib/s3_memory_handler.py,sha256=6Tj89xzc8z-FycVggGpjF_8lEbPsqRVB6undZwWsugo,3971
|
|
33
|
+
megfile/lib/s3_pipe_handler.py,sha256=hG8sEajO9dv9bLTeXsERxDioHHhzi4t8NC61lSbYk94,3557
|
|
34
|
+
megfile/lib/s3_prefetch_reader.py,sha256=vRZfFSV7bNliBrdD3cxgUrYSzLrgPPrzBT8zJMeAB8Q,4223
|
|
35
|
+
megfile/lib/s3_share_cache_reader.py,sha256=bPnD82lqrXtdLMegxyY15MzbWUHXuX0KKGtJHNOWD-w,3860
|
|
36
36
|
megfile/lib/shadow_handler.py,sha256=IbFyTw107t-yWH0cGrDjAJX-CS3xeEr77_PTGsnSgk4,2683
|
|
37
37
|
megfile/lib/stdio_handler.py,sha256=QDWtcZxz-hzi-rqQUiSlR3NrihX1fjK_Rj9T2mdTFEg,2044
|
|
38
38
|
megfile/lib/url.py,sha256=VbQLjo0s4AaV0iSk66BcjI68aUTcN9zBZ5x6-cM4Qvs,103
|
|
39
39
|
megfile/utils/__init__.py,sha256=qdX8FF_dYFKwp1BIWx3JeSGd91s7AKUDSEpDv9tORcM,9162
|
|
40
40
|
megfile/utils/mutex.py,sha256=-2KH3bNovKRd9zvsXq9n3bWM7rQdoG9hO7tUPxVG_Po,2538
|
|
41
|
-
megfile-2.2.
|
|
42
|
-
megfile-2.2.
|
|
43
|
-
megfile-2.2.
|
|
44
|
-
megfile-2.2.
|
|
45
|
-
megfile-2.2.
|
|
46
|
-
megfile-2.2.
|
|
47
|
-
megfile-2.2.
|
|
41
|
+
megfile-2.2.6a1.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
42
|
+
megfile-2.2.6a1.dist-info/LICENSE.pyre,sha256=9lf5nT-5ZH25JijpYAequ0bl8E8z5JmZB1qrjiUMp84,1080
|
|
43
|
+
megfile-2.2.6a1.dist-info/METADATA,sha256=rDzvBg6_knLWNEyWEX5k_afGpOcGkjNDk3XQoho_O7g,10744
|
|
44
|
+
megfile-2.2.6a1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
45
|
+
megfile-2.2.6a1.dist-info/entry_points.txt,sha256=M6ZWSSv5_5_QtIpZafy3vq7WuOJ_5dSGQQnEZbByt2Q,49
|
|
46
|
+
megfile-2.2.6a1.dist-info/top_level.txt,sha256=i3rMgdU1ZAJekAceojhA-bkm3749PzshtRmLTbeLUPQ,8
|
|
47
|
+
megfile-2.2.6a1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|