b2-cleanup 0.1.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.
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: b2-cleanup
3
+ Version: 0.1.0
4
+ Summary: Cleanup unfinished Backblaze B2 large uploads
5
+ Author-email: Jeroen Verhoeven <jeroen@joentje.org>
6
+ Requires-Dist: click>=8.0
7
+ Requires-Dist: b2sdk>=2.8.1
@@ -0,0 +1,6 @@
1
+ cleanup_unfinished_b2_uploads.py,sha256=_wShubS02qNxiRdJmuNSoLzcjd7epcB3Q_fU1CDpH_Q,3877
2
+ b2_cleanup-0.1.0.dist-info/METADATA,sha256=kHW0eHyzVtwFoYT9WNbG5g2HKJ6bESQ7YnXD4wLN1XY,215
3
+ b2_cleanup-0.1.0.dist-info/WHEEL,sha256=ooBFpIzZCPdw3uqIQsOo4qqbA4ZRPxHnOH7peeONza0,91
4
+ b2_cleanup-0.1.0.dist-info/entry_points.txt,sha256=hRMR8HC39r-P1TrTPYbH3hDQkBVvDb508tOUS8a0CcI,65
5
+ b2_cleanup-0.1.0.dist-info/top_level.txt,sha256=GmKi68dORi-iv2h2sT5Uf7Q3udlX1mElQQg50cqqF6o,30
6
+ b2_cleanup-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ b2-cleanup = cleanup_unfinished_b2_uploads:cli
@@ -0,0 +1 @@
1
+ cleanup_unfinished_b2_uploads
@@ -0,0 +1,106 @@
1
+ import os
2
+ import json
3
+ import subprocess
4
+ import logging
5
+ import click
6
+ from b2sdk.v2 import InMemoryAccountInfo, B2Api
7
+
8
+
9
+ class B2CleanupTool:
10
+ def __init__(
11
+ self,
12
+ dry_run: bool = False,
13
+ override_key_id: str = None,
14
+ override_key: str = None,
15
+ ):
16
+ self.dry_run = dry_run
17
+ self.logger = logging.getLogger("B2Cleanup")
18
+ self.api = self._authorize(override_key_id, override_key)
19
+
20
+ def _authorize(self, override_key_id=None, override_key=None):
21
+ info = InMemoryAccountInfo()
22
+ api = B2Api(info)
23
+
24
+ if override_key_id and override_key:
25
+ self.logger.info("🔐 Using credentials from CLI override.")
26
+ api.authorize_account("production", override_key_id, override_key)
27
+ return api
28
+
29
+ key_id = os.getenv("B2_APPLICATION_KEY_ID")
30
+ app_key = os.getenv("B2_APPLICATION_KEY")
31
+
32
+ if key_id and app_key:
33
+ self.logger.info("🔐 Using credentials from environment variables.")
34
+ api.authorize_account("production", key_id, app_key)
35
+ return api
36
+
37
+ try:
38
+ self.logger.info("🔍 Trying to load credentials via `b2 account get`...")
39
+ result = subprocess.run(
40
+ ["b2", "account", "get"],
41
+ check=True,
42
+ capture_output=True,
43
+ text=True,
44
+ )
45
+ creds = json.loads(result.stdout)
46
+ key_id = creds["applicationKeyId"]
47
+ app_key = creds["applicationKey"]
48
+ api.authorize_account("production", key_id, app_key)
49
+ self.logger.info("✅ Authorized with B2 CLI credentials.")
50
+ return api
51
+
52
+ except (subprocess.CalledProcessError, KeyError, json.JSONDecodeError) as e:
53
+ self.logger.error(
54
+ "❌ Failed to get B2 credentials from CLI or environment: %s", e
55
+ )
56
+ raise RuntimeError("Could not authorize with Backblaze B2.")
57
+
58
+ def cleanup_unfinished_uploads(self, bucket_name: str):
59
+ bucket = self.api.get_bucket_by_name(bucket_name)
60
+ unfinished = list(bucket.list_unfinished_large_files())
61
+ if not unfinished:
62
+ self.logger.info("✅ No unfinished large files found.")
63
+ return
64
+
65
+ self.logger.info("🗃️ Found %d unfinished uploads", len(unfinished))
66
+ for file_version in unfinished:
67
+ file_id = file_version.id_
68
+ file_name = file_version.file_name
69
+ if self.dry_run:
70
+ self.logger.info(f"💡 Dry run: would cancel {file_id} ({file_name})")
71
+ else:
72
+ self.logger.info(f"🗑️ Cancelling {file_id} ({file_name})")
73
+ self.api.cancel_large_file(file_id)
74
+
75
+
76
+ @click.command()
77
+ @click.argument("bucket")
78
+ @click.option(
79
+ "--dry-run", is_flag=True, help="Only list unfinished uploads, do not cancel."
80
+ )
81
+ @click.option("--log-file", default="b2_cleanup.log", help="Path to log file.")
82
+ @click.option("--key-id", help="Backblaze B2 applicationKeyId to override env/config.")
83
+ @click.option("--key", help="Backblaze B2 applicationKey to override env/config.")
84
+ def cli(bucket, dry_run, log_file, key_id, key):
85
+ """Clean up unfinished large file uploads in BUCKET."""
86
+ logger = logging.getLogger()
87
+ logger.setLevel(logging.INFO)
88
+ if logger.hasHandlers():
89
+ logger.handlers.clear()
90
+
91
+ file_handler = logging.FileHandler(log_file)
92
+ file_handler.setFormatter(
93
+ logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
94
+ )
95
+ logger.addHandler(file_handler)
96
+
97
+ stream_handler = logging.StreamHandler()
98
+ stream_handler.setFormatter(logging.Formatter("%(message)s"))
99
+ logger.addHandler(stream_handler)
100
+
101
+ tool = B2CleanupTool(dry_run=dry_run, override_key_id=key_id, override_key=key)
102
+ tool.cleanup_unfinished_uploads(bucket)
103
+
104
+
105
+ if __name__ == "__main__":
106
+ cli()