I'm using Julia to autograde students' work. I have all of their files Student1.jl
, Student2.jl
, etc. as separate modules Student1
, Student2
, etc in a directory that is part of LOAD_PATH
. What I want to be able to do works completely fine in the REPL, but fails in a file.
macro Student(number)
return Meta.parse("Main.Student$number")
end
using Student1
@Student(1).call_function(inputs)
works completely fine in the REPL. However, since I'm running this in a script, I need to be able to include the modules with more metaprogramming that is currently not working. I would have thought that the exact same script above would have worked in a file Autograder.jl
by calling
@eval(using Student1)
@Student(1).call_function(inputs)
in a module Autograder
. But I get either an UndefVarError: Student1 not defined
or LoadError: cannot replace module Student1 during compilation
depending on how I tweak things.
Is there something small in Julia metaprogramming I'm missing here to make this autograding system work out? Thanks for any advice.
The code just as you have written works for me on julia versions 1.1.0, 1.3.1, 1.5.1, 1.6.0 and 1.7.0. By that I mean, if I add an inputs
variable and put your first code block in a file Autograder.jl
and run JULIA_LOAD_PATH="modules:$JULIA_LOAD_PATH" julia Autograder.jl
with the student modules in the modules
directory I get the output of the call_function
function in the Student1
module.
However if Autograder.jl
actually contains a module then the Student$number
module is not required into Main
and your macro needs to be modified accordingly:
module Autograder
macro Student(number)
return Meta.parse("Student$number") # or "Autograder.Student$number"
end
inputs = []
@eval(using Student1)
@Student(1).call_function(inputs)
end
Personally I wouldn't use a macro to accomplish this, here is one possible alternative:
student(id) = Base.require(Main, Symbol("Student$(id)"))
let student_module = student(1)
student_module.call_function(inputs)
end
or without modifying the LOAD_PATH
:
student(id) = include("modules/Student$(id).jl")
let student_module = student(1)
student_module.call_function(inputs)
end