PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Functions
picurv_cli.storage.transport Namespace Reference

Functions

str _remote_join (str remote, *str parts)
 Join path components without corrupting rclone remote syntax.
 
str _object_remote (dict profile, str archive_id, *str parts)
 Return the remote path for one immutable archive object.
 
str _blob_remote (dict profile, str digest)
 Return the remote path of one content-addressed payload blob.
 
str _chunk_remote_path (dict profile, str archive_id, dict chunk)
 Resolve where one manifest chunk's payload lives on the remote.
 
bool _remote_blob_present (dict profile, str digest)
 Whether a blob with this digest is already stored and intact.
 
subprocess.CompletedProcess _run_rclone (list arguments, bool check=True)
 Invoke rclone through the same argv-based subprocess boundary as other PICurv tools.
 
str _remote_sha256 (str remote_path)
 Ask rclone to calculate or retrieve the SHA-256 of one remote object.
 
dict _upload_verified (str local_path, str remote_path)
 Upload one file, then verify its remote SHA-256.
 
bytes _read_remote_bytes (str remote_path)
 Read a small remote catalog object through rclone.
 
dict _load_remote_manifest (dict profile, str archive_id, bool require_complete=True)
 Fetch and validate one versioned remote storage manifest.
 

Function Documentation

◆ _remote_join()

str picurv_cli.storage.transport._remote_join ( str  remote,
*str  parts 
)
protected

Join path components without corrupting rclone remote syntax.

Parameters
[in]remoteValue supplied through the remote argument.
[in]partsValue supplied through the parts argument.
Returns
Result produced by this operation.

Definition at line 37 of file transport.py.

37def _remote_join(remote: str, *parts: str) -> str:
38 """!
39 @brief Join path components without corrupting rclone remote syntax.
40 @param[in] remote Value supplied through the `remote` argument.
41 @param[in] parts Value supplied through the `parts` argument.
42 @return Result produced by this operation.
43 """
44 clean_parts = [str(part).strip("/") for part in parts if str(part).strip("/")]
45 suffix = "/".join(clean_parts)
46 if not suffix:
47 return remote
48 if remote.endswith(":"):
49 return remote + suffix
50 return remote.rstrip("/") + "/" + suffix
51
52

◆ _object_remote()

str picurv_cli.storage.transport._object_remote ( dict  profile,
str  archive_id,
*str  parts 
)
protected

Return the remote path for one immutable archive object.

Parameters
[in]profileValue supplied through the profile argument.
[in]archive_idValue supplied through the archive_id argument.
[in]partsValue supplied through the parts argument.
Returns
Result produced by this operation.

Definition at line 53 of file transport.py.

53def _object_remote(profile: dict, archive_id: str, *parts: str) -> str:
54 """!
55 @brief Return the remote path for one immutable archive object.
56 @param[in] profile Value supplied through the `profile` argument.
57 @param[in] archive_id Value supplied through the `archive_id` argument.
58 @param[in] parts Value supplied through the `parts` argument.
59 @return Result produced by this operation.
60 """
61 return _remote_join(profile["remote"], REMOTE_OBJECTS_DIRECTORY, archive_id, *parts)
62
63

◆ _blob_remote()

str picurv_cli.storage.transport._blob_remote ( dict  profile,
str  digest 
)
protected

Return the remote path of one content-addressed payload blob.

Parameters
[in]profileActive storage profile.
[in]digestPayload SHA-256.
Returns
Remote path, fanned out by digest prefix to keep directories small.

Definition at line 64 of file transport.py.

64def _blob_remote(profile: dict, digest: str) -> str:
65 """!
66 @brief Return the remote path of one content-addressed payload blob.
67 @param[in] profile Active storage profile.
68 @param[in] digest Payload SHA-256.
69 @return Remote path, fanned out by digest prefix to keep directories small.
70 """
71 return _remote_join(profile["remote"], REMOTE_BLOBS_DIRECTORY, digest[:2], digest)
72
73

◆ _chunk_remote_path()

str picurv_cli.storage.transport._chunk_remote_path ( dict  profile,
str  archive_id,
dict  chunk 
)
protected

Resolve where one manifest chunk's payload lives on the remote.

Every chunk this schema writes is blob-addressed, resolved out of the shared content-addressed store. _load_remote_manifest already refuses any manifest whose schema version does not match the current one, so a non-blob chunk cannot reach this function from a manifest this build actually loads.

Parameters
[in]profileActive storage profile.
[in]archive_idOwning archive id.
[in]chunkManifest chunk entry.
Returns
Remote path of the chunk payload.

Definition at line 74 of file transport.py.

74def _chunk_remote_path(profile: dict, archive_id: str, chunk: dict) -> str:
75 """!
76 @brief Resolve where one manifest chunk's payload lives on the remote.
77
78 @details Every chunk this schema writes is blob-addressed, resolved out of the
79 shared content-addressed store. `_load_remote_manifest` already refuses
80 any manifest whose schema version does not match the current one, so a
81 non-blob chunk cannot reach this function from a manifest this build
82 actually loads.
83 @param[in] profile Active storage profile.
84 @param[in] archive_id Owning archive id.
85 @param[in] chunk Manifest chunk entry.
86 @return Remote path of the chunk payload.
87 """
88 if chunk.get("blob"):
89 return _blob_remote(profile, chunk["sha256"])
90 return _object_remote(profile, archive_id, "chunks", chunk["name"])
91
92

◆ _remote_blob_present()

bool picurv_cli.storage.transport._remote_blob_present ( dict  profile,
str  digest 
)
protected

Whether a blob with this digest is already stored and intact.

Parameters
[in]profileActive storage profile.
[in]digestPayload SHA-256.
Returns
True when the remote already holds exactly this content.

Definition at line 93 of file transport.py.

93def _remote_blob_present(profile: dict, digest: str) -> bool:
94 """!
95 @brief Whether a blob with this digest is already stored and intact.
96 @param[in] profile Active storage profile.
97 @param[in] digest Payload SHA-256.
98 @return True when the remote already holds exactly this content.
99 """
100 try:
101 return _remote_sha256(_blob_remote(profile, digest)) == digest
102 except (StorageError, OSError):
103 # An absent blob is the normal case, not an error; anything else that stops us
104 # from confirming it is treated the same way, and the chunk is uploaded.
105 return False
106
107

◆ _run_rclone()

subprocess.CompletedProcess picurv_cli.storage.transport._run_rclone ( list  arguments,
bool   check = True 
)
protected

Invoke rclone through the same argv-based subprocess boundary as other PICurv tools.

Parameters
[in]argumentsValue supplied through the arguments argument.
[in]checkValue supplied through the check argument.
Returns
Result produced by this operation.

Definition at line 108 of file transport.py.

108def _run_rclone(arguments: list, check: bool = True) -> subprocess.CompletedProcess:
109 """!
110 @brief Invoke rclone through the same argv-based subprocess boundary as other PICurv tools.
111 @param[in] arguments Value supplied through the `arguments` argument.
112 @param[in] check Value supplied through the `check` argument.
113 @return Result produced by this operation.
114 """
115 executable = shutil.which("rclone")
116 if not executable:
117 raise StorageError("rclone was not found on PATH. Install/configure rclone before using PICurv storage.")
118 result = subprocess.run(
119 [executable] + [str(item) for item in arguments],
120 text=True,
121 capture_output=True,
122 check=False,
123 )
124 if check and result.returncode != 0:
125 detail = (result.stderr or result.stdout or "unknown rclone error").strip()
126 raise StorageError(f"rclone {' '.join(str(item) for item in arguments[:2])} failed: {detail}")
127 return result
128
129

◆ _remote_sha256()

str picurv_cli.storage.transport._remote_sha256 ( str  remote_path)
protected

Ask rclone to calculate or retrieve the SHA-256 of one remote object.

Parameters
[in]remote_pathValue supplied through the remote_path argument.
Returns
Result produced by this operation.

Definition at line 130 of file transport.py.

130def _remote_sha256(remote_path: str) -> str:
131 """!
132 @brief Ask rclone to calculate or retrieve the SHA-256 of one remote object.
133 @param[in] remote_path Value supplied through the `remote_path` argument.
134 @return Result produced by this operation.
135 """
136 result = _run_rclone(["hashsum", "SHA-256", remote_path])
137 for line in result.stdout.splitlines():
138 token = line.strip().split(None, 1)[0] if line.strip() else ""
139 if re.fullmatch(r"[0-9a-fA-F]{64}", token):
140 return token.lower()
141 raise StorageError(f"rclone did not return a SHA-256 for {remote_path}.")
142
143

◆ _upload_verified()

