playground-ls-cli 4.14.1.dev8__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.
Files changed (112) hide show
  1. localstack_cli/__init__.py +0 -0
  2. localstack_cli/cli/__init__.py +10 -0
  3. localstack_cli/cli/console.py +11 -0
  4. localstack_cli/cli/core_plugin.py +12 -0
  5. localstack_cli/cli/exceptions.py +19 -0
  6. localstack_cli/cli/localstack.py +951 -0
  7. localstack_cli/cli/lpm.py +138 -0
  8. localstack_cli/cli/main.py +22 -0
  9. localstack_cli/cli/plugin.py +39 -0
  10. localstack_cli/cli/plugins.py +134 -0
  11. localstack_cli/cli/profiles.py +65 -0
  12. localstack_cli/config.py +1689 -0
  13. localstack_cli/constants.py +165 -0
  14. localstack_cli/logging/__init__.py +0 -0
  15. localstack_cli/logging/format.py +194 -0
  16. localstack_cli/logging/setup.py +142 -0
  17. localstack_cli/packages/__init__.py +25 -0
  18. localstack_cli/packages/api.py +418 -0
  19. localstack_cli/packages/core.py +416 -0
  20. localstack_cli/pro/__init__.py +0 -0
  21. localstack_cli/pro/core/__init__.py +0 -0
  22. localstack_cli/pro/core/bootstrap/__init__.py +1 -0
  23. localstack_cli/pro/core/bootstrap/auth.py +213 -0
  24. localstack_cli/pro/core/bootstrap/dns_utils.py +55 -0
  25. localstack_cli/pro/core/bootstrap/entitlements.py +117 -0
  26. localstack_cli/pro/core/bootstrap/extensions/__init__.py +3 -0
  27. localstack_cli/pro/core/bootstrap/extensions/__main__.py +106 -0
  28. localstack_cli/pro/core/bootstrap/extensions/autoinstall.py +63 -0
  29. localstack_cli/pro/core/bootstrap/extensions/bootstrap.py +97 -0
  30. localstack_cli/pro/core/bootstrap/extensions/repository.py +374 -0
  31. localstack_cli/pro/core/bootstrap/licensingv2.py +1259 -0
  32. localstack_cli/pro/core/bootstrap/pods/__init__.py +0 -0
  33. localstack_cli/pro/core/bootstrap/pods/api_types.py +17 -0
  34. localstack_cli/pro/core/bootstrap/pods/constants.py +26 -0
  35. localstack_cli/pro/core/bootstrap/pods/remotes/__init__.py +0 -0
  36. localstack_cli/pro/core/bootstrap/pods/remotes/api.py +75 -0
  37. localstack_cli/pro/core/bootstrap/pods/remotes/configs.py +69 -0
  38. localstack_cli/pro/core/bootstrap/pods/remotes/params.py +86 -0
  39. localstack_cli/pro/core/bootstrap/pods_client.py +834 -0
  40. localstack_cli/pro/core/cli/__init__.py +0 -0
  41. localstack_cli/pro/core/cli/auth.py +226 -0
  42. localstack_cli/pro/core/cli/aws.py +16 -0
  43. localstack_cli/pro/core/cli/cli.py +99 -0
  44. localstack_cli/pro/core/cli/click_utils.py +21 -0
  45. localstack_cli/pro/core/cli/cloud_pods.py +465 -0
  46. localstack_cli/pro/core/cli/diff_view.py +41 -0
  47. localstack_cli/pro/core/cli/ephemeral.py +199 -0
  48. localstack_cli/pro/core/cli/extensions.py +492 -0
  49. localstack_cli/pro/core/cli/iam.py +180 -0
  50. localstack_cli/pro/core/cli/license.py +90 -0
  51. localstack_cli/pro/core/cli/localstack.py +118 -0
  52. localstack_cli/pro/core/cli/replicator.py +378 -0
  53. localstack_cli/pro/core/cli/state.py +183 -0
  54. localstack_cli/pro/core/cli/tree_view.py +235 -0
  55. localstack_cli/pro/core/config.py +556 -0
  56. localstack_cli/pro/core/constants.py +54 -0
  57. localstack_cli/pro/core/plugins.py +169 -0
  58. localstack_cli/runtime/__init__.py +6 -0
  59. localstack_cli/runtime/exceptions.py +7 -0
  60. localstack_cli/runtime/hooks.py +73 -0
  61. localstack_cli/testing/__init__.py +1 -0
  62. localstack_cli/testing/config.py +4 -0
  63. localstack_cli/utils/__init__.py +0 -0
  64. localstack_cli/utils/analytics/__init__.py +12 -0
  65. localstack_cli/utils/analytics/cli.py +67 -0
  66. localstack_cli/utils/analytics/client.py +111 -0
  67. localstack_cli/utils/analytics/events.py +30 -0
  68. localstack_cli/utils/analytics/logger.py +48 -0
  69. localstack_cli/utils/analytics/metadata.py +250 -0
  70. localstack_cli/utils/analytics/publisher.py +160 -0
  71. localstack_cli/utils/analytics/service_request_aggregator.py +133 -0
  72. localstack_cli/utils/archives.py +271 -0
  73. localstack_cli/utils/batching.py +258 -0
  74. localstack_cli/utils/bootstrap.py +1418 -0
  75. localstack_cli/utils/checksum.py +313 -0
  76. localstack_cli/utils/collections.py +554 -0
  77. localstack_cli/utils/common.py +229 -0
  78. localstack_cli/utils/container_networking.py +142 -0
  79. localstack_cli/utils/container_utils/__init__.py +0 -0
  80. localstack_cli/utils/container_utils/container_client.py +1585 -0
  81. localstack_cli/utils/container_utils/docker_cmd_client.py +987 -0
  82. localstack_cli/utils/container_utils/docker_sdk_client.py +1018 -0
  83. localstack_cli/utils/crypto.py +294 -0
  84. localstack_cli/utils/docker_utils.py +272 -0
  85. localstack_cli/utils/files.py +327 -0
  86. localstack_cli/utils/functions.py +92 -0
  87. localstack_cli/utils/http.py +326 -0
  88. localstack_cli/utils/json.py +219 -0
  89. localstack_cli/utils/net.py +516 -0
  90. localstack_cli/utils/no_exit_argument_parser.py +19 -0
  91. localstack_cli/utils/numbers.py +49 -0
  92. localstack_cli/utils/objects.py +235 -0
  93. localstack_cli/utils/patch.py +260 -0
  94. localstack_cli/utils/platform.py +77 -0
  95. localstack_cli/utils/run.py +514 -0
  96. localstack_cli/utils/server/__init__.py +0 -0
  97. localstack_cli/utils/server/tcp_proxy.py +108 -0
  98. localstack_cli/utils/serving.py +187 -0
  99. localstack_cli/utils/ssl.py +71 -0
  100. localstack_cli/utils/strings.py +245 -0
  101. localstack_cli/utils/sync.py +267 -0
  102. localstack_cli/utils/threads.py +163 -0
  103. localstack_cli/utils/time.py +81 -0
  104. localstack_cli/utils/urls.py +21 -0
  105. localstack_cli/utils/venv.py +100 -0
  106. localstack_cli/utils/xml.py +41 -0
  107. localstack_cli/version.py +34 -0
  108. playground_ls_cli-4.14.1.dev8.dist-info/METADATA +95 -0
  109. playground_ls_cli-4.14.1.dev8.dist-info/RECORD +112 -0
  110. playground_ls_cli-4.14.1.dev8.dist-info/WHEEL +5 -0
  111. playground_ls_cli-4.14.1.dev8.dist-info/entry_points.txt +17 -0
  112. playground_ls_cli-4.14.1.dev8.dist-info/top_level.txt +1 -0
