4 Commits
Author SHA1 Message Date
dsql f480a7c077 docs: correct README error contract (initialize-time, raw discord exceptions)
the README Errors section claimed resolution raises ValueError 'at call time', but per-call resolution is swallowed with send failures and the raise happens at initialize() — and the raised type can be an underlying discord exception (NotFound/Forbidden/HTTPException), not only ValueError. corrected to match the code and the module docstring.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-29 01:39:21 -04:00
dsql dd56009add docs: clarify resolution raises at initialize(), per-call resolution is swallowed
the module docstring said resolution failures raise 'at initialize/call time', but per-call send-path resolution failures are swallowed with send failures (the documented intentional swallow). corrected to state resolution raises from initialize() at setup, while a per-call guild= that doesn't resolve falls back to stdlib like any send failure.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-29 01:10:25 -04:00
dsql 988464ef12 fix: compare guild by id so an uninitialized int-guild routes correctly
_get_channel compared guild == self.guild; when initialize() was not called, self.guild stays an int while the passed guild is a Guild object, so the comparison was always False and routing silently fell through to the bot.settings lookup instead of using the constructed channel. now compares by .id on both sides.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-28 17:46:20 -04:00
dsql 7a5684e065 add package: pyproject + src
DPYLogger: leveled discord channel logger (debug/info/success/fail/task/
critical) over discord.py. config-free — embed identity injected at
construction, per-guild channel routing read live from bot.settings.
dual-sink: mirrors every call to the stdlib logger. embed_builder
callable + build_embed override for customization. raises by design;
object-only (no module proxy). src/ layout, hatchling build.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-24 21:36:43 -04:00
2 changed files with 12 additions and 8 deletions
+7 -4
View File
@@ -70,10 +70,13 @@ await bot.log.debug("noisy", log_to_file=False) # -> Discord only
## Errors
Resolution failures (unresolvable guild/channel, bad config) raise `ValueError` at
`initialize`/call time — a misconfigured logger should fail loudly at setup. Per-call
**send** failures do **not** propagate: they fall back to the stdlib logger so a
transient Discord failure never breaks the caller's command.
Resolution failures (unresolvable guild/channel, bad config) raise from `initialize()`
— a misconfigured logger should fail loudly at setup. The raised type is usually
`ValueError`, but an underlying `discord` exception (`NotFound` / `Forbidden` /
`HTTPException`) from `fetch_guild`/`fetch_channel` can also propagate. On a **per-call**
send, neither resolution nor send failures propagate: they fall back to the stdlib
logger so a transient Discord failure (or a per-call `guild=` that doesn't resolve)
never breaks the caller's command.
## Construction contract
+5 -4
View File
@@ -41,9 +41,10 @@ extending: this base has no feed/announcement method by design. a project
that wants one subclasses DPYLogger and adds it, reusing _resolve.
errors: resolution failures (unresolvable guild/channel, bad config) raise
ValueError at initialize/call time — a misconfigured logger should fail loudly.
per-call send failures do NOT propagate: they fall back to the stdlib logger so
a transient discord failure never breaks the caller's command.
ValueError from `initialize()` — a misconfigured logger should fail loudly at
setup. on a per-call send, resolution AND send failures do NOT propagate: they
fall back to the stdlib logger so a transient discord failure (or a per-call
`guild=` that doesn't resolve) never breaks the caller's command.
"""
import logging
@@ -128,7 +129,7 @@ class DPYLogger:
if not guild:
raise ValueError("[dpy_logger] cannot resolve channel without a guild")
if not override and guild == self.guild:
if not override and getattr(guild, "id", guild) == getattr(self.guild, "id", self.guild):
if isinstance(self.channel, discord.TextChannel):
return self.channel
if isinstance(self.channel, int):