rclone-api 1.1.28__tar.gz → 1.1.30__tar.gz

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.
Files changed (85) hide show
  1. {rclone_api-1.1.28 → rclone_api-1.1.30}/PKG-INFO +1 -1
  2. {rclone_api-1.1.28 → rclone_api-1.1.30}/pyproject.toml +1 -1
  3. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/rclone.py +54 -3
  4. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api.egg-info/PKG-INFO +1 -1
  5. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api.egg-info/SOURCES.txt +1 -0
  6. rclone_api-1.1.30/tests/test_copy_bytes.py +97 -0
  7. {rclone_api-1.1.28 → rclone_api-1.1.30}/.aiderignore +0 -0
  8. {rclone_api-1.1.28 → rclone_api-1.1.30}/.github/workflows/lint.yml +0 -0
  9. {rclone_api-1.1.28 → rclone_api-1.1.30}/.github/workflows/push_macos.yml +0 -0
  10. {rclone_api-1.1.28 → rclone_api-1.1.30}/.github/workflows/push_ubuntu.yml +0 -0
  11. {rclone_api-1.1.28 → rclone_api-1.1.30}/.github/workflows/push_win.yml +0 -0
  12. {rclone_api-1.1.28 → rclone_api-1.1.30}/.gitignore +0 -0
  13. {rclone_api-1.1.28 → rclone_api-1.1.30}/.pylintrc +0 -0
  14. {rclone_api-1.1.28 → rclone_api-1.1.30}/.vscode/launch.json +0 -0
  15. {rclone_api-1.1.28 → rclone_api-1.1.30}/.vscode/settings.json +0 -0
  16. {rclone_api-1.1.28 → rclone_api-1.1.30}/.vscode/tasks.json +0 -0
  17. {rclone_api-1.1.28 → rclone_api-1.1.30}/LICENSE +0 -0
  18. {rclone_api-1.1.28 → rclone_api-1.1.30}/MANIFEST.in +0 -0
  19. {rclone_api-1.1.28 → rclone_api-1.1.30}/README.md +0 -0
  20. {rclone_api-1.1.28 → rclone_api-1.1.30}/clean +0 -0
  21. {rclone_api-1.1.28 → rclone_api-1.1.30}/install +0 -0
  22. {rclone_api-1.1.28 → rclone_api-1.1.30}/lint +0 -0
  23. {rclone_api-1.1.28 → rclone_api-1.1.30}/requirements.testing.txt +0 -0
  24. {rclone_api-1.1.28 → rclone_api-1.1.30}/setup.cfg +0 -0
  25. {rclone_api-1.1.28 → rclone_api-1.1.30}/setup.py +0 -0
  26. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/__init__.py +0 -0
  27. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/assets/example.txt +0 -0
  28. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/cli.py +0 -0
  29. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/cmd/copy_large_s3.py +0 -0
  30. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/cmd/list_files.py +0 -0
  31. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/completed_process.py +0 -0
  32. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/config.py +0 -0
  33. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/convert.py +0 -0
  34. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/deprecated.py +0 -0
  35. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/diff.py +0 -0
  36. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/dir.py +0 -0
  37. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/dir_listing.py +0 -0
  38. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/exec.py +0 -0
  39. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/experimental/flags.py +0 -0
  40. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/experimental/flags_base.py +0 -0
  41. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/file.py +0 -0
  42. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/filelist.py +0 -0
  43. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/group_files.py +0 -0
  44. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/mount.py +0 -0
  45. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/process.py +0 -0
  46. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/remote.py +0 -0
  47. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/rpath.py +0 -0
  48. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/s3/api.py +0 -0
  49. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/s3/basic_ops.py +0 -0
  50. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/s3/chunk_file.py +0 -0
  51. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/s3/chunk_types.py +0 -0
  52. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/s3/create.py +0 -0
  53. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/s3/types.py +0 -0
  54. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/s3/upload_file_multipart.py +0 -0
  55. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/scan_missing_folders.py +0 -0
  56. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/types.py +0 -0
  57. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/util.py +0 -0
  58. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api/walk.py +0 -0
  59. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api.egg-info/dependency_links.txt +0 -0
  60. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api.egg-info/entry_points.txt +0 -0
  61. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api.egg-info/requires.txt +0 -0
  62. {rclone_api-1.1.28 → rclone_api-1.1.30}/src/rclone_api.egg-info/top_level.txt +0 -0
  63. {rclone_api-1.1.28 → rclone_api-1.1.30}/test +0 -0
  64. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/archive/test_paramiko.py.disabled +0 -0
  65. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_cmd_list_files.py +0 -0
  66. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_copy.py +0 -0
  67. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_copy_file_resumable_s3.py +0 -0
  68. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_copy_files.py +0 -0
  69. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_diff.py +0 -0
  70. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_group_files.py +0 -0
  71. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_is_synced.py +0 -0
  72. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_ls.py +0 -0
  73. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_mount.py +0 -0
  74. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_mount_s3.py +0 -0
  75. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_obscure.py +0 -0
  76. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_rclone_config.py +0 -0
  77. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_remote_control.py +0 -0
  78. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_remotes.py +0 -0
  79. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_s3.py +0 -0
  80. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_scan_missing_folders.py +0 -0
  81. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_size_files.py +0 -0
  82. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_size_suffix.py +0 -0
  83. {rclone_api-1.1.28 → rclone_api-1.1.30}/tests/test_walk.py +0 -0
  84. {rclone_api-1.1.28 → rclone_api-1.1.30}/tox.ini +0 -0
  85. {rclone_api-1.1.28 → rclone_api-1.1.30}/upload_package.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.1.28
