I want to write a macro for the package DrWatson that does the following:
I define a macro @m
and a function f
inside DrWatson. Now, when the function f
is called in a script somewhere, I want to be able inside f
to resolve the path of the file that called f
, using a super magic macro @m
.
E.g. I have a script ~/test.jl
and inside the script I have
using DrWatson
f()
then f
should be able to resolve inside it the path ~/test.jl
.
Is this even possible or I am asking for too much magic?
f() = display(stacktrace(backtrace()))
should give you some ideas about how to go about extracting this info.
1 Like
You could also make your user facing API a macro and inspect the (hidden) __source__
argument.
1 Like
@pfitzseb where can I find information about this argument? Searching the Julia docs didn’t yield anything. (https://docs.julialang.org/en/latest/search/?q=__source__+ )
@tim.holy , even though I would much rather use a normal function than a macro here, I am not so sure I can use your suggestion.
I define in my module the test functions
function tag2(x)
a = callerpath()
end
function callerpath()
a = stacktrace(backtrace())
end
and I create a file test.jl
with contents
using DrWatson
a = tag2(rand())
Regardless if I run this in Juno or in the REPL (using include
) I get a stack trace that looks like:
julia> include(raw"C:\Users\datseris\.julia\dev\DrWatson\test\test.jl")
12-element Array{Base.StackTraces.StackFrame,1}:
callerpath at saving_tools.jl:91 [inlined]
tag2(::Float64) at saving_tools.jl:87
top-level scope at none:0
include at boot.jl:326 [inlined]
include_relative(::Module, ::String) at loading.jl:1038
include(::Module, ::String) at sysimg.jl:29
include(::String) at client.jl:403
top-level scope at none:0
eval(::Module, ::Any) at boot.jl:328
eval_user_input(::Any, ::REPL.REPLBackend) at REPL.jl:85
macro expansion at REPL.jl:117 [inlined]
(::getfield(REPL, Symbol("##26#27")){REPL.REPLBackend})() at task.jl:259
In both Juno, REPL the third element of the trace (which I would assume to be the filename) is “none” because the execution is done at the top-level scope. Also, notably, test.jl
is not present in any trace.
Scroll down a bit from here.
1 Like
So far I have been able to do that using the following definition:
macro tag!(d, path = projectdir())
s = QuoteNode(__source__)
:(tag!($(esc(d)), $(esc(path)), $s))
end
and adding a third argument to my already existing function tag!
.
I would still appreciate if this can be done using a normal function call instead of the macro, for various reasons (mostly having a cleaner API). If anyone knows a way please let me know.