megfile 4.1.2__py3-none-any.whl → 4.1.3__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 CHANGED
@@ -6,15 +6,19 @@ import sys
6
6
  import time
7
7
  from concurrent.futures import ThreadPoolExecutor
8
8
  from functools import partial
9
+ from itertools import islice
9
10
  from queue import Queue
10
11
 
11
12
  import click
13
+ from click import ParamType
14
+ from click.shell_completion import CompletionItem, ZshComplete
12
15
  from tqdm import tqdm
13
16
 
14
17
  from megfile.config import READER_BLOCK_SIZE, SFTP_HOST_KEY_POLICY, set_log_level
15
18
  from megfile.hdfs_path import DEFAULT_HDFS_TIMEOUT
16
19
  from megfile.interfaces import FileEntry
17
20
  from megfile.lib.glob import get_non_glob_dir, has_magic
21
+ from megfile.s3_path import get_s3_session
18
22
  from megfile.sftp import sftp_add_host_key
19
23
  from megfile.smart import (
20
24
  _smart_sync_single_file,
@@ -47,7 +51,6 @@ from megfile.utils import get_human_size
47
51
  from megfile.version import VERSION
48
52
 
49
53
  options = {}
50
- set_log_level()
51
54
  max_file_object_catch_count = 1024 * 128
52
55
 
53
56
 
@@ -176,8 +179,37 @@ def _ls(path: str, long: bool, recursive: bool, human_readable: bool):
176
179
  click.echo(f"total({total_count}): {get_human_size(total_size)}")
177
180
 
178
181
 
182
+ class PathType(ParamType):
183
+ name = "path"
184
+
185
+ def shell_complete(self, ctx, param, incomplete):
186
+ if "://" not in incomplete and not incomplete.startswith("/"):
187
+ completions = [
188
+ CompletionItem(f"{protocol}://")
189
+ for protocol in SmartPath._registered_protocols
190
+ ]
191
+ for name in get_s3_session().available_profiles:
192
+ if name == "default":
193
+ continue
194
+ completions.append(CompletionItem(f"s3+{name}://"))
195
+ return completions
196
+ try:
197
+ return [
198
+ CompletionItem(f"{entry.path}/" if entry.is_dir() else entry.path)
199
+ for entry in islice(smart_glob_stat(incomplete + "*"), 128)
200
+ ]
201
+ except Exception:
202
+ return []
203
+
204
+
205
+ # Some magic, remove trailing spaces in completion
206
+ ZshComplete.source_template = ZshComplete.source_template.replace(
207
+ "compadd -U -V", "compadd -S '' -U -V"
208
+ )
209
+
210
+
179
211
  @cli.command(short_help="List all the objects in the path.")
180
- @click.argument("path")
212
+ @click.argument("path", type=PathType())
181
213
  @click.option(
182
214
  "-l",
183
215
  "--long",
@@ -202,7 +234,7 @@ def ls(path: str, long: bool, recursive: bool, human_readable: bool):
202
234
 
203
235
 
204
236
  @cli.command(short_help="List all the objects in the path.")
205
- @click.argument("path")
237
+ @click.argument("path", type=PathType())
206
238
  @click.option(
207
239
  "-r",
208
240
  "--recursive",
@@ -215,8 +247,8 @@ def ll(path: str, recursive: bool):
215
247
 
216
248
 
217
249
  @cli.command(short_help="Copy files from source to dest, skipping already copied.")
218
- @click.argument("src_path")
219
- @click.argument("dst_path")
250
+ @click.argument("src_path", type=PathType())
251
+ @click.argument("dst_path", type=PathType())
220
252
  @click.option(
221
253
  "-r",
222
254
  "--recursive",
@@ -282,8 +314,8 @@ def cp(
282
314
 
283
315
 
284
316
  @cli.command(short_help="Move files from source to dest.")
285
- @click.argument("src_path")
286
- @click.argument("dst_path")
317
+ @click.argument("src_path", type=PathType())
318
+ @click.argument("dst_path", type=PathType())
287
319
  @click.option(
288
320
  "-r",
289
321
  "--recursive",
@@ -351,7 +383,7 @@ def mv(
351
383
 
352
384
 
353
385
  @cli.command(short_help="Remove files from path.")
354
- @click.argument("path")
386
+ @click.argument("path", type=PathType())
355
387
  @click.option(
356
388
  "-r",
357
389
  "--recursive",
@@ -367,8 +399,8 @@ def rm(path: str, recursive: bool):
367
399
 
368
400
 
369
401
  @cli.command(short_help="Make source and dest identical, modifying destination only.")
370
- @click.argument("src_path")
371
- @click.argument("dst_path")
402
+ @click.argument("src_path", type=PathType())
403
+ @click.argument("dst_path", type=PathType())
372
404
  @click.option(
373
405
  "-f", "--force", is_flag=True, help="Copy files forcible, ignore same files."
374
406
  )
@@ -500,7 +532,7 @@ def sync(
500
532
 
501
533
 
502
534
  @cli.command(short_help="Make the path if it doesn't already exist.")
503
- @click.argument("path")
535
+ @click.argument("path", type=PathType())
504
536
  def mkdir(path: str):
505
537
  _sftp_prompt_host_key(path)
506
538
 
@@ -508,7 +540,7 @@ def mkdir(path: str):
508
540
 
509
541
 
510
542
  @cli.command(short_help="Make the file if it doesn't already exist.")
511
- @click.argument("path")
543
+ @click.argument("path", type=PathType())
512
544
  def touch(path: str):
513
545
  _sftp_prompt_host_key(path)
514
546
 
@@ -516,7 +548,7 @@ def touch(path: str):
516
548
 
517
549
 
518
550
  @cli.command(short_help="Concatenate any files and send them to stdout.")
519
- @click.argument("path")
551
+ @click.argument("path", type=PathType())
520
552
  def cat(path: str):
521
553
  _sftp_prompt_host_key(path)
522
554
 
@@ -527,7 +559,7 @@ def cat(path: str):
527
559
  @cli.command(
528
560
  short_help="Concatenate any files and send first n lines of them to stdout."
529
561
  )
530
- @click.argument("path")
562
+ @click.argument("path", type=PathType())
531
563
  @click.option(
532
564
  "-n", "--lines", type=click.INT, default=10, help="print the first NUM lines"
533
565
  )
@@ -554,7 +586,7 @@ def _tail_follow_content(path, offset):
554
586
  @cli.command(
555
587
  short_help="Concatenate any files and send last n lines of them to stdout."
556
588
  )
557
- @click.argument("path")
589
+ @click.argument("path", type=PathType())
558
590
  @click.option(
559
591
  "-n", "--lines", type=click.INT, default=10, help="print the last NUM lines"
560
592
  )
@@ -601,7 +633,7 @@ def tail(path: str, lines: int, follow: bool):
601
633
 
602
634
 
603
635
  @cli.command(short_help="Write bytes from stdin to file.")
604
- @click.argument("path")
636
+ @click.argument("path", type=PathType())
605
637
  @click.option("-a", "--append", is_flag=True, help="Append to the given file")
606
638
  @click.option("-o", "--stdout", is_flag=True, help="File content to standard output")
607
639
  def to(path: str, append: bool, stdout: bool):
@@ -626,7 +658,7 @@ def to(path: str, append: bool, stdout: bool):
626
658
 
627
659
 
628
660
  @cli.command(short_help="Produce an md5sum file for all the objects in the path.")
629
- @click.argument("path")
661
+ @click.argument("path", type=PathType())
630
662
  def md5sum(path: str):
631
663
  _sftp_prompt_host_key(path)
632
664
 
@@ -634,7 +666,7 @@ def md5sum(path: str):
634
666
 
635
667
 
636
668
  @cli.command(short_help="Return the total size and number of objects in remote:path.")
637
- @click.argument("path")
669
+ @click.argument("path", type=PathType())
638
670
  def size(path: str):
639
671
  _sftp_prompt_host_key(path)
640
672
 
@@ -642,7 +674,7 @@ def size(path: str):
642
674
 
643
675
 
644
676
  @cli.command(short_help="Return the mtime and number of objects in remote:path.")
645
- @click.argument("path")
677
+ @click.argument("path", type=PathType())
646
678
  def mtime(path: str):
647
679
  _sftp_prompt_host_key(path)
648
680
 
@@ -650,7 +682,7 @@ def mtime(path: str):
650
682
 
651
683
 
652
684
  @cli.command(short_help="Return the stat and number of objects in remote:path.")
653
- @click.argument("path")
685
+ @click.argument("path", type=PathType())
654
686
  def stat(path: str):
655
687
  _sftp_prompt_host_key(path)
656
688
 
@@ -825,18 +857,26 @@ def hdfs(path, url, profile_name, user, root, token, timeout, no_cover):
825
857
  help="alias config file, default is $HOME/.config/megfile/aliases.conf",
826
858
  )
827
859
  @click.argument("name")
828
- @click.argument("protocol")
860
+ @click.argument("protocol_or_path")
829
861
  @click.option("--no-cover", is_flag=True, help="Not cover the same-name config")
830
- def alias(path, name, protocol, no_cover):
862
+ def alias(path, name, protocol_or_path, no_cover):
831
863
  path = os.path.expanduser(path)
832
864
  config = configparser.ConfigParser()
833
865
  if os.path.exists(path):
834
866
  config.read(path)
835
867
  if name in config.sections() and no_cover:
836
868
  raise NameError(f"alias-name has been used: {name}")
837
- config[name] = {
838
- "protocol": protocol,
839
- }
869
+
870
+ if "://" in protocol_or_path:
871
+ protocol, prefix = protocol_or_path.split("://", maxsplit=1)
872
+ config[name] = {
873
+ "protocol": protocol,
874
+ "prefix": prefix,
875
+ }
876
+ else:
877
+ config[name] = {
878
+ "protocol": protocol_or_path,
879
+ }
840
880
 
841
881
  _safe_makedirs(os.path.dirname(path)) # make sure dirpath exist
842
882
  with open(path, "w") as fp:
@@ -844,6 +884,49 @@ def alias(path, name, protocol, no_cover):
844
884
  click.echo(f"Your alias config has been saved into {path}")
845
885
 
846
886
 
887
+ @cli.group(short_help="Return the completion file")
888
+ def completion():
889
+ pass
890
+
891
+
892
+ @completion.command(short_help="Update the config file for bash")
893
+ def bash():
894
+ script_name = os.path.basename(sys.argv[0])
895
+ command = f'eval "$(_{script_name.upper()}_COMPLETE=bash_source {script_name})"'
896
+ config_path = os.path.expanduser("~/.bashrc")
897
+ with open(config_path, "r") as fp:
898
+ if command in fp.read():
899
+ click.echo("Your bashrc has already been updated.")
900
+ return
901
+ with open(config_path, "a") as fp:
902
+ fp.write("\n" + command + "\n")
903
+ click.echo("Your bashrc has been updated.")
904
+
905
+
906
+ @completion.command(short_help="Update the config file for zsh")
907
+ def zsh():
908
+ script_name = os.path.basename(sys.argv[0])
909
+ command = f'eval "$(_{script_name.upper()}_COMPLETE=zsh_source {script_name})"'
910
+ config_path = os.path.expanduser("~/.zshrc")
911
+ with open(config_path, "r") as fp:
912
+ if command in fp.read():
913
+ click.echo("Your zshrc has already been updated.")
914
+ return
915
+ with open(config_path, "a") as fp:
916
+ fp.write("\n" + command + "\n")
917
+ click.echo("Your zshrc has been updated.")
918
+
919
+
920
+ @completion.command(short_help="Update the config file for fish")
921
+ def fish():
922
+ script_name = os.path.basename(sys.argv[0])
923
+ command = f"_{script_name.upper()}_COMPLETE=fish_source {script_name} | source"
924
+ config_path = os.path.expanduser(f"~/.config/fish/completions/{script_name}.fish")
925
+ with open(config_path, "w") as fp:
926
+ fp.write(command)
927
+ click.echo(f"Your fish config has been saved into {config_path}.")
928
+
929
+
847
930
  if __name__ == "__main__":
848
931
  # Usage: python -m megfile.cli
849
932
  safe_cli() # pragma: no cover
megfile/errors.py CHANGED
@@ -97,6 +97,7 @@ s3_retry_exceptions = [
97
97
  botocore.exceptions.ConnectTimeoutError,
98
98
  botocore.exceptions.ProxyConnectionError,
99
99
  botocore.exceptions.ConnectionClosedError,
100
+ botocore.exceptions.SSLError,
100
101
  requests.exceptions.ReadTimeout,
101
102
  requests.exceptions.ConnectTimeout,
102
103
  urllib3.exceptions.IncompleteRead,
@@ -141,8 +142,6 @@ s3_retry_error_codes = (
141
142
  def s3_should_retry(error: Exception) -> bool:
142
143
  if isinstance(error, s3_retry_exceptions): # pyre-ignore[6]
143
144
  return True
144
- if isinstance(error, botocore.exceptions.SSLError):
145
- return "EOF" in str(error)
146
145
  if isinstance(error, botocore.exceptions.ClientError):
147
146
  return client_error_code(error) in s3_retry_error_codes
148
147
  return False
megfile/smart_path.py CHANGED
@@ -81,8 +81,9 @@ class SmartPath(BasePath):
81
81
  raise ProtocolNotFoundError("protocol not found: %r" % path)
82
82
  aliases: Dict[str, Dict[str, str]] = cls._aliases # pyre-ignore[9]
83
83
  if protocol in aliases:
84
+ prefix = aliases[protocol].get("prefix", "")
84
85
  protocol = aliases[protocol]["protocol"]
85
- path = "%s://%s" % (protocol, path_without_protocol)
86
+ path = "%s://%s%s" % (protocol, prefix, path_without_protocol)
86
87
  return protocol, path
87
88
 
88
89
  @classmethod
megfile/version.py CHANGED
@@ -1 +1 @@
1
- VERSION = "4.1.2"
1
+ VERSION = "4.1.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: megfile
3
- Version: 4.1.2
3
+ Version: 4.1.3
4
4
  Summary: Megvii file operation library
5
5
  Author-email: megvii <megfile@megvii.com>
6
6
  Project-URL: Homepage, https://github.com/megvii-research/megfile
@@ -1,7 +1,7 @@
1
1
  megfile/__init__.py,sha256=7oEfu410CFKzDWZ9RjL5xEJ1gtkJkTfvPrL_7TWdJuY,7366
2
- megfile/cli.py,sha256=X9nWGke-VM-FCFdRoEoxVH_SNFkKQesBxhSrRv7jAsI,25884
2
+ megfile/cli.py,sha256=iwwlwVNu_yXgqnaURPHqGPrx1WeReqN0zi9C4rqK5Ag,29178
3
3
  megfile/config.py,sha256=2MMj5QkhlDJQFZRbCQL2c9iDdeMAVctiaPszRBkg5vM,3988
4
- megfile/errors.py,sha256=-HuHCMj6AUPlXZnTWlUScZPoiw_DS6xx0NKsSuhztxw,14646
4
+ megfile/errors.py,sha256=A4qX2h1rk6UOOlNy4E81RmI_zuQd5vjh_rlqTfccwag,14589
5
5
  megfile/fs.py,sha256=KMEqAE35alpcxiy6du5nPFYcaorhUM_kPJMah3q76ng,19160
6
6
  megfile/fs_path.py,sha256=Hozl9LAJ8EMuSWBSZXGj2GNmPZ1sJp9PZs-7hPrLgm8,39341
7
7
  megfile/hdfs.py,sha256=owXr4d3j1frCvlbhmhENcSBnKKDky5cJZzWLOF4ZJMo,13251
@@ -15,10 +15,10 @@ megfile/s3_path.py,sha256=zelXhlRVOVSWBE6HJz0vXrrcRzSuj6Cnjd9HLGwPbCM,93644
15
15
  megfile/sftp.py,sha256=uBcLQs-j6Q-q-sWAdd-pgi5Qmb_kq7boJM-0sCfcNO0,26540
16
16
  megfile/sftp_path.py,sha256=Wz4VcQ0pBUuWDGMSxPpPbutrT09mnY6jZNiAqTi5tO4,43840
17
17
  megfile/smart.py,sha256=Sae2KJzaU0k_qV_Bk0YifOMq8WsV5qQ2pGInDRF546I,36411
18
- megfile/smart_path.py,sha256=HqCOlDwekqqIyJAll-U9YKmaXjjfCGZD5n5aG80lOKw,7592
18
+ megfile/smart_path.py,sha256=Up_6xNZ2019iSzMn_JAU_1H--z-AP6O7SxdXGdeTG0c,7659
19
19
  megfile/stdio.py,sha256=ZwxsnJNJYIT7Iyg5pIw4qiyH8bszG6oAhEJuR-hXGG4,658
20
20
  megfile/stdio_path.py,sha256=cxaDr8rtisTPnN-rjtaEpqQnshwiqwXFUJBM9xWY7Cg,2711
21
- megfile/version.py,sha256=kYS0NJJrKL79xx4qm3iMYFp2WaSU6ttQqSdCIGu0WVA,19
21
+ megfile/version.py,sha256=VxmH81mTbRqT3UnqXh5OXAB3IfXRrcnMl0YgLgOqhPw,19
22
22
  megfile/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  megfile/lib/base_prefetch_reader.py,sha256=uxVwYknOjc8hLF7q_T2QKMsBqFcrf411ZsuK25CN1eQ,12848
24
24
  megfile/lib/combine_reader.py,sha256=Kp2wEloOUpTlIU7dve87MBpSzmIM-F9OtpTawAjFkiU,4828
@@ -43,8 +43,8 @@ megfile/lib/stdio_handler.py,sha256=IDdgENLQlhigEwkLL4zStueVSzdWg7xVcTF_koof_Ek,
43
43
  megfile/lib/url.py,sha256=ER32pWy9Q2MAk3TraAaNEBWIqUeBmLuM57ol2cs7-Ks,103
44
44
  megfile/utils/__init__.py,sha256=xAzmICA0MtAbg-I2yPfeHjA1N4CiMP4sBrC9BgrfZLw,10151
45
45
  megfile/utils/mutex.py,sha256=asb8opGLgK22RiuBJUnfsvB8LnMmodP8KzCVHKmQBWA,2561
46
- megfile-4.1.2.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
47
- megfile-4.1.2.dist-info/licenses/LICENSE.pyre,sha256=9lf5nT-5ZH25JijpYAequ0bl8E8z5JmZB1qrjiUMp84,1080
46
+ megfile-4.1.3.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
47
+ megfile-4.1.3.dist-info/licenses/LICENSE.pyre,sha256=9lf5nT-5ZH25JijpYAequ0bl8E8z5JmZB1qrjiUMp84,1080
48
48
  scripts/benchmark/code/iopath_read.py,sha256=O1Qs3mpvD9S_XCuRH2A2FpGWxCOSw6qZvEBrtPeRL1E,661
49
49
  scripts/benchmark/code/iopath_write.py,sha256=Mm0efW1J09RJ_CK5i1xtG2hJuaaslikin8qVpuRFP_Q,704
50
50
  scripts/benchmark/code/megfile_read.py,sha256=sAMebUiMColHDv3JEkXplImAHvn_IF1-g3BIJxhcQYE,239
@@ -55,8 +55,8 @@ scripts/benchmark/code/s3fs_read.py,sha256=XiTA-qrYblUs-jQWXSnvNg5Wo722C_g47aMMf
55
55
  scripts/benchmark/code/s3fs_write.py,sha256=gdXKkWXYGjLJlRT_J64pJN85XvRg3bZexcAJQEMXwtw,402
56
56
  scripts/benchmark/code/smart_open_read.py,sha256=SA02jHwS9Y31yFtV9CoJcfND5dR0eA_HsGmGNUrpQls,515
57
57
  scripts/benchmark/code/smart_open_write.py,sha256=jDxFJdY97yNH889jz3pawBoei3yaqy8pEMvC_ymHFtM,537
58
- megfile-4.1.2.dist-info/METADATA,sha256=7Bxhk9yqd8P_kSNqVhvqCP1yif4ScV2x63YE6vzsznQ,9595
59
- megfile-4.1.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
60
- megfile-4.1.2.dist-info/entry_points.txt,sha256=M6ZWSSv5_5_QtIpZafy3vq7WuOJ_5dSGQQnEZbByt2Q,49
61
- megfile-4.1.2.dist-info/top_level.txt,sha256=TR6xUw8Px5Ms_UENhEmLNmxOdfHAhTlSt9yTw9LRgsQ,35
62
- megfile-4.1.2.dist-info/RECORD,,
58
+ megfile-4.1.3.dist-info/METADATA,sha256=hKZKmMsgZAysVrZJ3aRQtiGRrvHnRfEPx68ayMEt3EM,9595
59
+ megfile-4.1.3.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
60
+ megfile-4.1.3.dist-info/entry_points.txt,sha256=M6ZWSSv5_5_QtIpZafy3vq7WuOJ_5dSGQQnEZbByt2Q,49
61
+ megfile-4.1.3.dist-info/top_level.txt,sha256=TR6xUw8Px5Ms_UENhEmLNmxOdfHAhTlSt9yTw9LRgsQ,35
62
+ megfile-4.1.3.dist-info/RECORD,,