megfile 5.0.0__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,3 +1,4 @@
1
+ import megfile.config # noqa: F401 # make sure env config is loaded
1
2
  from megfile.fs_path import FSPath, is_fs
2
3
  from megfile.hdfs_path import HdfsPath, is_hdfs
3
4
  from megfile.http_path import HttpPath, HttpsPath, is_http
@@ -7,6 +8,7 @@ from megfile.s3_path import (
7
8
  s3_buffered_open,
8
9
  s3_cached_open,
9
10
  s3_memory_open,
11
+ s3_open,
10
12
  s3_pipe_open,
11
13
  s3_prefetch_open,
12
14
  s3_share_cache_open,
@@ -114,6 +116,7 @@ __all__ = [
114
116
  "s3_buffered_open",
115
117
  "s3_cached_open",
116
118
  "s3_memory_open",
119
+ "s3_open",
117
120
  "s3_pipe_open",
118
121
  "s3_prefetch_open",
119
122
  "s3_share_cache_open",
megfile/cli.py CHANGED
@@ -1,4 +1,3 @@
1
- import configparser
2
1
  import os
3
2
  import shutil
4
3
  import signal
@@ -14,7 +13,13 @@ 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
@@ -841,7 +846,7 @@ def hdfs(path, url, profile_name, user, root, token, timeout, no_cover):
841
846
  "timeout": timeout,
842
847
  }
843
848
  profile_name = f"{profile_name}.alias"
844
- config = configparser.ConfigParser()
849
+ config = CaseSensitiveConfigParser()
845
850
  if os.path.exists(path):
846
851
  config.read(path)
847
852
  if "global" not in config.sections():
@@ -865,37 +870,58 @@ def hdfs(path, url, profile_name, user, root, token, timeout, no_cover):
865
870
  @click.option(
866
871
  "-p",
867
872
  "--path",
868
- default="~/.config/megfile/aliases.conf",
869
- 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}",
870
875
  )
871
876
  @click.argument("name")
872
877
  @click.argument("protocol_or_path")
873
878
  @click.option("--no-cover", is_flag=True, help="Not cover the same-name config")
874
879
  def alias(path, name, protocol_or_path, no_cover):
875
880
  path = os.path.expanduser(path)
876
- config = configparser.ConfigParser()
881
+ config = CaseSensitiveConfigParser()
877
882
  if os.path.exists(path):
878
883
  config.read(path)
879
- if name in config.sections() and no_cover:
880
- raise NameError(f"alias-name has been used: {name}")
881
-
882
- if "://" in protocol_or_path:
883
- protocol, prefix = protocol_or_path.split("://", maxsplit=1)
884
- config[name] = {
885
- "protocol": protocol,
886
- "prefix": prefix,
887
- }
888
- else:
889
- config[name] = {
890
- "protocol": protocol_or_path,
891
- }
892
-
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)
893
889
  _safe_makedirs(os.path.dirname(path)) # make sure dirpath exist
894
890
  with open(path, "w") as fp:
895
891
  config.write(fp)
896
892
  click.echo(f"Your alias config has been saved into {path}")
897
893
 
898
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
+
899
925
  @cli.group(short_help="Return the completion file")
900
926
  def completion():
901
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(
@@ -19,7 +19,7 @@ def _webdav_stat(client: WebdavClient, remote_path: str):
19
19
  info = WebDavXmlUtils.parse_info_response(
20
20
  response.content, path, client.webdav.hostname
21
21
  )
22
- info["is_dir"] = WebDavXmlUtils.parse_is_dir_response(
22
+ info["isdir"] = WebDavXmlUtils.parse_is_dir_response(
23
23
  response.content, path, client.webdav.hostname
24
24
  )
25
25
  return info
@@ -60,7 +60,7 @@ class WebdavMemoryHandler(BaseMemoryHandler):
60
60
 
61
61
  def _file_exists(self) -> bool:
62
62
  try:
63
- return not _webdav_stat(self._client, self._remote_path)["is_dir"]
63
+ return not _webdav_stat(self._client, self._remote_path)["isdir"]
64
64
  except RemoteResourceNotFound:
65
65
  return False
66
66
 
megfile/smart_path.py CHANGED
@@ -1,18 +1,19 @@
1
1
  import os
2
- from configparser import ConfigParser
3
2
  from functools import cached_property
4
3
  from pathlib import PurePath
5
4
  from typing import Dict, Optional, Tuple, Union
6
5
 
6
+ from megfile.config import load_megfile_config
7
7
  from megfile.lib.compat import fspath
8
8
  from megfile.lib.url import get_url_scheme
9
9
  from megfile.pathlike import URIPathParents
10
10
  from megfile.utils import cached_classproperty
11
11
 
12
+ from .config import CaseSensitiveConfigParser
12
13
  from .errors import ProtocolExistsError, ProtocolNotFoundError
13
14
  from .interfaces import BasePath, PathLike
14
15
 
15
- aliases_config = "~/.config/megfile/aliases.conf"
16
+ LEGACY_ALIASES_CONFIG = "~/.config/megfile/aliases.conf"
16
17
 
17
18
 
18
19
  def _bind_function(name, after_callback=None, before_callback=None):
@@ -54,14 +55,20 @@ def _bind_property(name, callback=None):
54
55
  return smart_property
55
56
 
56
57
 
57
- def _load_aliases_config(config_path) -> Dict[str, Dict[str, str]]:
58
- if not os.path.exists(config_path):
59
- return {}
60
- parser = ConfigParser()
61
- parser.read(config_path)
58
+ def _load_aliases_config() -> Dict[str, Dict[str, str]]:
62
59
  configs = {}
63
- for section in parser.sections():
64
- configs[section] = dict(parser.items(section))
60
+ config_path = os.path.expanduser(LEGACY_ALIASES_CONFIG)
61
+ if os.path.isfile(config_path):
62
+ parser = CaseSensitiveConfigParser()
63
+ parser.read(config_path)
64
+ for section in parser.sections():
65
+ configs[section] = dict(parser.items(section))
66
+ for name, protocol_or_path in load_megfile_config("alias").items():
67
+ if "://" in protocol_or_path:
68
+ protocol, prefix = protocol_or_path.split("://", maxsplit=1)
69
+ configs[name] = {"protocol": protocol, "prefix": prefix}
70
+ else:
71
+ configs[name] = {"protocol": protocol_or_path}
65
72
  return configs
66
73
 
67
74
 
@@ -151,8 +158,7 @@ class SmartPath(BasePath):
151
158
 
152
159
  @cached_classproperty
153
160
  def _aliases(cls) -> Dict[str, Dict[str, str]]:
154
- config_path = os.path.expanduser(aliases_config)
155
- return _load_aliases_config(config_path)
161
+ return _load_aliases_config()
156
162
 
157
163
  @classmethod
158
164
  def _split_protocol(cls, path: Union[PathLike, int]) -> Tuple[str, Union[str, int]]:
megfile/version.py CHANGED
@@ -1 +1 @@
1
- VERSION = "5.0.0"
1
+ VERSION = "5.0.1"
megfile/webdav_path.py CHANGED
@@ -78,7 +78,7 @@ def _make_stat(info: dict) -> StatResult:
78
78
  except Exception:
79
79
  mtime = 0.0
80
80
 
81
- isdir = info.get("is_dir", False)
81
+ isdir = info.get("isdir", False)
82
82
 
83
83
  return StatResult(
84
84
  size=size,
@@ -144,6 +144,7 @@ def _patch_execute_request(
144
144
  cmds = shlex.split(client.webdav.token_command)
145
145
  client.webdav.token_command_last_call = time.time()
146
146
  client.webdav.token = subprocess.check_output(cmds).decode().strip()
147
+ _logger.debug("update webdav token by command: %s", client.webdav.token_command)
147
148
 
148
149
  def webdav_should_retry(error: Exception) -> bool:
149
150
  if http_should_retry(error):
@@ -457,7 +458,7 @@ class WebdavPath(URIPath):
457
458
  :returns: True if the path is a directory, else False
458
459
  """
459
460
  try:
460
- return _webdav_stat(self._client, self._remote_path)["is_dir"]
461
+ return _webdav_stat(self._client, self._remote_path)["isdir"]
461
462
  except RemoteResourceNotFound:
462
463
  return False
463
464
 
@@ -469,7 +470,7 @@ class WebdavPath(URIPath):
469
470
  :returns: True if the path is a file, else False
470
471
  """
471
472
  try:
472
- return not _webdav_stat(self._client, self._remote_path)["is_dir"]
473
+ return not _webdav_stat(self._client, self._remote_path)["isdir"]
473
474
  except RemoteResourceNotFound:
474
475
  return False
475
476
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: megfile
3
- Version: 5.0.0
3
+ Version: 5.0.1
4
4
  Summary: Megvii file operation library
5
5
  Author-email: megvii-reng <megvii-reng@googlegroups.com>
6
6
  Project-URL: Homepage, https://github.com/megvii-research/megfile
@@ -1,6 +1,6 @@
1
- megfile/__init__.py,sha256=2QtWAC9XFlKDhY0NTHsCJd2wQ-8LKz0q1ng4-8cXo38,2836
2
- megfile/cli.py,sha256=yRrnXLYejCz1928JCjjiqOe50YH8lD1xuwaiej_67Ng,29352
3
- megfile/config.py,sha256=r_QmInU_gFiSTV1nLJ5SyBVCX5bsANqOoNM82bRESnQ,4414
1
+ megfile/__init__.py,sha256=YvF3lLDTnxYxeICLYwzwJHQWfNTizBx8B0BwjWvHnRU,2934
2
+ megfile/cli.py,sha256=HdOIAMFXdZw79kayVIri5-_rbq_KwOGSdMYTx51ABp0,30204
3
+ megfile/config.py,sha256=K3B_o2dnI7qGsGnK8Jg18-S5YYLYuzskfNJowlSMkQM,5065
4
4
  megfile/errors.py,sha256=eC7z-2-QqE12pYgTGAOIlEBsemqcMyeBFVUEDz3gBS0,15585
5
5
  megfile/fs_path.py,sha256=tt2__W6E4vep0lmVreTLIW63njl-EzyQEEkEGziyAb4,41015
6
6
  megfile/hdfs_path.py,sha256=OmUe3vA3Qoxnqtcq0Rs3ygBvzAtqUz3fGo8iP5sWneE,26058
@@ -11,10 +11,10 @@ megfile/s3_path.py,sha256=LINHnHnpesXnf9wxbV6n0xQVT0wPwyjLc7xAasakefU,94467
11
11
  megfile/sftp2_path.py,sha256=K90bnMVAx0MQPGXP6LogGuDRzaD4MPR6lMOfdY9C9-0,37942
12
12
  megfile/sftp_path.py,sha256=_KU7_-Mq2m7lcLY1mpiGrju0SP-OsdEXlRjFhZH25UA,51223
13
13
  megfile/smart.py,sha256=Lab2jxprj-zvPw5GqUWlWiEY8bcpRlviks_qp9r-km8,38224
14
- megfile/smart_path.py,sha256=cI8LSD1M7p-pHFJ-yDIgen8g9FyfbfDLrDfQwkkKT-Q,12240
14
+ megfile/smart_path.py,sha256=kGidkM5S58ChE3LVZMcUACs3IQgsqh9m04sp6-wxuhk,12615
15
15
  megfile/stdio_path.py,sha256=cxaDr8rtisTPnN-rjtaEpqQnshwiqwXFUJBM9xWY7Cg,2711
16
- megfile/version.py,sha256=sWZ_cOSUdsW9iQos7P4NME_qRop6_sbMdttJcWoYa7o,19
17
- megfile/webdav_path.py,sha256=96rt3jN_XfDXY0UYC_Kufrz5Uc5KySo34QnlVbDIQK8,31278
16
+ megfile/version.py,sha256=LPuRF_X3cat_YjhbR3eY8fMsZ1mTDe7rRMdxF3ozYRY,19
17
+ megfile/webdav_path.py,sha256=xQmZMt-hDA7PfHzuSjRYaIoJA_Nbi1jsg952KZJhs-E,31364
18
18
  megfile/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  megfile/lib/base_memory_handler.py,sha256=i5-YHpL0k2tuFBnP9VMzb3_OsaU6D6j6thkmWgptnFg,2680
20
20
  megfile/lib/base_prefetch_reader.py,sha256=MYaWOkXc3geZMYNPHlPZmmpOR6uSz-AMuCZwYdoz7t0,13296
@@ -38,14 +38,14 @@ megfile/lib/s3_share_cache_reader.py,sha256=8uip5IdVjPXCquXrskjocsZx2-TiXqWZPY0g
38
38
  megfile/lib/shadow_handler.py,sha256=TntewlvIW9ZxCfmqASDQREHoiZ8v42faOe9sovQYQz0,2779
39
39
  megfile/lib/stdio_handler.py,sha256=IDdgENLQlhigEwkLL4zStueVSzdWg7xVcTF_koof_Ek,1987
40
40
  megfile/lib/url.py,sha256=ER32pWy9Q2MAk3TraAaNEBWIqUeBmLuM57ol2cs7-Ks,103
41
- megfile/lib/webdav_memory_handler.py,sha256=KDSU-Z3ECiH5p6mo8TsbqHxjSBCZNx-LtFW9nIimdOY,2508
41
+ megfile/lib/webdav_memory_handler.py,sha256=_UccPYPpvfTd4gSEhBFL1BHeyFtsBJdhVINkjNNtyaw,2506
42
42
  megfile/lib/webdav_prefetch_reader.py,sha256=M0X6E6t-DS5q9KiLvjVZx_AZuiW9SaIkBnIPLc774GQ,3941
43
43
  megfile/utils/__init__.py,sha256=4hBVSXbNTbDj7Je0y9SbwgcPm_s41H9v3eHUMr9JNGo,12700
44
44
  megfile/utils/mutex.py,sha256=asb8opGLgK22RiuBJUnfsvB8LnMmodP8KzCVHKmQBWA,2561
45
- megfile-5.0.0.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
46
- megfile-5.0.0.dist-info/licenses/LICENSE.pyre,sha256=9lf5nT-5ZH25JijpYAequ0bl8E8z5JmZB1qrjiUMp84,1080
47
- megfile-5.0.0.dist-info/METADATA,sha256=xwRH2pja42oQN9jJHaiTsFeKO0Ib8Ih4Thvi2cyNns0,9225
48
- megfile-5.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
- megfile-5.0.0.dist-info/entry_points.txt,sha256=M6ZWSSv5_5_QtIpZafy3vq7WuOJ_5dSGQQnEZbByt2Q,49
50
- megfile-5.0.0.dist-info/top_level.txt,sha256=i3rMgdU1ZAJekAceojhA-bkm3749PzshtRmLTbeLUPQ,8
51
- megfile-5.0.0.dist-info/RECORD,,
45
+ megfile-5.0.1.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
46
+ megfile-5.0.1.dist-info/licenses/LICENSE.pyre,sha256=9lf5nT-5ZH25JijpYAequ0bl8E8z5JmZB1qrjiUMp84,1080
47
+ megfile-5.0.1.dist-info/METADATA,sha256=r-7Od-pbEExL11PMsE0VhgxUvkv_O0kGz0s8bzzczRE,9225
48
+ megfile-5.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
+ megfile-5.0.1.dist-info/entry_points.txt,sha256=M6ZWSSv5_5_QtIpZafy3vq7WuOJ_5dSGQQnEZbByt2Q,49
50
+ megfile-5.0.1.dist-info/top_level.txt,sha256=i3rMgdU1ZAJekAceojhA-bkm3749PzshtRmLTbeLUPQ,8
51
+ megfile-5.0.1.dist-info/RECORD,,