That was quick!
To be fair, there was a rather heated argument back and forth about Base.get_extension
without a very clear outcome. It is great to use interactively while developing and testing – no argument there. But idioms related to, e.g. informative error messages for not-yet-loaded weak dependencies, can lead to footguns, and alternative setups that peek less in the internals are more reliable.
I have not yet understood that discussion. I only use that function in two places: Specifying modules in makedocs
and extending and extension (so ABCExt
extending stuff defined in ABExt
).
Especially without get_extension
, there is inadequate functionality to document (I think I ran into errors documenting helper functions, might be incorrect about this though) and test extensions
If you can’t use Base.get_extension
, you could always just define your own, i.e. in your main package have a function stub
#MyPackage/src/MyPackage.jl
module MyPackage
function get_foo_extension end
end
and then in your extension file you’d have
#MyPackage/ext/MyPackageFooExt.jl
module MyPackageFooExt
using MyPackage
MyPackage.get_foo_extension() = MyPackageFooExt
end
Users can then access the extension module with
MyPackage.get_foo_extension()
Thanks… I currently use:
function render_typst_document(catch_all)
if Base.get_extension(Luxor, :LuxorExtTypstry) isa Module
throw(MethodError(render_typst_document, catch_all))
else
throw(ErrorException("Module Typstry is not loaded."))
end
end
so not for users, more to allow the extension to add methods…
Nonetheless, it would be preferable to switch to the supported approach. Furthermore, it is also preferable to avoid throwing MethodError
explicitly, instead letting it be thrown by dispatch.
The only valid method of render_typst_document
appears to require passing an argument from Typstry
, so that message seems a best contradictory and at worst very confusing, since using get_extension
from there would appear to always incorrectly diagnose the issue.
OK, thanks, What code should I use to get this behaviour (which is what it does currently):
julia> using Luxor
julia> render_typst_document("a + b")
ERROR: Module Typstry is not loaded.
julia>
It’s true that there are two errors here (no extension not loaded and wrong argument type), but one is more important than the other, and needs addressing first.
If invalidations are fine, you can do:
module Luxor
function render_typst_document(arg::Any)
error("load Typst")
end
end
module Typstry # or in a Luxor extension dependent on Typstry
import Luxor
function Luxor.render_typst_document(arg::AbstractString)
# actual code
end
end
If invalidations are a problem (they are) you can instead register an error hint on method errors for the function render_typst_document
in Luxor.__init__
. If using __init__
is undesirable (e.g. because you want more aot-compilable trimmable code) then you can do the registration in a OncePerProcess
.
I think I would leave the error itself to Julia. We usually register just an additional error hint (on init) like
(when Manopt is loaded but not JuMP)
It’s true that there are two errors here (no extension not loaded and wrong argument type), but one is more important than the other, and needs addressing first.
I think that error message just intends to be something like "`render_typst_document` requires a object from an appropriate package such as Typst. Got `$(typeof(arg))` instead."
Can you elaborate what you mean by this?
My code example is exactly such an error hint registration.
If that error occurs, the hint is printed as well, giving you an idea what might resolve the problem. For functions that are only available if the extension is loaded, the hint to load the right packages is very reasonable I think.