lookup()'s {filename: FileRef} comprehension silently kept only the last
attachment when a foreign message carried duplicate filenames (Discord
permits this; cache()'s own upload path already rejects it loudly, so the
gap is only a foreign message passed to lookup()). Refs still resolve
correctly by attachment_id, so keep the last-wins map (non-breaking) but log
the collision instead of staying silent about it.
Signed-off-by: disqualifier <dev@disqualifier.me>
_fresh_file_from_source wrapped the stream-copy branch's failures in
DPYCacheError but called discord.File(source.fp.name, ...) in the
owner-reopen branch with no try - a path-backed File whose backing file was
deleted between caching calls raised a raw FileNotFoundError/PermissionError
where the module's fail-loud contract promises DPYCacheError for every
lib-domain fault. Wrap the reopen the same way the stream-copy branch
already is.
Signed-off-by: disqualifier <dev@disqualifier.me>
_normalize and _to_file both already accept bytearray at runtime
(isinstance((bytes, bytearray)), bytes(source) coercion in the dict path),
but the FileContent Union only listed bytes - a typed consumer passing a
bytearray gets spurious mypy arg-type/dict-item errors for input the code
genuinely handles. Widen the Union to match actual behavior.
Signed-off-by: disqualifier <dev@disqualifier.me>
__init__.py:14 hardcoded "0.1.0" while pyproject.toml, the README
install pin, and the README changelog all said 0.1.1 - a consumer
gating on __version__ read one release behind the actual tag. Derive
__version__ from installed package metadata
(importlib.metadata.version("dpy_cache")) so the hardcoded literal
can no longer drift from the release tag; keep a fallback literal for
the not-installed/editable case, synced to the current pyproject
version.
Bump 0.1.1 -> 0.1.2.
Signed-off-by: disqualifier <dev@disqualifier.me>
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>