megfile 4.2.4__py3-none-any.whl → 5.0.0__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/__init__.py CHANGED
@@ -1,164 +1,17 @@
1
- from megfile.fs import (
2
- fs_abspath,
3
- fs_access,
4
- fs_cwd,
5
- fs_exists,
6
- fs_expanduser,
7
- fs_getmd5,
8
- fs_getmtime,
9
- fs_getsize,
10
- fs_glob,
11
- fs_glob_stat,
12
- fs_home,
13
- fs_iglob,
14
- fs_isabs,
15
- fs_isdir,
16
- fs_isfile,
17
- fs_islink,
18
- fs_ismount,
19
- fs_listdir,
20
- fs_load_from,
21
- fs_lstat,
22
- fs_makedirs,
23
- fs_move,
24
- fs_readlink,
25
- fs_realpath,
26
- fs_relpath,
27
- fs_remove,
28
- fs_rename,
29
- fs_resolve,
30
- fs_save_as,
31
- fs_scan,
32
- fs_scan_stat,
33
- fs_scandir,
34
- fs_stat,
35
- fs_symlink,
36
- fs_sync,
37
- fs_unlink,
38
- fs_walk,
39
- is_fs,
40
- )
41
- from megfile.fs_path import FSPath
42
- from megfile.hdfs import (
43
- hdfs_exists,
44
- hdfs_getmd5,
45
- hdfs_getmtime,
46
- hdfs_getsize,
47
- hdfs_glob,
48
- hdfs_glob_stat,
49
- hdfs_iglob,
50
- hdfs_isdir,
51
- hdfs_isfile,
52
- hdfs_listdir,
53
- hdfs_load_from,
54
- hdfs_makedirs,
55
- hdfs_move,
56
- hdfs_open,
57
- hdfs_remove,
58
- hdfs_save_as,
59
- hdfs_scan,
60
- hdfs_scan_stat,
61
- hdfs_scandir,
62
- hdfs_stat,
63
- hdfs_unlink,
64
- hdfs_walk,
65
- is_hdfs,
66
- )
67
- from megfile.hdfs_path import HdfsPath
68
- from megfile.http import (
69
- http_exists,
70
- http_getmtime,
71
- http_getsize,
72
- http_open,
73
- http_stat,
74
- is_http,
75
- )
76
- from megfile.http_path import HttpPath, HttpsPath
77
- from megfile.s3 import (
1
+ from megfile.fs_path import FSPath, is_fs
2
+ from megfile.hdfs_path import HdfsPath, is_hdfs
3
+ from megfile.http_path import HttpPath, HttpsPath, is_http
4
+ from megfile.s3_path import (
5
+ S3Path,
78
6
  is_s3,
79
- s3_access,
80
7
  s3_buffered_open,
81
8
  s3_cached_open,
82
- s3_concat,
83
- s3_copy,
84
- s3_download,
85
- s3_exists,
86
- s3_getmd5,
87
- s3_getmtime,
88
- s3_getsize,
89
- s3_glob,
90
- s3_glob_stat,
91
- s3_hasbucket,
92
- s3_iglob,
93
- s3_isdir,
94
- s3_isfile,
95
- s3_listdir,
96
- s3_load_content,
97
- s3_load_from,
98
- s3_lstat,
99
- s3_makedirs,
100
9
  s3_memory_open,
101
- s3_move,
102
- s3_open,
103
- s3_path_join,
104
10
  s3_pipe_open,
105
11
  s3_prefetch_open,
106
- s3_readlink,
107
- s3_remove,
108
- s3_rename,
109
- s3_save_as,
110
- s3_scan,
111
- s3_scan_stat,
112
- s3_scandir,
113
- s3_stat,
114
- s3_symlink,
115
- s3_sync,
116
- s3_unlink,
117
- s3_upload,
118
- s3_walk,
119
- )
120
- from megfile.s3_path import S3Path
121
- from megfile.sftp import (
122
- is_sftp,
123
- sftp_absolute,
124
- sftp_add_host_key,
125
- sftp_chmod,
126
- sftp_concat,
127
- sftp_copy,
128
- sftp_exists,
129
- sftp_getmd5,
130
- sftp_getmtime,
131
- sftp_getsize,
132
- sftp_glob,
133
- sftp_glob_stat,
134
- sftp_iglob,
135
- sftp_isdir,
136
- sftp_isfile,
137
- sftp_islink,
138
- sftp_listdir,
139
- sftp_load_from,
140
- sftp_lstat,
141
- sftp_makedirs,
142
- sftp_move,
143
- sftp_open,
144
- sftp_path_join,
145
- sftp_readlink,
146
- sftp_realpath,
147
- sftp_remove,
148
- sftp_rename,
149
- sftp_resolve,
150
- sftp_rmdir,
151
- sftp_save_as,
152
- sftp_scan,
153
- sftp_scan_stat,
154
- sftp_scandir,
155
- sftp_stat,
156
- sftp_symlink,
157
- sftp_sync,
158
- sftp_unlink,
159
- sftp_walk,
12
+ s3_share_cache_open,
160
13
  )
161
- from megfile.sftp_path import SftpPath
14
+ from megfile.sftp_path import SftpPath, is_sftp, sftp_add_host_key
162
15
  from megfile.smart import (
163
16
  smart_access,
164
17
  smart_cache,
@@ -202,8 +55,7 @@ from megfile.smart import (
202
55
  smart_walk,
203
56
  )
204
57
  from megfile.smart_path import SmartPath
205
- from megfile.stdio import is_stdio, stdio_open
206
- from megfile.stdio_path import StdioPath
58
+ from megfile.stdio_path import StdioPath, is_stdio
207
59
  from megfile.version import VERSION as __version__ # noqa: F401
208
60
 
209
61
  try:
@@ -211,6 +63,11 @@ try:
211
63
  except ImportError:
212
64
  Sftp2Path = None
213
65
 
66
+ try:
67
+ from megfile.webdav_path import WebdavPath, is_webdav
68
+ except ImportError:
69
+ WebdavPath = is_webdav = None
70
+
214
71
  __all__ = [
215
72
  "smart_access",
216
73
  "smart_cache",
@@ -254,153 +111,21 @@ __all__ = [
254
111
  "smart_lstat",
255
112
  "smart_concat",
256
113
  "is_s3",
257
- "s3_access",
258
114
  "s3_buffered_open",
259
115
  "s3_cached_open",
260
- "s3_copy",
261
- "s3_download",
262
- "s3_exists",
263
- "s3_getmd5",
264
- "s3_getmtime",
265
- "s3_getsize",
266
- "s3_glob_stat",
267
- "s3_glob",
268
- "s3_hasbucket",
269
- "s3_iglob",
270
- "s3_isdir",
271
- "s3_isfile",
272
- "s3_listdir",
273
- "s3_load_content",
274
- "s3_load_from",
275
- "s3_makedirs",
276
116
  "s3_memory_open",
277
- "s3_open",
278
- "s3_path_join",
279
117
  "s3_pipe_open",
280
118
  "s3_prefetch_open",
281
- "s3_remove",
282
- "s3_rename",
283
- "s3_move",
284
- "s3_sync",
285
- "s3_save_as",
286
- "s3_scan_stat",
287
- "s3_scan",
288
- "s3_scandir",
289
- "s3_stat",
290
- "s3_lstat",
291
- "s3_unlink",
292
- "s3_upload",
293
- "s3_walk",
294
- "s3_symlink",
295
- "s3_readlink",
296
- "s3_concat",
119
+ "s3_share_cache_open",
297
120
  "is_fs",
298
- "fs_abspath",
299
- "fs_access",
300
- "fs_exists",
301
- "fs_getmtime",
302
- "fs_getsize",
303
- "fs_glob_stat",
304
- "fs_glob",
305
- "fs_iglob",
306
- "fs_isabs",
307
- "fs_isdir",
308
- "fs_isfile",
309
- "fs_islink",
310
- "fs_ismount",
311
- "fs_listdir",
312
- "fs_load_from",
313
- "fs_makedirs",
314
- "fs_realpath",
315
- "fs_relpath",
316
- "fs_remove",
317
- "fs_rename",
318
- "fs_move",
319
- "fs_sync",
320
- "fs_save_as",
321
- "fs_scan_stat",
322
- "fs_scan",
323
- "fs_scandir",
324
- "fs_stat",
325
- "fs_lstat",
326
- "fs_unlink",
327
- "fs_walk",
328
- "fs_cwd",
329
- "fs_home",
330
- "fs_expanduser",
331
- "fs_resolve",
332
- "fs_getmd5",
333
- "fs_symlink",
334
- "fs_readlink",
335
121
  "is_http",
336
- "http_open",
337
- "http_stat",
338
- "http_getsize",
339
- "http_getmtime",
340
- "http_exists",
341
122
  "is_stdio",
342
123
  "stdio_open",
343
124
  "is_sftp",
344
- "sftp_readlink",
345
- "sftp_absolute",
346
- "sftp_glob",
347
- "sftp_iglob",
348
- "sftp_glob_stat",
349
- "sftp_resolve",
350
- "sftp_isdir",
351
- "sftp_exists",
352
- "sftp_scandir",
353
- "sftp_getmtime",
354
- "sftp_getsize",
355
- "sftp_isfile",
356
- "sftp_listdir",
357
- "sftp_load_from",
358
- "sftp_makedirs",
359
- "sftp_realpath",
360
- "sftp_rename",
361
- "sftp_move",
362
- "sftp_remove",
363
- "sftp_scan",
364
- "sftp_scan_stat",
365
- "sftp_stat",
366
- "sftp_lstat",
367
- "sftp_unlink",
368
- "sftp_walk",
369
- "sftp_path_join",
370
- "sftp_getmd5",
371
- "sftp_symlink",
372
- "sftp_islink",
373
- "sftp_save_as",
374
- "sftp_open",
375
- "sftp_chmod",
376
- "sftp_rmdir",
377
- "sftp_copy",
378
- "sftp_sync",
379
- "sftp_concat",
380
125
  "sftp_add_host_key",
381
126
  "is_hdfs",
382
- "hdfs_exists",
383
- "hdfs_stat",
384
- "hdfs_getmtime",
385
- "hdfs_getsize",
386
- "hdfs_isdir",
387
- "hdfs_isfile",
388
- "hdfs_listdir",
389
- "hdfs_load_from",
390
- "hdfs_move",
391
- "hdfs_remove",
392
- "hdfs_scan",
393
- "hdfs_scan_stat",
394
- "hdfs_scandir",
395
- "hdfs_unlink",
396
- "hdfs_walk",
397
- "hdfs_getmd5",
398
- "hdfs_save_as",
399
- "hdfs_open",
400
- "hdfs_glob",
401
- "hdfs_glob_stat",
402
- "hdfs_iglob",
403
- "hdfs_makedirs",
127
+ "is_webdav",
128
+ "WebdavPath",
404
129
  "S3Path",
405
130
  "FSPath",
406
131
  "HttpPath",
megfile/cli.py CHANGED
@@ -19,7 +19,7 @@ from megfile.hdfs_path import DEFAULT_HDFS_TIMEOUT
19
19
  from megfile.interfaces import FileEntry
20
20
  from megfile.lib.glob import get_non_glob_dir, has_magic
21
21
  from megfile.s3_path import get_s3_session
22
- from megfile.sftp import sftp_add_host_key
22
+ from megfile.sftp_path import sftp_add_host_key
23
23
  from megfile.smart import (
24
24
  _smart_sync_single_file,
25
25
  smart_copy,
@@ -86,33 +86,33 @@ def safe_cli(): # pragma: no cover
86
86
  sys.exit(1)
87
87
 
88
88
 
89
- def get_echo_path(file_stat, base_path: str = "", full_path: bool = False):
89
+ def get_echo_path(file_stat, base_path: str = "", full: bool = False):
90
90
  if base_path == file_stat.path:
91
91
  path = file_stat.name
92
- elif full_path:
92
+ elif full:
93
93
  path = file_stat.path
94
94
  else:
95
95
  path = smart_relpath(file_stat.path, start=base_path)
96
96
  return path
97
97
 
98
98
 
99
- def simple_echo(file_stat, base_path: str = "", full_path: bool = False):
100
- return get_echo_path(file_stat, base_path, full_path)
99
+ def simple_echo(file_stat, base_path: str = "", full: bool = False):
100
+ return get_echo_path(file_stat, base_path, full)
101
101
 
102
102
 
103
- def long_echo(file_stat, base_path: str = "", full_path: bool = False):
103
+ def long_echo(file_stat, base_path: str = "", full: bool = False):
104
104
  return "%12d %s %s" % (
105
105
  file_stat.stat.size,
106
106
  time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(file_stat.stat.mtime)),
107
- get_echo_path(file_stat, base_path, full_path),
107
+ get_echo_path(file_stat, base_path, full),
108
108
  )
109
109
 
110
110
 
111
- def human_echo(file_stat, base_path: str = "", full_path: bool = False):
111
+ def human_echo(file_stat, base_path: str = "", full: bool = False):
112
112
  return "%10s %s %s" % (
113
113
  get_human_size(file_stat.stat.size),
114
114
  time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(file_stat.stat.mtime)),
115
- get_echo_path(file_stat, base_path, full_path),
115
+ get_echo_path(file_stat, base_path, full),
116
116
  )
117
117
 
118
118
 
@@ -143,13 +143,12 @@ def _sftp_prompt_host_key(path):
143
143
  )
144
144
 
145
145
 
146
- def _ls(path: str, long: bool, recursive: bool, human_readable: bool):
146
+ def _ls(path: str, long: bool, full: bool, recursive: bool, human_readable: bool):
147
147
  base_path = path
148
- full_path = False
149
148
  if has_magic(path):
150
149
  scan_func = smart_glob_stat
151
150
  base_path = get_non_glob_dir(path)
152
- full_path = True
151
+ full = True
153
152
  elif recursive:
154
153
  scan_func = smart_scan_stat
155
154
  else:
@@ -170,8 +169,8 @@ def _ls(path: str, long: bool, recursive: bool, human_readable: bool):
170
169
  for file_stat in scan_func(path):
171
170
  total_size += file_stat.stat.size
172
171
  total_count += 1
173
- output = echo_func(file_stat, base_path, full_path=full_path)
174
- if file_stat.is_symlink():
172
+ output = echo_func(file_stat, base_path, full=full)
173
+ if long and file_stat.is_symlink():
175
174
  output += " -> %s" % smart_readlink(file_stat.path)
176
175
  click.echo(output)
177
176
  if long:
@@ -215,6 +214,12 @@ ZshComplete.source_template = ZshComplete.source_template.replace(
215
214
  is_flag=True,
216
215
  help="List all the objects in the path with size, modification time and path.",
217
216
  )
217
+ @click.option(
218
+ "-f",
219
+ "--full",
220
+ is_flag=True,
221
+ help="Displays the full path of each file.",
222
+ )
218
223
  @click.option(
219
224
  "-r",
220
225
  "--recursive",
@@ -228,12 +233,24 @@ ZshComplete.source_template = ZshComplete.source_template.replace(
228
233
  is_flag=True,
229
234
  help="Displays file sizes in human readable format.",
230
235
  )
231
- def ls(path: str, long: bool, recursive: bool, human_readable: bool):
232
- _ls(path, long=long, recursive=recursive, human_readable=human_readable)
236
+ def ls(path: str, long: bool, full: bool, recursive: bool, human_readable: bool):
237
+ _ls(
238
+ path,
239
+ long=long,
240
+ full=full,
241
+ recursive=recursive,
242
+ human_readable=human_readable,
243
+ )
233
244
 
234
245
 
235
246
  @cli.command(short_help="List all the objects in the path.")
236
247
  @click.argument("path", type=PathType())
248
+ @click.option(
249
+ "-f",
250
+ "--full",
251
+ is_flag=True,
252
+ help="Displays the full path of each file.",
253
+ )
237
254
  @click.option(
238
255
  "-r",
239
256
  "--recursive",
@@ -241,8 +258,8 @@ def ls(path: str, long: bool, recursive: bool, human_readable: bool):
241
258
  help="Command is performed on all files or objects under "
242
259
  "the specified directory or prefix.",
243
260
  )
244
- def ll(path: str, recursive: bool):
245
- _ls(path, long=True, recursive=recursive, human_readable=True)
261
+ def ll(path: str, recursive: bool, full: bool):
262
+ _ls(path, long=True, full=full, recursive=recursive, human_readable=True)
246
263
 
247
264
 
248
265
  @cli.command(short_help="Copy files from source to dest, skipping already copied.")
@@ -342,8 +359,8 @@ def mv(
342
359
  _sftp_prompt_host_key(dst_path)
343
360
 
344
361
  if progress_bar:
345
- src_protocol, _ = SmartPath._extract_protocol(src_path)
346
- dst_protocol, _ = SmartPath._extract_protocol(dst_path)
362
+ src_protocol = SmartPath._extract_protocol(src_path)
363
+ dst_protocol = SmartPath._extract_protocol(dst_path)
347
364
 
348
365
  if recursive:
349
366
  if src_protocol == dst_protocol:
megfile/config.py CHANGED
@@ -125,10 +125,19 @@ HDFS_MAX_RETRY_TIMES = int(
125
125
  SFTP_MAX_RETRY_TIMES = int(
126
126
  os.getenv("MEGFILE_SFTP_MAX_RETRY_TIMES") or DEFAULT_MAX_RETRY_TIMES
127
127
  )
128
+ WEBDAV_MAX_RETRY_TIMES = int(
129
+ os.getenv("MEGFILE_WEBDAV_MAX_RETRY_TIMES") or DEFAULT_MAX_RETRY_TIMES
130
+ )
128
131
 
129
132
  SFTP_HOST_KEY_POLICY = os.getenv("MEGFILE_SFTP_HOST_KEY_POLICY")
130
133
 
131
- HTTP_AUTH_HEADERS = ("Authorization", "Www-Authenticate", "Cookie", "Cookie2")
134
+ HTTP_AUTH_HEADERS = (
135
+ "Authorization",
136
+ "Www-Authenticate",
137
+ "Cookie",
138
+ "Cookie2",
139
+ "X-Amz-Security-Token",
140
+ )
132
141
 
133
142
  if os.getenv("MEGFILE_LOG_LEVEL"):
134
143
  set_log_level()
megfile/errors.py CHANGED
@@ -50,8 +50,7 @@ _logger = getLogger(__name__)
50
50
 
51
51
 
52
52
  def s3_endpoint_url(path: Optional[PathLike] = None):
53
- from megfile.s3 import get_endpoint_url, get_s3_client
54
- from megfile.s3_path import S3Path
53
+ from megfile.s3_path import S3Path, get_endpoint_url, get_s3_client
55
54
 
56
55
  profile_name = None
57
56
  if path:
@@ -137,6 +136,7 @@ s3_retry_error_codes = (
137
136
  "UploadTrafficRateLimitExceeded",
138
137
  "MetaOperationQpsLimitExceeded",
139
138
  "TotalQpsLimitExceeded",
139
+ "PartitionQpsLimitted",
140
140
  "ActiveRequestLimitExceeded",
141
141
  "CpuLimitExceeded",
142
142
  "QpsLimitExceeded",
megfile/fs_path.py CHANGED
@@ -17,6 +17,7 @@ from megfile.interfaces import (
17
17
  Access,
18
18
  ContextIterator,
19
19
  FileEntry,
20
+ FileLike,
20
21
  PathLike,
21
22
  StatResult,
22
23
  URIPath,
@@ -32,7 +33,7 @@ from megfile.utils import calculate_md5, copyfd
32
33
  __all__ = [
33
34
  "FSPath",
34
35
  "is_fs",
35
- "fs_path_join",
36
+ "fs_copy",
36
37
  ]
37
38
 
38
39
 
@@ -60,8 +61,37 @@ def is_fs(path: Union["PathLike", int]) -> bool:
60
61
  return scheme == "" or scheme == "file"
61
62
 
62
63
 
63
- def fs_path_join(path: PathLike, *other_paths: PathLike) -> str:
64
- return path_join(fspath(path), *map(fspath, other_paths))
64
+ def fs_copy(
65
+ src_path: PathLike,
66
+ dst_path: PathLike,
67
+ callback: Optional[Callable[[int], None]] = None,
68
+ followlinks: bool = False,
69
+ overwrite: bool = True,
70
+ ):
71
+ """File copy on file system
72
+ Copy content (excluding meta date) of file on `src_path` to `dst_path`.
73
+ `dst_path` must be a complete file name
74
+
75
+ .. note ::
76
+
77
+ The differences between this function and shutil.copyfile are:
78
+
79
+ 1. If parent directory of dst_path doesn't exist, create it
80
+
81
+ 2. Allow callback function, None by default.
82
+ callback: Optional[Callable[[int], None]], the int data is means
83
+ the size (in bytes) of the written data that is passed periodically
84
+
85
+ 3. This function is thread-unsafe
86
+
87
+ :param src_path: Given path
88
+ :param dst_path: Target file path
89
+ :param callback: Called periodically during copy, and the input parameter is
90
+ the data size (in bytes) of copy since the last call
91
+ :param followlinks: False if regard symlink as file, else True
92
+ :param overwrite: whether or not overwrite file when exists, default is True
93
+ """
94
+ return FSPath(src_path).copy(dst_path, callback, followlinks, overwrite)
65
95
 
66
96
 
67
97
  def _fs_rename_file(
@@ -85,6 +115,36 @@ def _fs_rename_file(
85
115
  shutil.move(src_path, dst_path)
86
116
 
87
117
 
118
+ class WrapAtomic(FileLike):
119
+ __atomic__ = True
120
+
121
+ def __init__(self, fileobj):
122
+ self.fileobj = fileobj
123
+ self.temp_name = f"{self.name}.temp"
124
+ os.rename(self.name, self.temp_name)
125
+
126
+ @property
127
+ def name(self):
128
+ return self.fileobj.name
129
+
130
+ @property
131
+ def mode(self):
132
+ return self.fileobj.mode
133
+
134
+ def _close(self):
135
+ self.fileobj.close()
136
+ os.rename(self.temp_name, self.name)
137
+
138
+ def _abort(self):
139
+ try:
140
+ os.unlink(self.temp_name)
141
+ except FileNotFoundError:
142
+ pass
143
+
144
+ def __getattr__(self, name: str):
145
+ return getattr(self.fileobj, name)
146
+
147
+
88
148
  @SmartPath.register
89
149
  class FSPath(URIPath):
90
150
  """file protocol
@@ -627,9 +687,11 @@ class FSPath(URIPath):
627
687
  """
628
688
  self._check_int_path()
629
689
 
630
- if missing_ok and not self.exists():
631
- return
632
- os.unlink(self.path_without_protocol) # pyre-ignore[6]
690
+ try:
691
+ os.unlink(self.path_without_protocol) # pyre-ignore[6]
692
+ except FileNotFoundError:
693
+ if not missing_ok:
694
+ raise
633
695
 
634
696
  def walk(
635
697
  self, followlinks: bool = False
@@ -917,11 +979,12 @@ class FSPath(URIPath):
917
979
  def open(
918
980
  self,
919
981
  mode: str = "r",
920
- buffering=-1,
921
- encoding=None,
922
- errors=None,
923
- newline=None,
924
- closefd=True,
982
+ buffering: int = -1,
983
+ encoding: Optional[str] = None,
984
+ errors: Optional[str] = None,
985
+ newline: Optional[str] = None,
986
+ closefd: bool = True,
987
+ atomic: bool = False,
925
988
  **kwargs,
926
989
  ) -> IO:
927
990
  if not isinstance(self.path_without_protocol, int) and (
@@ -932,7 +995,7 @@ class FSPath(URIPath):
932
995
  self.path_without_protocol # pyre-ignore[6]
933
996
  )
934
997
  ).mkdir(parents=True, exist_ok=True)
935
- return io.open(
998
+ fp = io.open(
936
999
  self.path_without_protocol,
937
1000
  mode,
938
1001
  buffering=buffering,
@@ -941,6 +1004,9 @@ class FSPath(URIPath):
941
1004
  newline=newline,
942
1005
  closefd=closefd,
943
1006
  )
1007
+ if atomic and ("w" in mode or "x" in mode or "a" in mode):
1008
+ return WrapAtomic(fp)
1009
+ return fp
944
1010
 
945
1011
  @cached_property
946
1012
  def parts(self) -> Tuple[str, ...]: