ringo-task-queue 0.1.0.dev0__py3-none-win_amd64.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,253 @@
1
+ """Platform daemon discovery and integrity validation.
2
+
3
+ Platform wheels contain a manifest beside one or more daemon binaries. The
4
+ manifest is generated by ``tools/stage_binary.py`` and is deliberately not
5
+ checked into source control with a real executable. Explicit paths and the
6
+ ``RINGO_DAEMON_PATH`` override remain useful for development and are resolved
7
+ before packaged resources.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import hashlib
13
+ import json
14
+ import os
15
+ import platform
16
+ import re
17
+ import stat
18
+ import sys
19
+ from dataclasses import dataclass
20
+ from datetime import datetime, timezone
21
+ from importlib import resources
22
+ from pathlib import Path
23
+ from typing import Any
24
+
25
+ from ._version import PROTOCOL_MAJOR, PROTOCOL_MINOR, SCHEMA_VERSION, daemon_version_for_sdk
26
+ from .errors import RingoError
27
+
28
+ MANIFEST_NAME = "manifest.json"
29
+ MANIFEST_SCHEMA_VERSION = 1
30
+ DAEMON_NAME = "ringo-task-queue"
31
+ DAEMON_SCHEMA_VERSION = SCHEMA_VERSION
32
+ SUPPORTED_PLATFORMS = frozenset(
33
+ {
34
+ "windows-amd64",
35
+ "linux-amd64",
36
+ "linux-arm64",
37
+ "macos-amd64",
38
+ "macos-arm64",
39
+ }
40
+ )
41
+
42
+
43
+ class BinaryError(RingoError):
44
+ """Base error for a missing, unsupported, or invalid packaged daemon."""
45
+
46
+
47
+ class UnsupportedPlatformError(BinaryError):
48
+ """The current OS/architecture has no daemon in the wheel."""
49
+
50
+
51
+ class BinaryIntegrityError(BinaryError):
52
+ """A packaged daemon does not match its manifest checksum."""
53
+
54
+
55
+ @dataclass(frozen=True)
56
+ class BinaryMetadata:
57
+ platform: str
58
+ filename: str
59
+ sha256: str
60
+ version: str
61
+ protocol_major: int
62
+ protocol_minor: int
63
+ size: int
64
+
65
+
66
+ def platform_key(*, system: str | None = None, machine: str | None = None) -> str:
67
+ """Return the stable manifest key for an OS and CPU architecture."""
68
+ system = (system or sys.platform).lower()
69
+ machine = (machine or platform.machine()).lower().replace("-", "_")
70
+ if system.startswith("win"):
71
+ os_name = "windows"
72
+ elif system.startswith("linux"):
73
+ os_name = "linux"
74
+ elif system in {"darwin", "macos"}:
75
+ os_name = "macos"
76
+ else:
77
+ raise UnsupportedPlatformError(f"unsupported operating system: {system}")
78
+
79
+ if machine in {"amd64", "x86_64", "x64"}:
80
+ arch = "amd64"
81
+ elif machine in {"arm64", "aarch64"} and os_name in {"macos", "linux"}:
82
+ arch = "arm64"
83
+ else:
84
+ raise UnsupportedPlatformError(
85
+ f"unsupported architecture {machine!r} on {os_name}"
86
+ )
87
+ return f"{os_name}-{arch}"
88
+
89
+
90
+ def _manifest_from_object(raw: Any) -> dict[str, BinaryMetadata]:
91
+ """Parse the shared release-artifact manifest contract."""
92
+ if (
93
+ not isinstance(raw, dict)
94
+ or raw.get("manifestVersion") != 1
95
+ or raw.get("name") != DAEMON_NAME
96
+ or raw.get("schemaVersion") != DAEMON_SCHEMA_VERSION
97
+ ):
98
+ raise BinaryError("invalid daemon manifest schema")
99
+ artifacts = raw.get("artifacts")
100
+ if not isinstance(artifacts, list) or not artifacts:
101
+ raise BinaryError("daemon manifest contains no artifacts")
102
+ version = raw.get("version")
103
+ commit = raw.get("buildCommit")
104
+ build_date = raw.get("buildDate")
105
+ protocol_major = raw.get("protocolMajor")
106
+ protocol_minor = raw.get("protocolMinor")
107
+ if (
108
+ not isinstance(version, str)
109
+ or not version
110
+ or not isinstance(commit, str)
111
+ or not commit
112
+ or commit.lower() in {"unknown", "local"}
113
+ or not isinstance(build_date, str)
114
+ or not build_date
115
+ or version != daemon_version_for_sdk()
116
+ or not isinstance(protocol_major, int)
117
+ or protocol_major != PROTOCOL_MAJOR
118
+ or not isinstance(protocol_minor, int)
119
+ or protocol_minor != PROTOCOL_MINOR
120
+ or not re.fullmatch(
121
+ r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})",
122
+ build_date,
123
+ )
124
+ ):
125
+ raise BinaryError("daemon manifest has invalid compatibility metadata")
126
+ try:
127
+ parsed_date = datetime.fromisoformat(build_date.replace("Z", "+00:00"))
128
+ if parsed_date.tzinfo is None or parsed_date.utcoffset() != timezone.utc.utcoffset(None):
129
+ raise ValueError
130
+ except ValueError as exc:
131
+ raise BinaryError("daemon manifest has invalid buildDate") from exc
132
+ result: dict[str, BinaryMetadata] = {}
133
+ for item in artifacts:
134
+ if not isinstance(item, dict):
135
+ raise BinaryError("invalid daemon manifest artifact")
136
+ os_name = item.get("os")
137
+ arch = item.get("arch")
138
+ key = f"{os_name}-{'amd64' if arch == 'amd64' else arch}"
139
+ if os_name == "darwin":
140
+ key = f"macos-{arch}"
141
+ elif os_name in {"windows", "linux"}:
142
+ key = f"{os_name}-{arch}"
143
+ if key not in SUPPORTED_PLATFORMS:
144
+ raise BinaryError(f"invalid daemon manifest platform entry: {key!r}")
145
+ if key in result:
146
+ raise BinaryError(f"duplicate daemon manifest platform entry: {key!r}")
147
+ filename = item.get("filename")
148
+ expected_filename = f"ringo-task-queue-{os_name}-{arch}{'.exe' if os_name == 'windows' else ''}"
149
+ digest = item.get("sha256")
150
+ if (
151
+ not isinstance(filename, str)
152
+ or filename != expected_filename
153
+ or "/" in filename
154
+ or "\\" in filename
155
+ or Path(filename).name != filename
156
+ or not isinstance(digest, str)
157
+ or len(digest) != 64
158
+ or any(char not in "0123456789abcdef" for char in digest)
159
+ or not isinstance(item.get("size"), int)
160
+ or item["size"] <= 0
161
+ or item["size"] > 512 * 1024**2
162
+ ):
163
+ raise BinaryError(f"invalid daemon manifest entry for {key!r}")
164
+ result[key] = BinaryMetadata(
165
+ platform=key,
166
+ filename=filename,
167
+ sha256=digest,
168
+ version=version,
169
+ protocol_major=protocol_major,
170
+ protocol_minor=protocol_minor,
171
+ size=item["size"],
172
+ )
173
+ return result
174
+
175
+
176
+ def load_manifest(directory: Path) -> dict[str, BinaryMetadata]:
177
+ """Load and validate ``bin/manifest.json`` from an unpacked package."""
178
+ path = directory / MANIFEST_NAME
179
+ try:
180
+ raw = json.loads(path.read_text(encoding="utf-8"))
181
+ except FileNotFoundError:
182
+ raise BinaryError(f"daemon manifest not found: {path}") from None
183
+ except (OSError, ValueError) as exc:
184
+ raise BinaryError(f"cannot read daemon manifest {path}: {exc}") from exc
185
+ return _manifest_from_object(raw)
186
+
187
+
188
+ def ensure_executable(path: Path) -> None:
189
+ """Ensure a Unix-installed daemon retains an executable mode bit."""
190
+ if os.name == "nt":
191
+ return
192
+ try:
193
+ mode = path.stat().st_mode
194
+ if not mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH):
195
+ path.chmod(mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
196
+ except OSError as exc:
197
+ raise BinaryError(f"daemon is not executable: {path}: {exc}") from exc
198
+
199
+
200
+ def verify_binary(path: Path, metadata: BinaryMetadata) -> None:
201
+ """Check a staged daemon's SHA-256 and make it executable on Unix."""
202
+ try:
203
+ link_info = path.lstat()
204
+ except OSError as exc:
205
+ raise BinaryIntegrityError(f"daemon binary missing: {path}") from exc
206
+ if path.is_symlink() or not stat.S_ISREG(link_info.st_mode):
207
+ raise BinaryIntegrityError(f"daemon binary is not a regular file: {path}")
208
+ digest_state = hashlib.sha256()
209
+ try:
210
+ with path.open("rb") as source:
211
+ opened_info = os.fstat(source.fileno())
212
+ if not stat.S_ISREG(opened_info.st_mode) or not os.path.samestat(link_info, opened_info):
213
+ raise BinaryIntegrityError(f"daemon binary changed while opening: {path}")
214
+ if opened_info.st_size != metadata.size:
215
+ raise BinaryIntegrityError(
216
+ f"daemon size mismatch for {path}: expected {metadata.size}, got {opened_info.st_size}"
217
+ )
218
+ read_size = 0
219
+ while chunk := source.read(min(1024 * 1024, metadata.size - read_size + 1)):
220
+ read_size += len(chunk)
221
+ if read_size > metadata.size:
222
+ raise BinaryIntegrityError(f"daemon binary grew while reading: {path}")
223
+ digest_state.update(chunk)
224
+ if read_size != metadata.size:
225
+ raise BinaryIntegrityError(f"daemon binary changed while reading: {path}")
226
+ except BinaryIntegrityError:
227
+ raise
228
+ except OSError as exc:
229
+ raise BinaryIntegrityError(f"cannot read daemon binary: {path}: {exc}") from exc
230
+ digest = digest_state.hexdigest()
231
+ if digest != metadata.sha256:
232
+ raise BinaryIntegrityError(
233
+ f"daemon SHA-256 mismatch for {path}: expected {metadata.sha256}, got {digest}"
234
+ )
235
+ ensure_executable(path)
236
+
237
+
238
+ def packaged_binary() -> tuple[Path, BinaryMetadata]:
239
+ """Resolve and verify the daemon matching the running platform."""
240
+ key = platform_key()
241
+ try:
242
+ package_bin = Path(str(resources.files("ringo_task_queue") / "bin"))
243
+ except Exception as exc:
244
+ raise BinaryError(f"cannot access packaged daemon resources: {exc}") from exc
245
+ entries = load_manifest(package_bin)
246
+ metadata = entries.get(key)
247
+ if metadata is None:
248
+ raise UnsupportedPlatformError(
249
+ f"wheel has no daemon for platform {key}; available: {', '.join(sorted(entries))}"
250
+ )
251
+ path = package_bin / metadata.filename
252
+ verify_binary(path, metadata)
253
+ return path, metadata