register_theme! is a one-line insert with no validation:
register_theme!(name::Symbol, theme::Theme) = (_THEMES[name] = theme)
A theme that never defines emit_document registers happily and fails at the first render that selects it. The message when it does fail is good — Pinax.emit_document's fallback on ::Theme errors by name:
ERROR: Pinax: theme NoEmit does not implement `emit_document`. Define `Pinax.emit_document(::NoEmit, …)`.
So this is about when, not about diagnosis quality. A user theme loaded from a path (theme="mytheme.jl") is evaluated, type-checked as a Theme, registered, and only then discovered to be unusable — potentially after a long resolve/materialize pass.
The part worth writing down: hasmethod cannot express this check
The obvious guard does not work, and it does not work silently. Measured:
struct NoEmit <: Pinax.Theme end
struct HasEmit <: Pinax.Theme end
Pinax.emit_document(::HasEmit, doc, out, cache; comments_file="") = "x"
| theme |
hasmethod(emit_document, Tuple{T,Any,Any,Any}) |
which(...).sig |
NoEmit |
true |
Tuple{typeof(emit_document), Theme, Any, Any, Any} |
HasEmit |
true |
Tuple{typeof(emit_document), HasEmit, Any, Any, Any} |
hasmethod is true for both, because the erroring fallback on ::Theme is a method. A guard written with it would pass every theme and look like it was doing something — the failure mode this repository keeps finding in other people's code, in its own registry.
The distinguishing test is which(...).sig: the fallback's first parameter is Theme itself, a real implementation's is the concrete type.
Fix
If this is worth guarding at all:
function register_theme!(name::Symbol, theme::Theme)
m = which(emit_document, Tuple{typeof(theme),Any,Any,Any})
m.sig.parameters[2] === Theme && error(
"Pinax: $(typeof(theme)) does not implement `emit_document`, so it cannot render. " *
"Define it before registering.",
)
return _THEMES[name] = theme
end
Low priority — nothing is silently wrong today, and the render-time message already names the missing method. Filed mainly so the hasmethod trap is on record before someone reaches for it.
register_theme!is a one-line insert with no validation:A theme that never defines
emit_documentregisters happily and fails at the firstrenderthat selects it. The message when it does fail is good —Pinax.emit_document's fallback on::Themeerrors by name:So this is about when, not about diagnosis quality. A user theme loaded from a path (
theme="mytheme.jl") is evaluated, type-checked as aTheme, registered, and only then discovered to be unusable — potentially after a long resolve/materialize pass.The part worth writing down:
hasmethodcannot express this checkThe obvious guard does not work, and it does not work silently. Measured:
hasmethod(emit_document, Tuple{T,Any,Any,Any})which(...).sigNoEmittrueTuple{typeof(emit_document), Theme, Any, Any, Any}HasEmittrueTuple{typeof(emit_document), HasEmit, Any, Any, Any}hasmethodistruefor both, because the erroring fallback on::Themeis a method. A guard written with it would pass every theme and look like it was doing something — the failure mode this repository keeps finding in other people's code, in its own registry.The distinguishing test is
which(...).sig: the fallback's first parameter isThemeitself, a real implementation's is the concrete type.Fix
If this is worth guarding at all:
Low priority — nothing is silently wrong today, and the render-time message already names the missing method. Filed mainly so the
hasmethodtrap is on record before someone reaches for it.