3
+ Version: 1.1.30
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -22,7 +22,7 @@ dependencies = [
22
22
  ]
23
23
 
24
24
  # Change this with the version number bump.
25
- version = "1.1.28"
25
+ version = "1.1.30"
26
26
 
27
27
  [tool.setuptools]
28
28
  package-dir = {"" = "src"}
@@ -691,7 +691,7 @@ class Rclone:
691
691
  from rclone_api.util import S3PathInfo, random_str, split_s3_path
692
692
 
693
693
  other_args: list[str] = ["--no-modtime", "--vfs-read-wait", "1s"]
694
- chunk_size = chunk_size or SizeSuffix("128M")
694
+ chunk_size = chunk_size or SizeSuffix("64M")
695
695
  unit_chunk_size = chunk_size / read_threads
696
696
  vfs_read_chunk_size = unit_chunk_size
697
697
  vfs_read_chunk_size_limit = chunk_size
@@ -726,8 +726,6 @@ class Rclone:
726
726
  verbose=False,
727
727
  other_args=other_args,
728
728
  ):
729
- # raise NotImplementedError("Not implemented yet")
730
-
731
729
  path_info: S3PathInfo = split_s3_path(dst)
732
730
  remote = path_info.remote
733
731
  bucket_name = path_info.bucket
@@ -813,6 +811,59 @@ class Rclone:
813
811
  )
814
812
  return out
815
813
 
814
+ def copy_bytes(
815
+ self,
816
+ src: str,
817
+ offset: int,
818
+ length: int,
819
+ transfers: int = 16,
820
+ outfile: (
821
+ Path | None
822
+ ) = None, # If supplied then bytes are written to this file and success returns bytes(0)
823
+ ) -> bytes | Exception:
824
+ """Copy bytes from a file to another file."""
825
+ from rclone_api.util import random_str
826
+
827
+ tmp_mnt = Path("tmp_mnt") / random_str(12)
828
+ src_parent_path = Path(src).parent.as_posix()
829
+ src_file = Path(src).name
830
+ other_args: list[str] = ["--no-modtime", "--vfs-read-wait", "1s"]
831
+ unit_chunk_size = length
832
+ vfs_read_chunk_size = unit_chunk_size
833
+ vfs_read_chunk_size_limit = length
834
+ vfs_read_chunk_streams = transfers
835
+ vfs_disk_space_total_size = length
836
+ other_args += ["--vfs-read-chunk-size", str(vfs_read_chunk_size)]
837
+ other_args += ["--vfs-read-chunk-size-limit", str(vfs_read_chunk_size_limit)]
838
+ other_args += ["--vfs-read-chunk-streams", str(vfs_read_chunk_streams)]
839
+ other_args += ["--vfs-disk-space-total-size", str(vfs_disk_space_total_size)]
840
+ other_args += ["--read-only"]
841
+ other_args += ["--direct-io"]
842
+
843
+ try:
844
+ # use scoped mount to do the read, then write the bytes to the destination
845
+ with self.scoped_mount(
846
+ src_parent_path,
847
+ tmp_mnt,
848
+ use_links=True,
849
+ verbose=False,
850
+ vfs_cache_mode="minimal",
851
+ other_args=other_args,
852
+ ):
853
+ src_file_mnt = tmp_mnt / src_file
854
+ with open(src_file_mnt, "rb") as f:
855
+ f.seek(offset)
856
+ data = f.read(length)
857
+ if outfile is None:
858
+ return data
859
+ with open(outfile, "wb") as out:
860
+ out.write(data)
861
+ del data
862
+ return bytes(0)
863
+
864
+ except Exception as e:
865
+ return e
866
+
816
867
  def copy_dir(
817
868
  self, src: str | Dir, dst: str | Dir, args: list[str] | None = None
818
869
  ) -> CompletedProcess:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.1.28
