How to convert __source__.file to string?

I’m trying to extract filename as string from __source__.file. When running code below everything works, typeof(g) is String and it contains path filename.

macro file1()
  return QuoteNode(__source__.file)
end

f = @file1
g = String(@file1)

When trying to put code above in single macro, conversion to string fails:

macro file2() # this is line 13
  x = QuoteNode(__source__.file)
  return String(x)
end

h = @file2 #this is line 16

Error stack

ERROR: LoadError: LoadError: MethodError: no method matching String(::QuoteNode)
Closest candidates are:
String(!Matched::String) at boot.jl:318
String(!Matched::Array{UInt8,1}) at strings/string.jl:39
String(!Matched::Base.CodeUnits{UInt8,String}) at strings/string.jl:77

Stacktrace:
[1] @file2(::LineNumberNode, ::Module) at trace.jl:13
in expression starting at trace.jl:16
in expression starting at trace.jl:16

How can one extact filename as string from __source__.file in a single macro or function withtout arguements?

The closest I could get is code below. It works in the same script, but woudl point to a wrong file is caller froma different module though.

function file3() 
  return String(@file1)
end

h = file3()

So remaining part is - how file2 can work as a single macro?

Please understand the code, e.g. what each function does and why you need it, rather than blindly copying the code and trying things.

__source__.file is a Symbol. You basically want the macro to return it. For a normal value you’ll just return it directly but since macros handles a few syntax types (Expr, Symbol, etc) specially you need to stop it from doing that. That’s why you put it in a QuoteNode. In another word, that QuoteNode is there for the macro, not for the result.

That’s why you should not use QuoteNode before passing it to String. If anything, the QuoteNode should be on the String after you created it. It’s not necessary though since String isn’t a special syntax type.

AFAICT, what you want to do is exactly @__FILE__ so you can also find the answer by reading the source code of the macro, which you can find by searching for __FILE__ on github. Again, please understand it before using the implementation. If you don’t, feel free to ask here again about the “why” part.

1 Like

Hi, this should work

macro file2() # this is line 13
  x = QuoteNode(__source__.file)
  return :($String($x))
end
1 Like

Yes it should work but is unnecessarily slow and complicated… You are just quoting and unquoting the result. This is useful in certain cases when you want to run the code (String in this example) but that’s not the case here.

Dear chaoge, I was just answering his question literally.

1 Like

I was under impression from documentation QuoteNode is needed to access the contents of __source__, while in fact I needed String(__source__.line) for usable filename as you tend to suggest in your helpful answer.

Here is what documentation says about accessing __source__.line, while in fact as an uneducated user of __source__ I need to hear about String(__source__.line) and probably never know of QuoteNode:

The location information can be accessed by referencing __source__.line and __source__.file :

julia> macro __LOCATION__(); return QuoteNode(__source__); end
@__LOCATION__ (macro with 1 method)

julia> dump(
            @__LOCATION__(
       ))
LineNumberNode
  line: Int64 2
  file: Symbol none

I was also caught by file equalling to nothing when using include() as well as a hint in the documentation that file name may not be recorded at all:

Source location information is represented as (line line_num file_name) where the third component is optional (and omitted when the current line number, but not file name, changes).

Reading through this makes one a bit confused why exactly one not simply getting a filename.

AFAICT, what you want to do is exactly @__FILE__

Not exactly, I needed a function or a macro without arguments that will trace the location on invocation in caller script, not location at point of definition at source. More here, if you are interested.

so you can also find the answer by reading the source code of the macro

That’s single most useful point of reference on my question, wish it was in the docs:

macro __FILE__()
    __source__.file === nothing && return nothing
    return String(__source__.file)
end

Finally, String(__source__.file)! Genuine thanks for taking me to there.

rather than blindly copying the code and trying things.

That’s what people do when lacking good reference or prior knowledge.

Again, please understand it before using the implementation. If you don’t, feel free to ask here again about the “why” part.

I appreciate your time and effort in trying to explain. My remaining pain point is why __source__.file is a Symbol to start with, not a String? __source__.line is an integer, not a Symbol. What are the uses of __source__.file as a Symbol other than converting it to a string?

… I mean @__FILE__ is what you want @file2 to do…

The result is exactly as @__FILE__, but the caller function/macro should not have arguments, so passing it explicitly as an argument was not an intended option.

