rclone-api 1.5.70__py3-none-any.whl → 1.5.71__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/fs/walk.py CHANGED
@@ -1,27 +1,62 @@
1
- from pathlib import Path
1
+ import os
2
+ from collections import OrderedDict
3
+ from concurrent.futures import FIRST_COMPLETED, ThreadPoolExecutor, wait
2
4
  from typing import Generator
3
5
 
4
6
  from rclone_api.fs.filesystem import FSPath, logger
5
7
 
8
+ _FS_WALK_THREAD_MAX_BACKLOG = int(os.getenv("FS_WALK_THREAD_MAX_BACKLOG", "16"))
9
+ # module‐wide executor
10
+ _executor = ThreadPoolExecutor(max_workers=_FS_WALK_THREAD_MAX_BACKLOG)
6
11
 
7
- def fs_walk(
12
+
13
+ def _list_dir(path: FSPath):
14
+ try:
15
+ filenames, dirnames = path.ls()
16
+ except Exception as e:
17
+ logger.warning(f"Unable to list directory {path}: {e}")
18
+ return None
19
+ return path, dirnames, filenames
20
+
21
+
22
+ def fs_walk_parallel(
8
23
  self: FSPath,
9
24
  ) -> Generator[tuple[FSPath, list[str], list[str]], None, None]:
10
- root_path = self
11
- stack: list[str] = [self.path]
12
-
13
- while stack:
14
- curr_path = stack.pop()
15
- curr_suffix_path = Path(curr_path).relative_to(Path(root_path.path)).as_posix()
16
- current_dir = root_path / curr_suffix_path
17
- try:
18
- filenames, dirnames = current_dir.ls()
19
- except Exception as e:
20
- logger.warning(f"Unable to list directory {current_dir}: {e}")
21
- continue
22
-
23
- yield current_dir, dirnames, filenames
24
-
25
- # Add subdirectories to stack for further traversal
26
- for dirname in reversed(dirnames):
27
- stack.append((current_dir / dirname).path)
25
+ """
26
+ Parallel version of fs_walk: walks `self` and lists
27
+ up to 16 directories at once using the global executor,
28
+ but yields results in the same order tasks were submitted.
29
+ """
30
+ root = self
31
+
32
+ # use an OrderedDict to remember submission order
33
+ futures: OrderedDict = OrderedDict()
34
+ # submit the root first
35
+ futures[_executor.submit(_list_dir, root)] = root
36
+
37
+ while futures:
38
+ # wait until at least one of them finishes
39
+ done, _ = wait(futures.keys(), return_when=FIRST_COMPLETED)
40
+
41
+ # iterate through our futures *in submission order*
42
+ for fut in list(futures.keys()):
43
+ if fut not in done:
44
+ continue
45
+
46
+ _ = futures.pop(fut) # remove it in order
47
+ result = fut.result()
48
+ if result is None:
49
+ continue # error already logged
50
+
51
+ current_dir, dirnames, filenames = result
52
+ yield current_dir, dirnames, filenames
53
+
54
+ # now schedule its children (they go at the end of our OrderedDict)
55
+ for dirname in dirnames:
56
+ sub = current_dir / dirname
57
+ futures[_executor.submit(_list_dir, sub)] = sub
58
+
59
+
60
+ def fs_walk(self: FSPath) -> Generator[tuple[FSPath, list[str], list[str]], None, None]:
61
+ """Sequential API, now backed by the global-thread-pool parallel implementation."""
62
+ yield from fs_walk_parallel(self)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rclone_api
3
- Version: 1.5.70
3
+ Version: 1.5.71
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -41,7 +41,7 @@ rclone_api/detail/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,336
41
41
  rclone_api/experimental/flags.py,sha256=qCVD--fSTmzlk9hloRLr0q9elzAOFzPsvVpKM3aB1Mk,2739
42
42
  rclone_api/experimental/flags_base.py,sha256=ajU_czkTcAxXYU-SlmiCfHY7aCQGHvpCLqJ-Z8uZLk0,2102
43
43
  rclone_api/fs/filesystem.py,sha256=2Ewa5g_842XU-UT4JDkPkq199pjknjoqpTiKtbRRRGk,11964
44
- rclone_api/fs/walk.py,sha256=Yqn_SS25gTL0ANWSQxnAdN-ELBO1PMZfYYnyxoGafWA,875
44
+ rclone_api/fs/walk.py,sha256=gqxfJqzFSQlLwZ18ttO25SbCCs7Vcz_CtlDgjiqpNZc,2183
45
45
  rclone_api/fs/walk_threaded.py,sha256=zlbooQ7WbteUK1cCjm1Yj66VpDzS8Qm3DcHBSz1Gbjk,1591
46
46
  rclone_api/fs/walk_threaded_walker.py,sha256=xi0QkmmzI35NcaA08FLjGe7ox9tLZcgXtOZCT8nMuWU,718
47
47
  rclone_api/s3/api.py,sha256=HpheMOGHcCc0CyNdv0zvB0S92g312urjRdssRrkdpb8,4162
@@ -58,9 +58,9 @@ rclone_api/s3/multipart/upload_parts_inline.py,sha256=V7syKjFyVIe4U9Ahl5XgqVTzt9
58
58
  rclone_api/s3/multipart/upload_parts_resumable.py,sha256=tdOkEfoK9zENaTfgVyTWB5GNqn-IYl-zJn-rJLcoVTo,12388
59
59
  rclone_api/s3/multipart/upload_parts_server_side_merge.py,sha256=Fp2pdrs5dONQI9LkfNolgAGj1-Z2V1SsRd0r0sreuXI,18040
60
60
  rclone_api/s3/multipart/upload_state.py,sha256=f-Aq2NqtAaMUMhYitlICSNIxCKurWAl2gDEUVizLIqw,6019
61
- rclone_api-1.5.70.dist-info/licenses/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
62
- rclone_api-1.5.70.dist-info/METADATA,sha256=5DqhnAOnUh_TpQ-XqlSrmZv8imYRt0vqJeKsRqwt020,37305
63
- rclone_api-1.5.70.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
64
- rclone_api-1.5.70.dist-info/entry_points.txt,sha256=ognh2e11HTjn73_KL5MWI67pBKS2jekBi-QTiRXySXA,316
65
- rclone_api-1.5.70.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
66
- rclone_api-1.5.70.dist-info/RECORD,,
61
+ rclone_api-1.5.71.dist-info/licenses/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
62
+ rclone_api-1.5.71.dist-info/METADATA,sha256=qnLOllDyan73gwf8Nb5W5B9vhYnRUP1fRhRrA-5c56Q,37305
63
+ rclone_api-1.5.71.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
64
+ rclone_api-1.5.71.dist-info/entry_points.txt,sha256=ognh2e11HTjn73_KL5MWI67pBKS2jekBi-QTiRXySXA,316
65
+ rclone_api-1.5.71.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
66
+ rclone_api-1.5.71.dist-info/RECORD,,