Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make `eval` to use specific path to resolve `include` in Julia?

Tags:

julia

This expression eval(Meta.parse("begin $(code)\nend")) would eval Julia code with include resolved relative to the file eval... is defined in.

How to change it so that it would use another directory? Something like

eval(Meta.parse("begin $(code)\nend"), resolve_include_relative_to=somepath)

Or, if that's not possible - relative to current directory (like REPL)?

UPDATE

Possible solution - replacing relative paths with absolute

function fix_include(code::String, relative_path::String)::String
  code = replace(code, r"include\(\"\./(.*?)\"\)" => s"include(\"__relative_path__/\1\")")
  code = replace(code, r"__relative_path__" => relative_path)
  code
end

eval(Meta.parse("begin $(fix_include(code, relative_path))\nend")

Use case:

I'm evaluating snippets of string code, sometimes they contain include statement with relative paths and they resolved against wrong path. I want to explicitly specify tell it what path should be used for resolution. Or at the very least always use the current directory '.', not the directory where the file with the eval(xxx) line defined ./lib/runner.jl.

like image 919
Alex Craft Avatar asked Oct 15 '22 07:10

Alex Craft


2 Answers

This function should do the trick (include is relative to the path in task-local storage, as kinda indicated by the docstring):

function eval_at(code; path = "none", mod = Main)
    tls = task_local_storage()
    hassource = haskey(tls, :SOURCE_PATH)
    hassource && (path′ = tls[:SOURCE_PATH])
    # setting this is enough for `include` to be correct
    tls[:SOURCE_PATH] = path

    try
      # let's use the three-arg `include_string` here to make sure `@__FILE__`
      # etc resolve correctly
      return include_string(mod, code, path)
    finally
      hassource ?
        (tls[:SOURCE_PATH] = path′) :
        delete!(tls, :SOURCE_PATH)
    end
end

Example usage:

julia> pwd()
"/home/pfitzseb/Documents"

julia> isfile("test.jl")
false

julia> include("test.jl")
ERROR: could not open file /home/pfitzseb/Documents/test.jl

julia> eval_at("""include("test.jl")""", path = "/home/pfitzseb/foo.jl")
Main.LogT

julia> eval_at("""@__FILE__""", path = "/home/pfitzseb/foo.jl")
"/home/pfitzseb/foo.jl"
like image 73
pfitzseb Avatar answered Oct 21 '22 04:10

pfitzseb


It is not clear what exactly you want to do but for "do something in a folder" situations usually cd() do ... end syntax works great.

 code = quote
     cd("c:/temp") do
         println("do something in $(pwd())")
         #do something more
     end
 end

And now use it

julia> eval(code)
do something in c:\temp

Depending in your scenario you might consider using macros to manipulate code blocks that do something in a directory. Below is a simple example not offering more functionality than cd ... do ... end statement but of course it can be extended:

macro doinfolder(folder, what)
    isa(what, Expr) || error("what needs to be some expression")
    quote
        cd($folder) do
            $what
            #other useful code injections can occur here...
        end
    end
end

And now use it

julia> @doinfolder "C:\\temp" pwd()
"C:\\temp"

Will also work with more complex code structures

julia> @doinfolder "C:\\temp" begin
       pwd()
       end
"C:\\temp"
like image 20
Przemyslaw Szufel Avatar answered Oct 21 '22 03:10

Przemyslaw Szufel