megfile 4.2.5__py3-none-any.whl → 5.0.1__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,19 @@
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
+ import megfile.config # noqa: F401 # make sure env config is loaded
2
+ from megfile.fs_path import FSPath, is_fs
3
+ from megfile.hdfs_path import HdfsPath, is_hdfs
4
+ from megfile.http_path import HttpPath, HttpsPath, is_http
5
+ from megfile.s3_path import (
6
+ S3Path,
78
7
  is_s3,
79
- s3_access,
80
8
  s3_buffered_open,
81
9
  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
10
  s3_memory_open,
101
- s3_move,
102
11
  s3_open,
103
- s3_path_join,
104
12
  s3_pipe_open,
105
13
  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,
14
+ s3_share_cache_open,
160
15
  )
161
- from megfile.sftp_path import SftpPath
16
+ from megfile.sftp_path import SftpPath, is_sftp, sftp_add_host_key
162
17
  from megfile.smart import (
163
18
  smart_access,
164
19
  smart_cache,
@@ -202,8 +57,7 @@ from megfile.smart import (
202
57
  smart_walk,
203
58
  )
204
59
  from megfile.smart_path import SmartPath
205
- from megfile.stdio import is_stdio, stdio_open
206
- from megfile.stdio_path import StdioPath
60
+ from megfile.stdio_path import StdioPath, is_stdio
207
61
  from megfile.version import VERSION as __version__ # noqa: F401
208
62
 
209
63
  try:
@@ -212,9 +66,9 @@ except ImportError:
212
66
  Sftp2Path = None
213
67
 
214
68
  try:
215
- from megfile.webdav_path import WebdavPath
69
+ from megfile.webdav_path import WebdavPath, is_webdav
216
70
  except ImportError:
217
- WebdavPath = None
71
+ WebdavPath = is_webdav = None
218
72
 
219
73
  __all__ = [
220
74
  "smart_access",
@@ -259,153 +113,22 @@ __all__ = [
259
113
  "smart_lstat",
260
114
  "smart_concat",
261
115
  "is_s3",
262
- "s3_access",
263
116
  "s3_buffered_open",
264
117
  "s3_cached_open",
265
- "s3_copy",
266
- "s3_download",
267
- "s3_exists",
268
- "s3_getmd5",
269
- "s3_getmtime",
270
- "s3_getsize",
271
- "s3_glob_stat",
272
- "s3_glob",
273
- "s3_hasbucket",
274
- "s3_iglob",
275
- "s3_isdir",
276
- "s3_isfile",
277
- "s3_listdir",
278
- "s3_load_content",
279
- "s3_load_from",
280
- "s3_makedirs",
281
118
  "s3_memory_open",
282
119
  "s3_open",
283
- "s3_path_join",
284
120
  "s3_pipe_open",
285
121
  "s3_prefetch_open",
286
- "s3_remove",
287
- "s3_rename",
288
- "s3_move",
289
- "s3_sync",
290
- "s3_save_as",
291
- "s3_scan_stat",
292
- "s3_scan",
293
- "s3_scandir",
294
- "s3_stat",
295
- "s3_lstat",
296
- "s3_unlink",
297
- "s3_upload",
298
- "s3_walk",
299
- "s3_symlink",
300
- "s3_readlink",
301
- "s3_concat",
122
+ "s3_share_cache_open",
302
123
  "is_fs",
303
- "fs_abspath",
304
- "fs_access",
305
- "fs_exists",
306
- "fs_getmtime",
307
- "fs_getsize",
308
- "fs_glob_stat",
309
- "fs_glob",
310
- "fs_iglob",
311
- "fs_isabs",
312
- "fs_isdir",
313
- "fs_isfile",
314
- "fs_islink",
315
- "fs_ismount",
316
- "fs_listdir",
317
- "fs_load_from",
318
- "fs_makedirs",
319
- "fs_realpath",
320
- "fs_relpath",
321
- "fs_remove",
322
- "fs_rename",
323
- "fs_move",
324
- "fs_sync",
325
- "fs_save_as",
326
- "fs_scan_stat",
327
- "fs_scan",
328
- "fs_scandir",
329
- "fs_stat",
330
- "fs_lstat",
331
- "fs_unlink",
332
- "fs_walk",
333
- "fs_cwd",
334
- "fs_home",
335
- "fs_expanduser",
336
- "fs_resolve",
337
- "fs_getmd5",
338
- "fs_symlink",
339
- "fs_readlink",
340
124
  "is_http",
341
- "http_open",
342
- "http_stat",
343
- "http_getsize",
344
- "http_getmtime",
345
- "http_exists",
346
125
  "is_stdio",
347
126
  "stdio_open",
348
127
  "is_sftp",
349
- "sftp_readlink",
350
- "sftp_absolute",
351
- "sftp_glob",
352
- "sftp_iglob",
353
- "sftp_glob_stat",
354
- "sftp_resolve",
355
- "sftp_isdir",
356
- "sftp_exists",
357
- "sftp_scandir",
358
- "sftp_getmtime",
359
- "sftp_getsize",
360
- "sftp_isfile",
361
- "sftp_listdir",
362
- "sftp_load_from",
363
- "sftp_makedirs",
364
- "sftp_realpath",
365
- "sftp_rename",
366
- "sftp_move",
367
- "sftp_remove",
368
- "sftp_scan",
369
- "sftp_scan_stat",
370
- "sftp_stat",
371
- "sftp_lstat",
372
- "sftp_unlink",
373
- "sftp_walk",
374
- "sftp_path_join",
375
- "sftp_getmd5",
376
- "sftp_symlink",
377
- "sftp_islink",
378
- "sftp_save_as",
379
- "sftp_open",
380
- "sftp_chmod",
381
- "sftp_rmdir",
382
- "sftp_copy",
383
- "sftp_sync",
384
- "sftp_concat",
385
128
  "sftp_add_host_key",
386
129
  "is_hdfs",
387
- "hdfs_exists",
388
- "hdfs_stat",
389
- "hdfs_getmtime",
390
- "hdfs_getsize",
391
- "hdfs_isdir",
392
- "hdfs_isfile",
393
- "hdfs_listdir",
394
- "hdfs_load_from",
395
- "hdfs_move",
396
- "hdfs_remove",
397
- "hdfs_scan",
398
- "hdfs_scan_stat",
399
- "hdfs_scandir",
400
- "hdfs_unlink",
401
- "hdfs_walk",
402
- "hdfs_getmd5",
403
- "hdfs_save_as",
404
- "hdfs_open",
405
- "hdfs_glob",
406
- "hdfs_glob_stat",
407
- "hdfs_iglob",
408
- "hdfs_makedirs",
130
+ "is_webdav",
131
+ "WebdavPath",
409
132
  "S3Path",
410
133
  "FSPath",
411
134
  "HttpPath",
megfile/cli.py CHANGED
@@ -1,4 +1,3 @@
1
- import configparser
2
1
  import os
3
2
  import shutil
4
3
  import signal
@@ -14,12 +13,18 @@ from click import ParamType
14
13
  from click.shell_completion import CompletionItem, ZshComplete
15
14
  from tqdm import tqdm
16
15
 
17
- from megfile.config import READER_BLOCK_SIZE, SFTP_HOST_KEY_POLICY, set_log_level
16
+ from megfile.config import (
17
+ CONFIG_PATH,
18
+ READER_BLOCK_SIZE,
19
+ SFTP_HOST_KEY_POLICY,
20
+ CaseSensitiveConfigParser,
21
+ set_log_level,
22
+ )
18
23
  from megfile.hdfs_path import DEFAULT_HDFS_TIMEOUT
19
24
  from megfile.interfaces import FileEntry
20
25
  from megfile.lib.glob import get_non_glob_dir, has_magic
21
26
  from megfile.s3_path import get_s3_session
22
- from megfile.sftp import sftp_add_host_key
27
+ from megfile.sftp_path import sftp_add_host_key
23
28
  from megfile.smart import (
24
29
  _smart_sync_single_file,
25
30
  smart_copy,
@@ -86,33 +91,33 @@ def safe_cli(): # pragma: no cover
86
91
  sys.exit(1)
87
92
 
88
93
 
89
- def get_echo_path(file_stat, base_path: str = "", full_path: bool = False):
94
+ def get_echo_path(file_stat, base_path: str = "", full: bool = False):
90
95
  if base_path == file_stat.path:
91
96
  path = file_stat.name
92
- elif full_path:
97
+ elif full:
93
98
  path = file_stat.path
94
99
  else:
95
100
  path = smart_relpath(file_stat.path, start=base_path)
96
101
  return path
97
102
 
98
103
 
99
- def simple_echo(file_stat, base_path: str = "", full_path: bool = False):
100
- return get_echo_path(file_stat, base_path, full_path)
104
+ def simple_echo(file_stat, base_path: str = "", full: bool = False):
105
+ return get_echo_path(file_stat, base_path, full)
101
106
 
102
107
 
103
- def long_echo(file_stat, base_path: str = "", full_path: bool = False):
108
+ def long_echo(file_stat, base_path: str = "", full: bool = False):
104
109
  return "%12d %s %s" % (
105
110
  file_stat.stat.size,
106
111
  time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(file_stat.stat.mtime)),
107
- get_echo_path(file_stat, base_path, full_path),
112
+ get_echo_path(file_stat, base_path, full),
108
113
  )
109
114
 
110
115
 
111
- def human_echo(file_stat, base_path: str = "", full_path: bool = False):
116
+ def human_echo(file_stat, base_path: str = "", full: bool = False):
112
117
  return "%10s %s %s" % (
113
118
  get_human_size(file_stat.stat.size),
114
119
  time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(file_stat.stat.mtime)),
115
- get_echo_path(file_stat, base_path, full_path),
120
+ get_echo_path(file_stat, base_path, full),
116
121
  )
117
122
 
118
123
 
@@ -143,13 +148,12 @@ def _sftp_prompt_host_key(path):
143
148
  )
144
149
 
145
150
 
146
- def _ls(path: str, long: bool, recursive: bool, human_readable: bool):
151
+ def _ls(path: str, long: bool, full: bool, recursive: bool, human_readable: bool):
147
152
  base_path = path
148
- full_path = False
149
153
  if has_magic(path):
150
154
  scan_func = smart_glob_stat
151
155
  base_path = get_non_glob_dir(path)
152
- full_path = True
156
+ full = True
153
157
  elif recursive:
154
158
  scan_func = smart_scan_stat
155
159
  else:
@@ -170,8 +174,8 @@ def _ls(path: str, long: bool, recursive: bool, human_readable: bool):
170
174
  for file_stat in scan_func(path):
171
175
  total_size += file_stat.stat.size
172
176
  total_count += 1
173
- output = echo_func(file_stat, base_path, full_path=full_path)
174
- if file_stat.is_symlink():
177
+ output = echo_func(file_stat, base_path, full=full)
178
+ if long and file_stat.is_symlink():
175
179
  output += " -> %s" % smart_readlink(file_stat.path)
176
180
  click.echo(output)
177
181
  if long:
@@ -215,6 +219,12 @@ ZshComplete.source_template = ZshComplete.source_template.replace(
215
219
  is_flag=True,
216
220
  help="List all the objects in the path with size, modification time and path.",
217
221
  )
222
+ @click.option(
223
+ "-f",
224
+ "--full",
225
+ is_flag=True,
226
+ help="Displays the full path of each file.",
227
+ )
218
228
  @click.option(
219
229
  "-r",
220
230
  "--recursive",
@@ -228,12 +238,24 @@ ZshComplete.source_template = ZshComplete.source_template.replace(
228
238
  is_flag=True,
229
239
  help="Displays file sizes in human readable format.",
230
240
  )
231
- def ls(path: str, long: bool, recursive: bool, human_readable: bool):
232
- _ls(path, long=long, recursive=recursive, human_readable=human_readable)
241
+ def ls(path: str, long: bool, full: bool, recursive: bool, human_readable: bool):
242
+ _ls(
243
+ path,
244
+ long=long,
245
+ full=full,
246
+ recursive=recursive,
247
+ human_readable=human_readable,
248
+ )
233
249
 
234
250
 
235
251
  @cli.command(short_help="List all the objects in the path.")
236
252
  @click.argument("path", type=PathType())
253
+ @click.option(
254
+ "-f",
255
+ "--full",
256
+ is_flag=True,
257
+ help="Displays the full path of each file.",
258
+ )
237
259
  @click.option(
238
260
  "-r",
239
261
  "--recursive",
@@ -241,8 +263,8 @@ def ls(path: str, long: bool, recursive: bool, human_readable: bool):
241
263
  help="Command is performed on all files or objects under "
242
264
  "the specified directory or prefix.",
243
265
  )
244
- def ll(path: str, recursive: bool):
245
- _ls(path, long=True, recursive=recursive, human_readable=True)
266
+ def ll(path: str, recursive: bool, full: bool):
267
+ _ls(path, long=True, full=full, recursive=recursive, human_readable=True)
246
268
 
247
269
 
248
270
  @cli.command(short_help="Copy files from source to dest, skipping already copied.")
@@ -342,8 +364,8 @@ def mv(
342
364
  _sftp_prompt_host_key(dst_path)
343
365
 
344
366
  if progress_bar:
345
- src_protocol, _ = SmartPath._extract_protocol(src_path)
346
- dst_protocol, _ = SmartPath._extract_protocol(dst_path)
367
+ src_protocol = SmartPath._extract_protocol(src_path)
368
+ dst_protocol = SmartPath._extract_protocol(dst_path)
347
369
 
348
370
  if recursive:
349
371
  if src_protocol == dst_protocol:
@@ -824,7 +846,7 @@ def hdfs(path, url, profile_name, user, root, token, timeout, no_cover):
824
846
  "timeout": timeout,
825
847
  }
826
848
  profile_name = f"{profile_name}.alias"
827
- config = configparser.ConfigParser()
849
+ config = CaseSensitiveConfigParser()
828
850
  if os.path.exists(path):
829
851
  config.read(path)
830
852
  if "global" not in config.sections():
@@ -848,37 +870,58 @@ def hdfs(path, url, profile_name, user, root, token, timeout, no_cover):
848
870
  @click.option(
849
871
  "-p",
850
872
  "--path",
851
- default="~/.config/megfile/aliases.conf",
852
- help="alias config file, default is $HOME/.config/megfile/aliases.conf",
873
+ default=CONFIG_PATH,
874
+ help=f"megfile config file, default is {CONFIG_PATH}",
853
875
  )
854
876
  @click.argument("name")
855
877
  @click.argument("protocol_or_path")
856
878
  @click.option("--no-cover", is_flag=True, help="Not cover the same-name config")
857
879
  def alias(path, name, protocol_or_path, no_cover):
858
880
  path = os.path.expanduser(path)
859
- config = configparser.ConfigParser()
881
+ config = CaseSensitiveConfigParser()
860
882
  if os.path.exists(path):
861
883
  config.read(path)
862
- if name in config.sections() and no_cover:
863
- raise NameError(f"alias-name has been used: {name}")
864
-
865
- if "://" in protocol_or_path:
866
- protocol, prefix = protocol_or_path.split("://", maxsplit=1)
867
- config[name] = {
868
- "protocol": protocol,
869
- "prefix": prefix,
870
- }
871
- else:
872
- config[name] = {
873
- "protocol": protocol_or_path,
874
- }
875
-
884
+ config.setdefault("alias", {})
885
+ if config.has_option("alias", name) and no_cover:
886
+ value = config.get("alias", name)
887
+ raise NameError(f"alias-name has been used: {name} = {value}")
888
+ config.set("alias", name, protocol_or_path)
876
889
  _safe_makedirs(os.path.dirname(path)) # make sure dirpath exist
877
890
  with open(path, "w") as fp:
878
891
  config.write(fp)
879
892
  click.echo(f"Your alias config has been saved into {path}")
880
893
 
881
894
 
895
+ @config.command(short_help="Update the config file for envs")
896
+ @click.option(
897
+ "-p",
898
+ "--path",
899
+ default=CONFIG_PATH,
900
+ help=f"megfile config file, default is {CONFIG_PATH}",
901
+ )
902
+ @click.argument("expr")
903
+ @click.option("--no-cover", is_flag=True, help="Not cover the same-name config")
904
+ def env(path, expr, no_cover):
905
+ if "=" not in expr:
906
+ raise ValueError("Invalid env format: {}".format(expr))
907
+ name, value = expr.split("=", 1)
908
+
909
+ path = os.path.expanduser(path)
910
+
911
+ config = CaseSensitiveConfigParser()
912
+ if os.path.exists(path):
913
+ config.read(path)
914
+ config.setdefault("env", {})
915
+ if config.has_option("env", name) and no_cover:
916
+ value = config.get("env", name)
917
+ raise NameError(f"env has been set: {name} = {value}")
918
+ config.set("env", name, value)
919
+ _safe_makedirs(os.path.dirname(path)) # make sure dirpath exist
920
+ with open(path, "w") as fp:
921
+ config.write(fp)
922
+ click.echo(f"Your env config has been saved into {path}")
923
+
924
+
882
925
  @cli.group(short_help="Return the completion file")
883
926
  def completion():
884
927
  pass
megfile/config.py CHANGED
@@ -1,8 +1,14 @@
1
+ import configparser
1
2
  import logging
2
3
  import os
3
4
  import typing as T
4
5
 
5
6
 
7
+ class CaseSensitiveConfigParser(configparser.ConfigParser):
8
+ def optionxform(self, optionstr: str) -> str:
9
+ return optionstr
10
+
11
+
6
12
  def parse_quantity(quantity: T.Union[str, int]) -> int:
7
13
  """
8
14
  Parse kubernetes canonical form quantity like 200Mi to a int number.
@@ -75,6 +81,25 @@ def set_log_level(level: T.Optional[T.Union[int, str]] = None):
75
81
  logging.getLogger("megfile").setLevel(level)
76
82
 
77
83
 
84
+ CONFIG_PATH = "~/.config/megfile/megfile.conf"
85
+
86
+
87
+ def load_megfile_config(section) -> T.Dict[str, str]:
88
+ path = os.path.expanduser(CONFIG_PATH)
89
+ if not os.path.isfile(path):
90
+ return {}
91
+ config = CaseSensitiveConfigParser()
92
+ if os.path.exists(path):
93
+ config.read(path)
94
+ if not config.has_section(section):
95
+ return {}
96
+ return dict(config.items(section))
97
+
98
+
99
+ for key, value in load_megfile_config("env").items():
100
+ os.environ.setdefault(key.upper(), value)
101
+
102
+
78
103
  READER_BLOCK_SIZE = parse_quantity(os.getenv("MEGFILE_READER_BLOCK_SIZE") or 8 * 2**20)
79
104
  if READER_BLOCK_SIZE <= 0:
80
105
  raise ValueError(
@@ -125,10 +150,19 @@ HDFS_MAX_RETRY_TIMES = int(
125
150
  SFTP_MAX_RETRY_TIMES = int(
126
151
  os.getenv("MEGFILE_SFTP_MAX_RETRY_TIMES") or DEFAULT_MAX_RETRY_TIMES
127
152
  )
153
+ WEBDAV_MAX_RETRY_TIMES = int(
154
+ os.getenv("MEGFILE_WEBDAV_MAX_RETRY_TIMES") or DEFAULT_MAX_RETRY_TIMES
155
+ )
128
156
 
129
157
  SFTP_HOST_KEY_POLICY = os.getenv("MEGFILE_SFTP_HOST_KEY_POLICY")
130
158
 
131
- HTTP_AUTH_HEADERS = ("Authorization", "Www-Authenticate", "Cookie", "Cookie2")
159
+ HTTP_AUTH_HEADERS = (
160
+ "Authorization",
161
+ "Www-Authenticate",
162
+ "Cookie",
163
+ "Cookie2",
164
+ "X-Amz-Security-Token",
165
+ )
132
166
 
133
167
  if os.getenv("MEGFILE_LOG_LEVEL"):
134
168
  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",