3
+ Version: 1.1.30
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -62,6 +62,7 @@ src/rclone_api/s3/types.py
62
62
  src/rclone_api/s3/upload_file_multipart.py
63
63
  tests/test_cmd_list_files.py
64
64
  tests/test_copy.py
65
+ tests/test_copy_bytes.py
65
66
  tests/test_copy_file_resumable_s3.py
66
67
  tests/test_copy_files.py
67
68
  tests/test_diff.py
@@ -0,0 +1,97 @@
1
+ """
2
+ Unit test file.
3
+ """
4
+
5
+ import os
6
+ import tempfile
7
+ import unittest
8
+ from pathlib import Path
9
+
10
+ from dotenv import load_dotenv
11
+
12
+ from rclone_api import Config, Rclone
13
+
14
+ load_dotenv()
15
+
16
+ BUCKET_NAME = os.getenv("BUCKET_NAME") # Default if not in .env
17
+
18
+
19
+ def _generate_rclone_config() -> Config:
20
+
21
+ # BUCKET_NAME = os.getenv("BUCKET_NAME", "TorrentBooks") # Default if not in .env
22
+
23
+ # Load additional environment variables
24
+ BUCKET_KEY_SECRET = os.getenv("BUCKET_KEY_SECRET")
25
+ BUCKET_KEY_PUBLIC = os.getenv("BUCKET_KEY_PUBLIC")
26
+ # BUCKET_URL = os.getenv("BUCKET_URL")
27
+ BUCKET_URL = "sfo3.digitaloceanspaces.com"
28
+
29
+ config_text = f"""
30
+ [dst]
31
+ type = s3
32
+ provider = DigitalOcean
33
+ access_key_id = {BUCKET_KEY_PUBLIC}
34
+ secret_access_key = {BUCKET_KEY_SECRET}
35
+ endpoint = {BUCKET_URL}
36
+ """
37
+
38
+ out = Config(config_text)
39
+ return out
40
+
41
+
42
+ class RcloneCopyBytesTester(unittest.TestCase):
43
+ """Test rclone functionality."""
44
+
45
+ def setUp(self) -> None:
46
+ """Check if all required environment variables are set before running tests."""
47
+ required_vars = [
48
+ "BUCKET_NAME",
49
+ "BUCKET_KEY_SECRET",
50
+ "BUCKET_KEY_PUBLIC",
51
+ "BUCKET_URL",
52
+ ]
53
+ missing = [var for var in required_vars if not os.getenv(var)]
54
+ if missing:
55
+ self.skipTest(
56
+ f"Missing required environment variables: {', '.join(missing)}"
57
+ )
58
+ os.environ["RCLONE_API_VERBOSE"] = "1"
59
+
60
+ def test_copy_bytes(self) -> None:
61
+ rclone = Rclone(_generate_rclone_config())
62
+ bytes_or_err: bytes | Exception = rclone.copy_bytes(
63
+ src="dst:rclone-api-unit-test/zachs_video/breaking_ai_mind.mp4",
64
+ offset=0,
65
+ length=1024 * 1024,
66
+ )
67
+ if isinstance(bytes_or_err, Exception):
68
+ print(bytes_or_err)
69
+ self.fail(f"Error: {bytes_or_err}")
70
+ assert isinstance(bytes_or_err, bytes)
71
+ self.assertEqual(
72
+ len(bytes_or_err), 1024 * 1024
73
+ ) # , f"Length: {len(bytes_or_err)}"
74
+
75
+ def test_copy_bytes_to_temp_file(self) -> None:
76
+
77
+ with tempfile.TemporaryDirectory() as tmpdir:
78
+ tmp = Path(tmpdir) / "tmp.mp4"
79
+ rclone = Rclone(_generate_rclone_config())
80
+ bytes_or_err: bytes | Exception = rclone.copy_bytes(
81
+ src="dst:rclone-api-unit-test/zachs_video/breaking_ai_mind.mp4",
82
+ offset=0,
83
+ length=1024 * 1024,
84
+ outfile=tmp,
85
+ )
86
+ if isinstance(bytes_or_err, Exception):
87
+ print(bytes_or_err)
88
+ self.fail(f"Error: {bytes_or_err}")
89
+ assert isinstance(bytes_or_err, bytes)
90
+ self.assertEqual(len(bytes_or_err), 0)
91
+ self.assertTrue(tmp.exists())
92
+ tmp_size = tmp.stat().st_size
93
+ self.assertEqual(tmp_size, 1024 * 1024)
94
+
95
+
96
+ if __name__ == "__main__":
97
+ unittest.main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes