FileBackup 0.3.5__py3-none-any.whl → 0.4.0__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.
@@ -70,10 +70,12 @@ class FastGlacierDataStore(BulkStorageDataStore):
70
70
  )
71
71
  return
72
72
 
73
- with dm.Nested("Uploading to Fast Glacier...") as upload_dm:
74
- command_line = f'glacier-con upload "{self.account_name}" "{local_path / "*"}" "{self.aws_region}" "{self._glacier_dir.as_posix()}"'
73
+ self._validated_command_line = True
75
74
 
76
- upload_dm.WriteVerbose(f"Command Line: {command_line}\n\n")
75
+ with dm.Nested("Uploading to Fast Glacier...") as upload_dm:
76
+ command_line = f'glacier-con upload "{self.account_name}" "{local_path / "*"}" "{self.aws_region}" "{self._glacier_dir.as_posix()}"'
77
77
 
78
- with upload_dm.YieldStream() as stream:
79
- upload_dm.result = SubprocessEx.Stream(command_line, stream)
78
+ upload_dm.WriteVerbose(f"Command Line: {command_line}\n\n")
79
+
80
+ with upload_dm.YieldStream() as stream:
81
+ upload_dm.result = SubprocessEx.Stream(command_line, stream)
@@ -0,0 +1,81 @@
1
+ # ----------------------------------------------------------------------
2
+ # |
3
+ # | S3BrowserDataStore.py
4
+ # |
5
+ # | David Brownell <db@DavidBrownell.com>
6
+ # | 2025-02-23 14:08:12
7
+ # |
8
+ # ----------------------------------------------------------------------
9
+ # |
10
+ # | Copyright David Brownell 2025
11
+ # | Distributed under the MIT License.
12
+ # |
13
+ # ----------------------------------------------------------------------
14
+ """Contains the S3BrowserDataStore object"""
15
+
16
+ from pathlib import Path
17
+ from typing import Optional
18
+
19
+ from dbrownell_Common.Streams.DoneManager import DoneManager # type: ignore[import-untyped]
20
+ from dbrownell_Common import SubprocessEx # type: ignore[import-untyped]
21
+ from dbrownell_Common.Types import override # type: ignore[import-untyped]
22
+
23
+ from FileBackup.DataStore.Interfaces.BulkStorageDataStore import BulkStorageDataStore
24
+
25
+
26
+ # ----------------------------------------------------------------------
27
+ class S3BrowserDataStore(BulkStorageDataStore):
28
+ """Data store that uses the S3 Browser application (https://s3browser.com/)"""
29
+
30
+ # ----------------------------------------------------------------------
31
+ def __init__(
32
+ self,
33
+ account_name: str,
34
+ bucket_name: str,
35
+ s3_dir: Optional[Path],
36
+ ) -> None:
37
+ super(S3BrowserDataStore, self).__init__()
38
+
39
+ self.account_name = account_name
40
+ self.bucket_name = bucket_name
41
+
42
+ self._s3_dir = self.bucket_name / (s3_dir or Path())
43
+
44
+ self._validated_command_line = False
45
+
46
+ # ----------------------------------------------------------------------
47
+ @override
48
+ def ExecuteInParallel(self) -> bool:
49
+ return False
50
+
51
+ # ----------------------------------------------------------------------
52
+ @override
53
+ def Upload(
54
+ self,
55
+ dm: DoneManager,
56
+ local_path: Path,
57
+ ) -> None:
58
+ if self._validated_command_line is False:
59
+ with dm.Nested(
60
+ "Validating S3 Browser on the command line...",
61
+ suffix="\n",
62
+ ) as check_dm:
63
+ result = SubprocessEx.Run("s3browser-cli license show")
64
+
65
+ check_dm.WriteVerbose(result.output)
66
+
67
+ if result.returncode != 0:
68
+ check_dm.WriteError(
69
+ "S3 Browser is not available; please make sure it exists in the path and run the script again.\n"
70
+ )
71
+ return
72
+
73
+ self._validated_command_line = True
74
+
75
+ with dm.Nested("Uploading via S3 Browser...") as upload_dm:
76
+ command_line = f's3browser-cli file upload "{self.account_name}" "{local_path / "*"}" "{self._s3_dir.as_posix()}"'
77
+
78
+ upload_dm.WriteVerbose(f"Command Line: {command_line}\n\n")
79
+
80
+ with upload_dm.YieldStream() as stream:
81
+ upload_dm.result = SubprocessEx.Stream(command_line, stream)
FileBackup/Impl/Common.py CHANGED
@@ -22,7 +22,16 @@ from contextlib import contextmanager
22
22
  from dataclasses import dataclass, field
23
23
  from enum import auto, Enum
24
24
  from pathlib import Path
25
- from typing import Any, Callable, cast, Iterable, Iterator, Optional, Pattern, TYPE_CHECKING
25
+ from typing import (
26
+ Any,
27
+ Callable,
28
+ cast,
29
+ Iterable,
30
+ Iterator,
31
+ Optional,
32
+ Pattern,
33
+ TYPE_CHECKING,
34
+ )
26
35
  from urllib import parse as urlparse
27
36
 
28
37
  from dbrownell_Common import ExecuteTasks # type: ignore[import-untyped]
@@ -35,6 +44,7 @@ from FileBackup.DataStore.FastGlacierDataStore import FastGlacierDataStore
35
44
  from FileBackup.DataStore.FileSystemDataStore import FileSystemDataStore
36
45
  from FileBackup.DataStore.Interfaces.DataStore import DataStore, ItemType
37
46
  from FileBackup.DataStore.Interfaces.FileBasedDataStore import FileBasedDataStore
47
+ from FileBackup.DataStore.S3BrowserDataStore import S3BrowserDataStore
38
48
  from FileBackup.DataStore.SFTPDataStore import SFTPDataStore, SSH_PORT
39
49
 
40
50
  if TYPE_CHECKING:
@@ -79,6 +89,22 @@ FAST_GLACIER_TEMPLATE_REGEX = re.compile(
79
89
  [sep] )\/(?#
80
90
  Posix Working Dir )(?P<working_dir>.+)(?#
81
91
  Working Dir End ))?(?#
92
+ End )$(?#
93
+ )""",
94
+ )
95
+
96
+
97
+ S3_BROWSER_TEMPLATE_REGEX = re.compile(
98
+ r"""(?#
99
+ Start )^(?#
100
+ Prefix )s3_browser:\/\/(?#
101
+ Account Name )(?P<account_name>[^@]+)(?#
102
+ [sep] )@(?#
103
+ Bucket Name )(?P<bucket_name>[^\/]+)(?#
104
+ Working Dir Begin )(?:(?#
105
+ Posix Working Dir )(?P<working_dir>.+)(?#
106
+ Working Dir End ))?(?#
107
+ End )$(?#
82
108
  )""",
83
109
  )
84
110
 
@@ -288,6 +314,17 @@ def GetDestinationHelp() -> str:
288
314
  fast_glacier://MyFastGlacierAccount@us-west-2
289
315
  fast_glacier://MyFastGlacierAccount@us-west-2/Glacier/Dir
290
316
 
317
+ S3 Browser
318
+ ----------
319
+ Write content using the S3 Browser application (https://s3browser.com/).
320
+
321
+ Format:
322
+ s3_browser://<s3_browser_account_name>@<bucket_name>[/<working_dir>]
323
+
324
+ Examples:
325
+ s3_browser://MyS3BrowserAccount@MyBucket
326
+ s3_browser://MyS3BrowserAccount@MyBucket/A/Working/Dir
327
+
291
328
  """,
292
329
  ).replace("\n", "\n\n")
293
330
 
@@ -337,6 +374,16 @@ def YieldDataStore(
337
374
  )
338
375
  return
339
376
 
377
+ # S3 Browser
378
+ s3_browser_match = S3_BROWSER_TEMPLATE_REGEX.match(destination)
379
+ if s3_browser_match:
380
+ yield S3BrowserDataStore(
381
+ s3_browser_match.group("account_name"),
382
+ s3_browser_match.group("bucket_name"),
383
+ Path(s3_browser_match.group("working_dir") or ""),
384
+ )
385
+ return
386
+
340
387
  # Create a FileSystemDataStore instance
341
388
  is_local_filesystem_override_value_for_testing: Optional[bool] = None
342
389
 
FileBackup/__init__.py CHANGED
@@ -9,4 +9,4 @@
9
9
  # Note that this value will be overwritten by calls to `python ../../Build.py update_version` based
10
10
  # on changes observed in the git repository. The default value below will be used until the value
11
11
  # here is explicitly updated by the Continuous Integration system as part of a commit.
12
- __version__ = "0.3.5"
12
+ __version__ = "0.4.0"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: FileBackup
3
- Version: 0.3.5
3
+ Version: 0.4.0
4
4
  Summary: Tool for creating and restoring file system backups.
5
5
  Author-email: David Brownell <github@DavidBrownell.com>
6
6
  License: MIT
@@ -28,15 +28,15 @@ Classifier: Topic :: System :: Systems Administration
28
28
  Classifier: Topic :: Utilities
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE.txt
31
- Requires-Dist: dbrownell-Common
32
- Requires-Dist: paramiko ~=3.4
33
- Requires-Dist: typer ~=0.9
31
+ Requires-Dist: dbrownell_Common
32
+ Requires-Dist: paramiko~=3.4
33
+ Requires-Dist: typer~=0.9
34
34
  Provides-Extra: dev
35
- Requires-Dist: dbrownell-DevTools ; extra == 'dev'
35
+ Requires-Dist: dbrownell_DevTools; extra == "dev"
36
36
  Provides-Extra: package
37
- Requires-Dist: build ~=1.2 ; extra == 'package'
38
- Requires-Dist: cx-Freeze ~=6.15 ; extra == 'package'
39
- Requires-Dist: twine ~=5.1 ; extra == 'package'
37
+ Requires-Dist: build~=1.2; extra == "package"
38
+ Requires-Dist: cx_Freeze~=6.15; extra == "package"
39
+ Requires-Dist: twine~=5.1; extra == "package"
40
40
 
41
41
  # FileBackup
42
42
 
@@ -57,9 +57,9 @@ TODO: Complete this section
57
57
  ## Additional Information
58
58
  Additional information can be found at these locations.
59
59
 
60
+ <!-- [BEGIN] Additional Information -->
60
61
  | Title | Document | Description |
61
62
  | --- | --- | --- |
62
- <!-- [BEGIN] Additional Information -->
63
63
  | Code of Conduct | [CODE_OF_CONDUCT.md](https://github.com/davidbrownell/FileBackup/blob/main/CODE_OF_CONDUCT.md) | Information about the the norms, rules, and responsibilities we adhere to when participating in this open source community. |
64
64
  | Contributing | [CONTRIBUTING.md](https://github.com/davidbrownell/FileBackup/blob/main/CONTRIBUTING.md) | Information about contributing code changes to this project. |
65
65
  | Development | [DEVELOPMENT.md](https://github.com/davidbrownell/FileBackup/blob/main/DEVELOPMENT.md) | Information about development activities involved in making changes to this project. |
@@ -1,25 +1,26 @@
1
1
  FileBackup/Mirror.py,sha256=FA9vfvPHOJzmN__Tx09ndWMeMTSWcED52BuVfeOBuKc,23904
2
2
  FileBackup/Offsite.py,sha256=2kK5ToJIHXIAPChtREcztp1e1L95ZJF6adIk-8d5V8g,62796
3
3
  FileBackup/Snapshot.py,sha256=r6l35Tl75WIQALtzN81r62XNkzJiTddzuihmlSb56Aw,27336
4
- FileBackup/__init__.py,sha256=PdLwrWcELNRlsgjQiRpRt_tOyJbhvspDa1_zkQ1G_V4,597
4
+ FileBackup/__init__.py,sha256=HCvFRVx_mOm1khMXjjxM4UX5ljGM23mVGc5wKNrJRJY,597
5
5
  FileBackup/CommandLine/CommandLineArguments.py,sha256=79K4jVxtZ8xk1TfSJT8KIdkqZFjCEAspoIKP21GUXxE,2410
6
6
  FileBackup/CommandLine/EntryPoint.py,sha256=Fn62wtKV6uiTJ1dQpolNFUyWWeUOKKgetOFSXPgoe-w,1813
7
7
  FileBackup/CommandLine/MirrorEntryPoint.py,sha256=ZQ3mz0AgBwwn7n9uOuxN-lEQvm22u8SQbpWASkvRsAA,6800
8
8
  FileBackup/CommandLine/OffsiteEntryPoint.py,sha256=enbgeD_nhdR2T6UufySt33f4P2FnOVzsHiEktyVtLlo,12528
9
9
  FileBackup/CommandLine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- FileBackup/DataStore/FastGlacierDataStore.py,sha256=YxmYY0IVZ6HTFE_2zMTz4g09Z9jJcXM9sMu_Yp86DAg,2982
10
+ FileBackup/DataStore/FastGlacierDataStore.py,sha256=5J4W9FqB92hnHoRoZLo-DfMQ6uyjaOAWWrnuc0pvnJo,3017
11
11
  FileBackup/DataStore/FileSystemDataStore.py,sha256=XUvIQe2Yl-W6OPfvsJlPzDPsGclDOkKlozO0GJ-05bo,6791
12
+ FileBackup/DataStore/S3BrowserDataStore.py,sha256=3NHl-advSbyXuL1kDu1wswbcAZbhmAGDu8yzlOuY5Us,2954
12
13
  FileBackup/DataStore/SFTPDataStore.py,sha256=TKz-hwwHg4avxKHDFRRz7FlKx3d40wT15ug1gCpu6VU,11374
13
14
  FileBackup/DataStore/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
15
  FileBackup/DataStore/Interfaces/BulkStorageDataStore.py,sha256=qZo92nPDP_aVTxlohsc1ySnU9Skgxgo7Mj_tT0KxFZ8,1310
15
16
  FileBackup/DataStore/Interfaces/DataStore.py,sha256=FpQmxoqBS5indJYRxibkSXHTgk7U6HFyViI0BDqCcVw,1169
16
17
  FileBackup/DataStore/Interfaces/FileBasedDataStore.py,sha256=eMA_cQ-CED83z5uMDQZ18UFzBRvaXOwWz67kVu-2ftM,5307
17
18
  FileBackup/DataStore/Interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- FileBackup/Impl/Common.py,sha256=8XV0ARuLLci8AGjvW2lfJyXbA2FnyRpUHel4AI-7ReM,23712
19
+ FileBackup/Impl/Common.py,sha256=L444spBwjUULSQcAGYV-90dHRFNgglC7k962NshpIJ8,25261
19
20
  FileBackup/Impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- FileBackup-0.3.5.dist-info/LICENSE.txt,sha256=6ycKI8SWG920Saey4PGi5YUVLX0C_8hfqJr7Q0sqGI8,1071
21
- FileBackup-0.3.5.dist-info/METADATA,sha256=FJzSVtumCyGZ3rAxtBVwi4ZN_3T-7jg9Hd-qv-Z5_JA,3300
22
- FileBackup-0.3.5.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
23
- FileBackup-0.3.5.dist-info/entry_points.txt,sha256=OlHrvdhWJQGIp4vCUJAqOWxW7y0ZH59qQ-2ImWFUcPI,69
24
- FileBackup-0.3.5.dist-info/top_level.txt,sha256=mXd2b6GEOpQCnmzgYEb6c_rfwlgP3RgmMuVDkQasxKw,11
25
- FileBackup-0.3.5.dist-info/RECORD,,
21
+ FileBackup-0.4.0.dist-info/LICENSE.txt,sha256=6ycKI8SWG920Saey4PGi5YUVLX0C_8hfqJr7Q0sqGI8,1071
22
+ FileBackup-0.4.0.dist-info/METADATA,sha256=QemMImLG2dvbvLt8t68rCA9YxYwWIdjtoL6cSTpkwo0,3291
23
+ FileBackup-0.4.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
24
+ FileBackup-0.4.0.dist-info/entry_points.txt,sha256=OlHrvdhWJQGIp4vCUJAqOWxW7y0ZH59qQ-2ImWFUcPI,69
25
+ FileBackup-0.4.0.dist-info/top_level.txt,sha256=mXd2b6GEOpQCnmzgYEb6c_rfwlgP3RgmMuVDkQasxKw,11
26
+ FileBackup-0.4.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (74.1.2)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5