rclone-api 1.5.14__py3-none-any.whl → 1.5.17__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.
rclone_api/filesystem.py CHANGED
@@ -27,6 +27,10 @@ class FileSystem(abc.ABC):
27
27
  def mkdir(self, path: str, parents=True, exist_ok=True) -> None:
28
28
  pass
29
29
 
30
+ @abc.abstractmethod
31
+ def ls(self, path: Path | str) -> list[str]:
32
+ pass
33
+
30
34
  @abc.abstractmethod
31
35
  def get_path(self, path: str) -> "FSPath":
32
36
  pass
@@ -51,6 +55,9 @@ class RealFileSystem(FileSystem):
51
55
  def __init__(self) -> None:
52
56
  super().__init__()
53
57
 
58
+ def ls(self, path: Path | str) -> list[str]:
59
+ return [str(p) for p in Path(path).iterdir()]
60
+
54
61
  def copy(self, src: Path | str, dest: Path | str) -> None:
55
62
  shutil.copy(str(src), str(dest))
56
63
 
@@ -110,6 +117,24 @@ class RemoteFileSystem(FileSystem):
110
117
  def mkdir(self, path: str, parents=True, exist_ok=True) -> None:
111
118
  raise NotImplementedError("RemoteFileSystem does not support mkdir")
112
119
 
120
+ def is_dir(self, path: Path | str) -> bool:
121
+ path = self._to_str(path)
122
+ err = self.server.list(path)
123
+ return isinstance(err, list)
124
+
125
+ def is_file(self, path: Path | str) -> bool:
126
+ path = self._to_str(path)
127
+ err = self.server.list(path)
128
+ # Make faster.
129
+ return isinstance(err, Exception) and self.exists(path)
130
+
131
+ def ls(self, path: Path | str) -> list[str]:
132
+ path = self._to_str(path)
133
+ err = self.server.list(path)
134
+ if isinstance(err, Exception):
135
+ raise FileNotFoundError(f"File not found: {path}, because of {err}")
136
+ return err
137
+
113
138
  def get_path(self, path: str) -> "FSPath":
114
139
  return FSPath(self, path)
115
140
 
@@ -125,8 +150,8 @@ class RemoteFileSystem(FileSystem):
125
150
 
126
151
  class FSPath:
127
152
  def __init__(self, fs: FileSystem, path: str) -> None:
128
- self.path = path
129
153
  self.fs = fs
154
+ self.path = path
130
155
 
131
156
  def read_text(self) -> str:
132
157
  return self.fs.read_text(self.path)
@@ -155,6 +180,10 @@ class FSPath:
155
180
  assert isinstance(self.fs, RealFileSystem)
156
181
  shutil.rmtree(self.path, ignore_errors=ignore_errors)
157
182
 
183
+ def ls(self) -> "list[FSPath]":
184
+ names: list[str] = self.fs.ls(self.path)
185
+ return [self / name for name in names]
186
+
158
187
  @property
159
188
  def name(self) -> str:
160
189
  return Path(self.path).name
@@ -168,3 +197,7 @@ class FSPath:
168
197
  def __truediv__(self, other: str) -> "FSPath":
169
198
  new_path = Path(self.path) / other
170
199
  return FSPath(self.fs, new_path.as_posix())
200
+
201
+ # hashable
202
+ def __hash__(self) -> int:
203
+ return hash(f"{repr(self.fs)}:{self.path}")
rclone_api/http_server.py CHANGED
@@ -11,6 +11,7 @@ from threading import Semaphore
11
11
  from typing import Any
12
12
 
13
13
  import httpx
14
+ from bs4 import BeautifulSoup
14
15
 
15
16
  from rclone_api.file_part import FilePart
16
17
  from rclone_api.process import Process
@@ -23,6 +24,25 @@ _PUT_WARNED = False
23
24
  _range = range
24
25
 
25
26
 
27
+ def _parse_files(html: str) -> list[str]:
28
+ soup = BeautifulSoup(html, "html.parser")
29
+ files = []
30
+ # Find each table row with class "file"
31
+ for tr in soup.find_all("tr", class_="file"):
32
+ name_span = tr.find("span", class_="name") # type: ignore
33
+ if not name_span:
34
+ continue
35
+ a_tag = name_span.find("a") # type: ignore
36
+ if not a_tag:
37
+ continue
38
+ # Get the text from the <a> tag
39
+ file_name = a_tag.get_text(strip=True) # type: ignore
40
+ # Skip directories (they end with a slash)
41
+ if not file_name.endswith("/"):
42
+ files.append(file_name)
43
+ return files
44
+
45
+
26
46
  class HttpServer:
27
47
  """HTTP server configuration."""
28
48
 
@@ -103,6 +123,24 @@ class HttpServer:
103
123
  warnings.warn(f"Failed to remove {path} from {self.url}: {e}")
104
124
  return e
105
125
 
126
+ # curl "http://localhost:5572/?list"
127
+
128
+ def list(self, path: str) -> list[str] | Exception:
129
+ """List files on the server."""
130
+
131
+ try:
132
+ assert self.process is not None
133
+ url = self.url
134
+ if path:
135
+ url += f"/{path}"
136
+ url += "/?list"
137
+ response = httpx.get(url)
138
+ response.raise_for_status()
139
+ return _parse_files(response.content.decode())
140
+ except Exception as e:
141
+ warnings.warn(f"Failed to list files on {self.url}: {e}")
142
+ return e
143
+
106
144
  def download(
107
145
  self, path: str, dst: Path, range: Range | None = None
108
146
  ) -> Path | Exception:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rclone_api
3
- Version: 1.5.14
3
+ Version: 1.5.17
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -19,6 +19,7 @@ Requires-Dist: psycopg2-binary>=2.9.10
19
19
  Requires-Dist: httpx>=0.28.1
20
20
  Requires-Dist: download>=0.3.5
21
21
  Requires-Dist: appdirs>=1.4.4
22
+ Requires-Dist: beautifulsoup4>=4.13.3
22
23
  Dynamic: home-page
23
24
  Dynamic: license-file
24
25
 
@@ -13,9 +13,9 @@ rclone_api/file_item.py,sha256=cH-AQYsxedhNPp4c8NHY1ad4Z7St4yf_VGbmiGD59no,1770
13
13
  rclone_api/file_part.py,sha256=i6ByS5_sae8Eba-4imBVTxd-xKC8ExWy7NR8QGr0ors,6155
14
14
  rclone_api/file_stream.py,sha256=_W3qnwCuigqA0hzXl2q5pAxSZDRaUSwet4BkT0lpnzs,1431
15
15
  rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
16
- rclone_api/filesystem.py,sha256=NlDlKWjX_WtaqjkwinMpqhaaU5QPkIrnTNKi7_k3tG0,4960
16
+ rclone_api/filesystem.py,sha256=QPERBMxrTWNaWMxuHu5QnbRLF5eoSYDODNpjhD8K694,6008
17
17
  rclone_api/group_files.py,sha256=H92xPW9lQnbNw5KbtZCl00bD6iRh9yRbCuxku4j_3dg,8036
18
- rclone_api/http_server.py,sha256=p1_S9VAViVvGif6NA_rxrDgMqnOmPTxJaHQ7B43FK70,10431
18
+ rclone_api/http_server.py,sha256=ZdL-rGaq0zIjcaIiRIbPBQ4OIWZ7dCu71aq0nRlKtY4,11686
19
19
  rclone_api/install.py,sha256=Xb1BRn8rQcSpSd4dzmvIOELP2zM2DytUeIZ6jzv738A,2893
20
20
  rclone_api/log.py,sha256=VZHM7pNSXip2ZLBKMP7M1u-rp_F7zoafFDuR8CPUoKI,1271
21
21
  rclone_api/mount.py,sha256=LZqEhuKZunbWVqmsOIqkkCotaxWJpdFRS1InXveoU5E,1428
@@ -54,9 +54,9 @@ rclone_api/s3/multipart/upload_parts_inline.py,sha256=V7syKjFyVIe4U9Ahl5XgqVTzt9
54
54
  rclone_api/s3/multipart/upload_parts_resumable.py,sha256=6-nlMclS8jyVvMvFbQDcZOX9MY1WbCcKA_s9bwuYxnk,9793
55
55
  rclone_api/s3/multipart/upload_parts_server_side_merge.py,sha256=Fp2pdrs5dONQI9LkfNolgAGj1-Z2V1SsRd0r0sreuXI,18040
56
56
  rclone_api/s3/multipart/upload_state.py,sha256=f-Aq2NqtAaMUMhYitlICSNIxCKurWAl2gDEUVizLIqw,6019
57
- rclone_api-1.5.14.dist-info/licenses/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
58
- rclone_api-1.5.14.dist-info/METADATA,sha256=NEyfLjNiEDEL1_rdIVPkqPPr1fksSmZFpxgDFmkCFJQ,32657
59
- rclone_api-1.5.14.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
60
- rclone_api-1.5.14.dist-info/entry_points.txt,sha256=fJteOlYVwgX3UbNuL9jJ0zUTuX2O79JFAeNgK7Sw7EQ,255
61
- rclone_api-1.5.14.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
62
- rclone_api-1.5.14.dist-info/RECORD,,
57
+ rclone_api-1.5.17.dist-info/licenses/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
58
+ rclone_api-1.5.17.dist-info/METADATA,sha256=BkNxUxGH2-ZTFkBL2vfpCEf03jm8IVizA0uuzDh6gDQ,32696
59
+ rclone_api-1.5.17.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
60
+ rclone_api-1.5.17.dist-info/entry_points.txt,sha256=fJteOlYVwgX3UbNuL9jJ0zUTuX2O79JFAeNgK7Sw7EQ,255
61
+ rclone_api-1.5.17.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
62
+ rclone_api-1.5.17.dist-info/RECORD,,