dict picurv_cli.storage.transport._upload_verified ( str  local_path,
str  remote_path 
)
protected

Upload one file, then verify its remote SHA-256.

Parameters
[in]local_pathValue supplied through the local_path argument.
[in]remote_pathValue supplied through the remote_path argument.
Returns
Result produced by this operation.

Definition at line 144 of file transport.py.

144def _upload_verified(local_path: str, remote_path: str) -> dict:
145 """!
146 @brief Upload one file, then verify its remote SHA-256.
147 @param[in] local_path Value supplied through the `local_path` argument.
148 @param[in] remote_path Value supplied through the `remote_path` argument.
149 @return Result produced by this operation.
150 """
151 local_digest = _sha256_file(local_path)
152 _run_rclone(["copyto", local_path, remote_path])
153 remote_digest = _remote_sha256(remote_path)
154 if remote_digest != local_digest:
155 raise StorageError(
156 f"Remote checksum mismatch after upload: {remote_path} "
157 f"(local {local_digest}, remote {remote_digest})."
158 )
159 return {"sha256": local_digest, "stored_bytes": os.path.getsize(local_path)}
160
161

◆ _read_remote_bytes()

bytes picurv_cli.storage.transport._read_remote_bytes ( str  remote_path)
protected

Read a small remote catalog object through rclone.

Parameters
[in]remote_pathValue supplied through the remote_path argument.
Returns
Result produced by this operation.

Definition at line 162 of file transport.py.

162def _read_remote_bytes(remote_path: str) -> bytes:
163 """!
164 @brief Read a small remote catalog object through rclone.
165 @param[in] remote_path Value supplied through the `remote_path` argument.
166 @return Result produced by this operation.
167 """
168 executable = shutil.which("rclone")
169 if not executable:
170 raise StorageError("rclone was not found on PATH.")
171 result = subprocess.run(
172 [executable, "cat", remote_path], capture_output=True, check=False
173 )
174 if result.returncode != 0:
175 detail = (result.stderr or result.stdout or b"unknown rclone error").decode("utf-8", "replace").strip()
176 raise StorageError(f"Unable to read remote object {remote_path}: {detail}")
177 return result.stdout
178
179

◆ _load_remote_manifest()

dict picurv_cli.storage.transport._load_remote_manifest ( dict  profile,
str  archive_id,
bool   require_complete = True 
)
protected

Fetch and validate one versioned remote storage manifest.

Parameters
[in]profileValue supplied through the profile argument.
[in]archive_idValue supplied through the archive_id argument.
[in]require_completeValue supplied through the require_complete argument.
Returns
Result produced by this operation.

Definition at line 180 of file transport.py.

180def _load_remote_manifest(profile: dict, archive_id: str, require_complete: bool = True) -> dict:
181 """!
182 @brief Fetch and validate one versioned remote storage manifest.
183 @param[in] profile Value supplied through the `profile` argument.
184 @param[in] archive_id Value supplied through the `archive_id` argument.
185 @param[in] require_complete Value supplied through the `require_complete` argument.
186 @return Result produced by this operation.
187 """
188 if not ARCHIVE_ID_PATTERN.fullmatch(str(archive_id)):
189 raise StorageError(f"Invalid archive ID: {archive_id!r}.")
190 manifest_bytes = _read_remote_bytes(_object_remote(profile, archive_id, REMOTE_MANIFEST_FILENAME))
191 if require_complete:
192 complete = _read_remote_bytes(_object_remote(profile, archive_id, REMOTE_COMPLETE_FILENAME))
193 recorded = complete.decode("ascii", "replace").strip().lower()
194 actual = hashlib.sha256(manifest_bytes).hexdigest()
195 if recorded != actual:
196 raise StorageError(f"Archive {archive_id} has no valid completion marker.")
197 try:
198 manifest = json.loads(manifest_bytes.decode("utf-8"))
199 except (UnicodeDecodeError, ValueError) as exc:
200 raise StorageError(f"Archive {archive_id} has an invalid manifest.") from exc
201 if not isinstance(manifest, dict) or manifest.get("storage_schema_version") != STORAGE_SCHEMA_VERSION:
202 raise StorageError(
203 f"Archive {archive_id} uses unsupported storage schema "
204 f"{manifest.get('storage_schema_version') if isinstance(manifest, dict) else 'unknown'}."
205 )
206 return manifest