@@ -0,0 +1,313 @@
1
+ import hashlib
2
+ import logging
3
+ import os
4
+ import re
5
+ import tempfile
6
+ from abc import ABC, abstractmethod
7
+
8
+ from localstack_cli.utils.files import load_file, rm_rf
9
+
10
+ # Setup logger
11
+ LOG = logging.getLogger(__name__)
12
+
13
+
14
+ class ChecksumException(Exception):
15
+ """Base exception for checksum errors."""
16
+
17
+ pass
18
+
19
+
20
+ class ChecksumFormat(ABC):
21
+ """Abstract base class for checksum format parsers."""
22
+
23
+ @abstractmethod
24
+ def can_parse(self, content: str) -> bool:
25
+ """
26
+ Check if this parser can handle the given content.
27
+
28
+ :param content: The content to check
29
+ :return: True if parser can handle content, False otherwise
30
+ """
31
+ pass
32
+
33
+ @abstractmethod
34
+ def parse(self, content: str) -> dict[str, str]:
35
+ """
36
+ Parse the content and return filename to checksum mapping.
37
+
38
+ :param content: The content to parse
39
+ :return: Dictionary mapping filenames to checksums
40
+ """
41
+ pass
42
+
43
+
44
+ class StandardFormat(ChecksumFormat):
45
+ """
46
+ Handles standard checksum format.
47
+
48
+ Supports formats like:
49
+
50
+ * ``checksum filename``
51
+ * ``checksum *filename``
52
+ """
53
+
54
+ def can_parse(self, content: str) -> bool:
55
+ lines = content.strip().split("\n")
56
+ for line in lines[:5]: # Check first 5 lines
57
+ if re.match(r"^[a-fA-F0-9]{32,128}\s+\S+", line.strip()):
58
+ return True
59
+ return False
60
+
61
+ def parse(self, content: str) -> dict[str, str]:
62
+ checksums = {}
63
+ for line in content.strip().split("\n"):
64
+ line = line.strip()
65
+ if not line or line.startswith("#"):
66
+ continue
67
+
68
+ # Match: checksum (whitespace) filename
69
+ match = re.match(r"^([a-fA-F0-9]{32,128})\s+(\*?)(.+)$", line)
70
+ if match:
71
+ checksum, star, filename = match.groups()
72
+ checksums[filename.strip()] = checksum.lower()
73
+
74
+ return checksums
75
+
76
+
77
+ class BSDFormat(ChecksumFormat):
78
+ """
79
+ Handles BSD-style checksum format.
80
+
81
+ Format: ``SHA512 (filename) = checksum``
82
+ """
83
+
84
+ def can_parse(self, content: str) -> bool:
85
+ lines = content.strip().split("\n")
86
+ for line in lines[:5]:
87
+ if re.match(r"^(MD5|SHA1|SHA256|SHA512)\s*\(.+\)\s*=\s*[a-fA-F0-9]+", line):
88
+ return True
89
+ return False
90
+
91
+ def parse(self, content: str) -> dict[str, str]:
92
+ checksums = {}
93
+ for line in content.strip().split("\n"):
94
+ line = line.strip()
95
+ if not line:
96
+ continue
97
+
98
+ # Match: ALGORITHM (filename) = checksum
99
+ match = re.match(r"^(MD5|SHA1|SHA256|SHA512)\s*\((.+)\)\s*=\s*([a-fA-F0-9]+)$", line)
100
+ if match:
101
+ algo, filename, checksum = match.groups()
102
+ checksums[filename.strip()] = checksum.lower()
103
+
104
+ return checksums
105
+
106
+
107
+ class ApacheBSDFormat(ChecksumFormat):
108
+ """
109
+ Handles Apache's BSD-style format with split checksums.
110
+
111
+ Format::
112
+
113
+ filename: CHECKSUM_PART1
114
+ CHECKSUM_PART2
115
+ CHECKSUM_PART3
116
+ """
117
+
118
+ def can_parse(self, content: str) -> bool:
119
+ lines = content.strip().split("\n")
120
+ if lines and ":" in lines[0]:
121
+ # Check if it looks like filename: hex_data
122
+ parts = lines[0].split(":", 1)
123
+ if len(parts) == 2 and re.search(r"[a-fA-F0-9\s]+", parts[1]):
124
+ return True
125
+ return False
126
+
127
+ def parse(self, content: str) -> dict[str, str]:
128
+ checksums = {}
129
+ lines = content.strip().split("\n")
130
+
131
+ current_file = None
132
+ checksum_parts = []
133
+
134
+ for line in lines:
135
+ if ":" in line and not line.startswith(" "):
136
+ # New file entry
137
+ if current_file and checksum_parts:
138
+ # Save previous file's checksum
139
+ full_checksum = "".join(checksum_parts).replace(" ", "").lower()
140
+ if re.match(r"^[a-fA-F0-9]+$", full_checksum):
141
+ checksums[current_file] = full_checksum
142
+
143
+ # Start new file
144
+ parts = line.split(":", 1)
145
+ current_file = parts[0].strip()
146
+ checksum_part = parts[1].strip()
147
+ checksum_parts = [checksum_part]
148
+ elif line.strip() and current_file:
149
+ # Continuation of checksum
150
+ checksum_parts.append(line.strip())
151
+
152
+ # Don't forget the last file
153
+ if current_file and checksum_parts:
154
+ full_checksum = "".join(checksum_parts).replace(" ", "").lower()
155
+ if re.match(r"^[a-fA-F0-9]+$", full_checksum):
156
+ checksums[current_file] = full_checksum
157
+
158
+ return checksums
159
+
160
+
161
+ class ChecksumParser:
162
+ """Main parser that tries different checksum formats."""
163
+
164
+ def __init__(self):
165
+ """Initialize parser with available format parsers."""
166
+ self.formats = [
167
+ StandardFormat(),
168
+ BSDFormat(),
169
+ ApacheBSDFormat(),
170
+ ]
171
+
172
+ def parse(self, content: str) -> dict[str, str]:
173
+ """
174
+ Try each format parser until one works.
175
+
176
+ :param content: The content to parse
177
+ :return: Dictionary mapping filenames to checksums
178
+ """
179
+ for format_parser in self.formats:
180
+ if format_parser.can_parse(content):
181
+ result = format_parser.parse(content)
182
+ if result:
183
+ return result
184
+
185
+ return {}
186
+
187
+
188
+ def parse_checksum_file_from_url(checksum_url: str) -> dict[str, str]:
189
+ """
190
+ Parse a SHA checksum file from a URL using multiple format parsers.
191
+
192
+ :param checksum_url: URL of the checksum file
193
+ :return: Dictionary mapping filenames to checksums
194
+ """
195
+ # import here to avoid circular dependency issues
196
+ from localstack.utils.http import download
197
+
198
+ checksum_name = os.path.basename(checksum_url)
199
+ checksum_path = os.path.join(tempfile.gettempdir(), checksum_name)
200
+ try:
201
+ download(checksum_url, checksum_path)
202
+ checksum_content = load_file(checksum_path)
203
+
204
+ parser = ChecksumParser()
205
+ checksums = parser.parse(checksum_content)
206
+
207
+ return checksums
208
+ finally:
209
+ rm_rf(checksum_path)
210
+
211
+
212
+ def calculate_file_checksum(file_path: str, algorithm: str = "sha256") -> str:
213
+ """
214
+ Calculate checksum of a local file.
215
+
216
+ :param file_path: Path to the file
217
+ :param algorithm: Hash algorithm to use
218
+ :return: Calculated checksum as hexadecimal string
219
+
220
+ note: Supported algorithms: 'md5', 'sha1', 'sha256', 'sha512'
221
+ """
222
+ hash_func = getattr(hashlib, algorithm)()
223
+
224
+ with open(file_path, "rb") as f:
225
+ # Read file in chunks to handle large files efficiently
226
+ for chunk in iter(lambda: f.read(8192), b""):
227
+ hash_func.update(chunk)
228
+
229
+ return hash_func.hexdigest()
230
+
231
+
232
+ def verify_local_file_with_checksum_url(file_path: str, checksum_url: str, filename=None) -> bool:
233
+ """
234
+ Verify a local file against checksums from an online checksum file.
235
+
236
+ :param file_path: Path to the local file to verify
237
+ :param checksum_url: URL of the checksum file
238
+ :param filename: Filename to look for in checksum file (defaults to basename of file_path)
239
+ :return: True if verification succeeds, False otherwise
240
+
241
+ note: The algorithm is automatically detected based on checksum length:
242
+
243
+ * 32 characters: MD5
244
+ * 40 characters: SHA1
245
+ * 64 characters: SHA256
246
+ * 128 characters: SHA512
247
+ """
248
+ # Get checksums from URL
249
+ LOG.debug("Fetching checksums from %s...", checksum_url)
250
+ checksums = parse_checksum_file_from_url(checksum_url)
251
+
252
+ if not checksums:
253
+ raise ChecksumException(f"No checksums found in {checksum_url}")
254
+
255
+ # Determine filename to look for
256
+ if filename is None:
257
+ filename = os.path.basename(file_path)
258
+
259
+ # Find checksum for our file
260
+ if filename not in checksums:
261
+ # Try with different path variations
262
+ possible_names = [
263
+ filename,
264
+ os.path.basename(filename), # just filename without path
265
+ filename.replace("\\", "/"), # Unix-style paths
266
+ filename.replace("/", "\\"), # Windows-style paths
267
+ ]
268
+
269
+ found = False
270
+ for name in possible_names:
271
+ if name in checksums:
272
+ filename = name
273
+ found = True
274
+ break
275
+
276
+ if not found:
277
+ raise ChecksumException(f"Checksum for {filename} not found in {checksum_url}")
278
+
279
+ expected_checksum = checksums[filename]
280
+
281
+ # Detect algorithm based on checksum length
282
+ checksum_length = len(expected_checksum)
283
+ if checksum_length == 32:
284
+ algorithm = "md5"
285
+ elif checksum_length == 40:
286
+ algorithm = "sha1"
287
+ elif checksum_length == 64:
288
+ algorithm = "sha256"
289
+ elif checksum_length == 128:
290
+ algorithm = "sha512"
291
+ else:
292
+ raise ChecksumException(f"Unsupported checksum length: {checksum_length}")
293
+
294
+ # Calculate checksum of local file
295
+ LOG.debug("Calculating %s checksum of %s...", algorithm, file_path)
296
+ calculated_checksum = calculate_file_checksum(file_path, algorithm)
297
+
298
+ is_valid = calculated_checksum == expected_checksum.lower()
299
+
300
+ if not is_valid:
301
+ LOG.error(
302
+ "Checksum mismatch for %s: calculated %s, expected %s",
303
+ file_path,
304
+ calculated_checksum,
305
+ expected_checksum,
306
+ )
307
+ raise ChecksumException(
308
+ f"Checksum mismatch for {file_path}: calculated {calculated_checksum}, expected {expected_checksum}"
309
+ )
310
+ LOG.debug("Checksum verification successful for %s", file_path)
311
+
312
+ # Compare checksums
313
+ return calculated_checksum == expected_checksum.lower()