fix: reject duplicate-name uploads and rebuild fresh Files per send
cache() silently lost a FileRef when a list input had two sources resolving to the same name (two paths with the same basename, or two discord.File objects with the same .filename): both files were uploaded (real cost) but the merge step kept only the last, orphaning the first. _normalize now rejects a duplicate name with DPYCacheError before any upload happens. _to_file returned the same discord.File object for a discord.File source, only mutating .filename. Reusing one File under two keys swapped filename/URL between refs; reusing a BytesIO-backed File re-sent 0 bytes; reusing a path-backed File after discord.py's own close() raised ValueError. _to_file now builds a genuinely distinct discord.File per send, re-opening a path-backed source or seeking and copying a caller-supplied stream; an unrebuildable opaque stream now raises DPYCacheError instead of corrupting or crashing a later send. Also: lookup() accepts a digit-only message-id string (previously only bare int fetched directly, a numeric string routed into the jump-url parser and raised); dropped a redundant discord.Forbidden member from an except tuple (Forbidden already subclasses HTTPException); tightened _parse_jump_url's return type off a dead Optional and added `from err` to its raise. Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
@@ -9,7 +9,7 @@ bites you.
|
||||
## Install
|
||||
|
||||
```
|
||||
dpy_cache @ git+ssh://git@git.rethinkstudios.io/rethink-public/dpy_cache.git@v0.1.0
|
||||
dpy_cache @ git+ssh://git@git.rethinkstudios.io/rethink-public/dpy_cache.git@v0.1.1
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -46,6 +46,9 @@ refs = await cache.lookup(result.message_url) # jump url, message id, or disco
|
||||
refs["avatar.png"].url
|
||||
```
|
||||
|
||||
`lookup()` accepts a message id as an `int` or as a digit-only `str` (the snowflake shape
|
||||
JSON/DBs store), e.g. `cache.lookup("123456789012345678")`.
|
||||
|
||||
## What you inject
|
||||
|
||||
A resolved Discord channel object (`discord.abc.Messageable` that supports `.send` and
|
||||
@@ -58,7 +61,16 @@ first use.
|
||||
|
||||
`cache_one` / `cache` accept, per file: `bytes`, a filesystem path (`str` or
|
||||
`os.PathLike`), or a `discord.File`. In a **list** input, raw `bytes` have no filename and
|
||||
raise `ValueError` — pass a dict `{name: bytes}` or a `discord.File` to name bytes.
|
||||
raise `ValueError` — pass a dict `{name: bytes}` or a `discord.File` to name bytes. A
|
||||
**list** input whose sources resolve to the same name (two paths with the same basename,
|
||||
two `discord.File`s with the same `.filename`, ...) raises `DPYCacheError` before any
|
||||
upload — a dict input can't collide since dict keys are already unique.
|
||||
|
||||
A `discord.File` source is safe to reuse across multiple names/batches: `cache()` always
|
||||
builds a genuinely fresh `discord.File` per send (re-opening a path-backed source, or
|
||||
seeking + copying a caller-supplied stream) rather than reusing the object you passed in.
|
||||
An opaque stream that can't be safely re-read (already closed, not seekable) raises
|
||||
`DPYCacheError` instead of silently sending stale or empty data.
|
||||
|
||||
## Why `resolve()` exists
|
||||
|
||||
@@ -80,12 +92,26 @@ API. In short:
|
||||
- `lookup(message) -> dict[str, FileRef]`
|
||||
|
||||
**Fail-loud.** Nothing is swallowed. Lib-specific invariants raise `DPYCacheError`
|
||||
(attachment-count mismatch after a send, `cache_url` non-200, a `lookup` jump-URL pointing
|
||||
at a different channel than the injected one). Unnamed `bytes` in a list raise `ValueError`.
|
||||
Raw Discord errors (`Forbidden` / `HTTPException` / `NotFound`) propagate **unwrapped** so
|
||||
you can still branch on Discord's own types.
|
||||
(attachment-count mismatch after a send, a duplicate filename in a list input to `cache()`,
|
||||
`cache_url` non-200, a `lookup` jump-URL pointing at a different channel than the injected
|
||||
one, a `discord.File` source that can't be safely rebuilt for a repeat send). Unnamed
|
||||
`bytes` in a list raise `ValueError`. Raw Discord errors (`Forbidden` / `HTTPException` /
|
||||
`NotFound`) propagate **unwrapped** so you can still branch on Discord's own types.
|
||||
|
||||
## Versioning
|
||||
|
||||
Tagged `vX.Y.Z`; pin a tag in your install line. Targets `discord.py>=2.0` (not
|
||||
`discord.py-self`).
|
||||
|
||||
### v0.1.1
|
||||
|
||||
- `cache()` now rejects a duplicate filename in a **list** input with `DPYCacheError`
|
||||
before any upload, instead of silently uploading both files and losing the first
|
||||
`FileRef` at merge time.
|
||||
- `_to_file` now builds a genuinely fresh `discord.File` per send for `discord.File`
|
||||
sources (previously returned the same object, which could swap filename/URL when one
|
||||
`File` was reused under two names, or re-send 0 bytes / raise `ValueError` on a spent
|
||||
stream).
|
||||
- `lookup()` now accepts a digit-only message-id string in addition to `int`.
|
||||
- Dropped the redundant `discord.Forbidden` member from an `except` tuple (it already
|
||||
subclasses `discord.HTTPException`); no behavior change.
|
||||
|
||||
Reference in New Issue
Block a user