In my scenario the user writes something like:

loc = @trace

instead of:

loc = trace(file=@_FILE_, line=@_line__)

Yes, I know. It’s what a lot of people do and that’s bad. What I’m saying is that you should start from the basic questions, e.g. you can ask why is QuoteNode needed to access __source__ and you can expect a detailed explaination about why that’s not actually the case. I know it’s hard to resist but please try to understand every part of a code you use. That way you won’t get the very strange, yet somewhat informative, error you get from @file2 to begin with. (I would also say that the question below about Symbol vs String is certainly on the right track)

It shouldn’t really be a problem for any user. Symbol is a pretty common type that is very easy to convert to String if needed. One question you should ask yourself here is whether you actually need a String. Functionwise, there isn’t a difference between the two here (both of them can store the same info) but Symbol saves memory in this case since the user of it will never be freed and it is referenced many time from the same file. (It is not common to have a similar usage pattern so don’t use that in your own code unless you understand the trade-off much better).

… = = … Errr, I know the thing in your actual code is completely different. I also know that you want to know about the implementation rather than using the macro directly which is why I pointed you there… I know you already successfully identify your issue to be implementing @file2 so I’m just saying that @file2 is @__FILE__. By "you want @__FILE__" I never meant that you should use @__FILE__ in any way but what you wanted to implement in the simplified/minimized problem is actually @__FILE__… Hopefully that clears up what I meant, although it probably doesn’t matter…

2 Likes

One question you should ask yourself here is whether you actually need a String .

I think I do for reading the file contents.

I need to push back very strongly against this. Experimentation and parroting is a totally valid method of gaining knowledge. Perhaps it’s not how you work — and that’s fine! I find threads are much more successful when folks are actively trying things and posting what didn’t work. Further, you cannot expect someone just learning the language to always have a 100% thorough and accurate understanding of their code before running anything. They learn some bits, parrot some others, until they gain a more robust understanding of their knowledge and how things work.

Please be a little more gracious — I do not want folks to be timid about posting things they tried.

3 Likes

That’s fair.

Not completely and that’s also why we get a lot of invalid code…
By experiementing you collect data of what works and what not. It’s only when you know why you get the result you get that you get knowledge. Things that seems to work doesn’t really mean it works for the reason you think it works and I don’t think I need to give more examples about the undefined unsafe code that people have written.

To be clear, I have absolutely no problem with trying things around, but blinding trying things until something works is not acceptable (not what’s happening here) and when things work you should certainly have some understanding of why it works. In this case,

I was under impression from documentation QuoteNode is needed to access the contents of __source__

is certainly the problem. If @epogrebnyak actually just get this impression by reading the text from the document, then I apologize and the doc text should be fixed instead. If this idea was concluded by reading the code (including the code examples in the doc), then that was exactly my point. Copying and pasting code without understanding is going to lead to problem like this in the future. For a complex case it might not be practical and you might not need to make any adjustment to the copied code so it’s probably fine to let it be and simply use it as a blackbox library. For a simple case like this, one should definately understand why something is needed before using the code.

Yes, I agree with this. In fact, given the previous assumption I had absolutely no issue with how this particular issue is tried out. As mentioned above, all my comments about how one should understand better is targeted to the original @__LOCATION__/@file1 macro. Had that one be understood (or explained) better, this would not have been an issue to begin with.

I also just realize that by “understand every part of a code” I don’t mean to, well, every bit of the code, if that makes sense. I’m saying that in macro file1() QuoteNode(__source__.file) end one should know why QuoteNode is there but it’s certainly too much to require understanding the full semantics of macros and QuoteNode outside the context of the particular input type/usage pattern. I think this is very basic and I don’t think that’s too much to ask. I also don’t expect people to natually do that and I certainly expect beginners to not do that and run into problem because of it.

Exactly. Again, the way this question was asked was perfectly fine. More understanding should have been extracted from the original use of QuoteNode.

Again, all tried things in this one is fair for the knowledge known at the time. I’m only commenting on the obvious gap in understanding revealed by it. If I was not clear enough yet, doing what was done in the top post is exactly how you should try things. The result is strange this time because of a missing piece in a prevous learning that could improve easily if you ask a little more everything you get a working code.

1 Like