fix: pre-validate all cache() inputs before any upload so a bad late input orphans nothing
cache() built discord.File objects per batch inside the send loop, so an unsupported type or a missing file path in the 11th+ item raised only after earlier batches were already uploaded - orphaning those stored messages with no returned ref. A new _prevalidate pass checks every source up front (supported type; a path source exists and is a file) WITHOUT opening any File or reading bytes, so a locally-detectable bad input fails loud before the first send. The File is still built per batch in the loop (fresh-File-per-send unchanged), so no extra open FDs are held. Neg control: old code uploaded batch 1 then raised on the bad path (sends=1, orphaned); new raises with sends=0. Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
@@ -122,10 +122,13 @@ class DPYCache:
|
||||
) -> CacheResult:
|
||||
"""upload files to the storage channel (batching over 10/message) and return refs
|
||||
keyed by original filename; raises ValueError on unnamed bytes in a list input,
|
||||
DPYCacheError on a duplicate filename in a list input (checked before any upload, so
|
||||
a collision costs zero requests) or an attachment-count mismatch after a send, and
|
||||
propagates discord's own Forbidden/HTTPException unwrapped on a send failure"""
|
||||
DPYCacheError on a duplicate filename in a list input, an unsupported content type,
|
||||
or a missing file path (all checked before any upload, so a locally-detectable bad
|
||||
input in a >10-file batch costs zero requests and orphans nothing), or an
|
||||
attachment-count mismatch after a send, and propagates discord's own
|
||||
Forbidden/HTTPException unwrapped on a send failure"""
|
||||
items = self._normalize(files)
|
||||
self._prevalidate(items)
|
||||
merged: dict[str, FileRef] = {}
|
||||
jump_urls: list[str] = []
|
||||
|
||||
@@ -248,6 +251,27 @@ class DPYCache:
|
||||
items.append((name, source))
|
||||
return items
|
||||
|
||||
def _prevalidate(self, items: "list[tuple[str, FileContent]]") -> None:
|
||||
"""check every source is a supported, locally-resolvable input BEFORE any upload
|
||||
|
||||
catches an unsupported content type or a missing/directory file path up front so a
|
||||
bad input in a >10-file batch fails loud before the first send rather than after
|
||||
earlier batches already landed (which would orphan those stored messages). does NOT
|
||||
open Files or read bytes - only a cheap type check and, for a path source, an
|
||||
existence/is-file probe; the actual File is still built per batch in the send loop.
|
||||
"""
|
||||
for name, source in items:
|
||||
if isinstance(source, (discord.File, bytes, bytearray)):
|
||||
continue
|
||||
if isinstance(source, (str, os.PathLike)):
|
||||
path = os.fspath(source)
|
||||
if not os.path.isfile(path):
|
||||
raise DPYCacheError(
|
||||
f"file source for {name!r} is not an existing file: {path!r}"
|
||||
)
|
||||
continue
|
||||
raise ValueError(f"unsupported file content type for {name!r}: {type(source).__name__}")
|
||||
|
||||
def _to_file(self, name: str, source: FileContent) -> discord.File:
|
||||
"""build a genuinely fresh discord.File for ``source`` named ``name``, distinct from
|
||||
any File previously handed to a send; a fresh File is built per send so the source
|
||||
|
||||
Reference in New Issue
Block a user