Macro to deduce the filepath of the file that calls a specific function

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.