Commit Graph
11 Commits
Author SHA1 Message Date
dsql 105545a421 release: 1.0.0
first stable release. pre-1.0.0 verification complete: all surviving MED regressions and
gaps resolved and independently re-fired, tree audited clean across the suite.

Signed-off-by: disqualifier <dev@disqualifier.me>
v1.0.0
2026-07-09 18:53:15 -04:00
dsql 66b7037cc2 docs: correct cache() docstring exception types to match the code
the docstring listed an unsupported content type under DPYCacheError, but _normalize/
_prevalidate raise ValueError for it (DPYCacheError is only the duplicate-filename and
missing-path cases). a caller following the docstring with except DPYCacheError would let
the ValueError escape. group the raises correctly: ValueError = unnamed bytes / unsupported
type; DPYCacheError = duplicate filename / missing path / count mismatch. doc-only.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-06 21:02:02 -04:00
dsql 51a4f7a817 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>
2026-07-06 20:02:19 -04:00
dsql 96cf2e86ba fix: log a warning on duplicate attachment filenames in lookup() (dpycache-2)
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>
2026-07-06 00:18:52 -04:00
dsql d611797882 fix: wrap owner-reopen File rebuild in DPYCacheError (dpycache-1)
_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>
2026-07-06 00:18:14 -04:00
dsql 7a1ab625fa docs: widen FileContent to include bytearray (dpycache-3)
_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>
2026-07-06 00:17:48 -04:00
dsql c670ab952e refactor: version fallback to 0.0.0+unknown (drop hardcoded literal)
Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-03 17:01:33 -04:00
dsql 5885839f06 fix: __version__ hardcoded to 0.1.0, drifted from pyproject 0.1.1
__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>
2026-07-03 16:15:48 -04:00
dsql 248d6ebb6b 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>
2026-07-03 15:32:28 -04:00
dsql 2e93a0bcee add package: pyproject + src (DPYCache, cache/cache_one/cache_url/resolve/lookup, fail-loud)
Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-02 19:20:08 -04:00
dsql e06bf12fae init: persist images/files to a discord storage channel with durable, re-resolvable references
Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-02 19:20:04 -04:00