How do I determine whether a julia script is included as module or run as script?
Asked Answered
C

3

10

I would like to know how in the Julia language, I can determine if a file.jl is run as script, such as in the call:

bash$ julia file.jl

It must only in this case start a function main, for example. Thus I could use include('file.jl'), without actually executing the function.

To be specific, I am looking for something similar answered already in a python question:

def main():
    # does something

if __name__ == '__main__':
    main()

Edit: To be more specific, the method Base.isinteractive (see here) is not solving the problem, when using include('file.jl') from within a non-interactive (e.g. script) environment.

Cochise answered 20/3, 2018 at 23:35 Comment(1)
github.com/JuliaLang/julia/issues/572 is also related, but solves the problem in an interactive session onlyCochise
T
4

The global constant PROGRAM_FILE contains the script name passed to Julia from the command line (it does not change when include is called).

On the other hand @__FILE__ macro gives you a name of the file where it is present.

For instance if you have a files:

a.jl

println(PROGRAM_FILE)
println(@__FILE__)
include("b.jl")

b.jl

println(PROGRAM_FILE)
println(@__FILE__)

You have the following behavior:

$ julia a.jl
a.jl
D:\a.jl
a.jl
D:\b.jl

$ julia b.jl
b.jl
D:\b.jl

In summary:

  • PROGRAM_FILE tells you what is the file name that Julia was started with;
  • @__FILE__ tells you in what file actually the macro was called.
Twitch answered 21/3, 2018 at 0:0 Comment(2)
So a statement like basename(PROGRAM_FILE) == basename(@__FILE__) seems to return true for called as script and false for e.g. include?Cochise
In general yes. But you might have a problem if you have two files with the same name in different directories.Thessalonian
E
4

tl;dr version:

if !isdefined(:__init__) || Base.function_module(__init__) != MyModule
  main()
end

Explanation:

There seems to be some confusion. Python and Julia work very differently in terms of their "modules" (even though the two use the same term, in principle they are different).

In python, a source file is either a module or a script, depending on how you chose to "load" / "run" it: the boilerplate exists to detect the environment in which the source code was run, by querying the __name__ of the embedding module at the time of execution. E.g. if you have a file called mymodule.py, it you import it normally, then within the module definition the variable __name__ automatically gets set to the value mymodule; but if you ran it as a standalone script (effectively "dumping" the code into the "main" module), the __name__ variable is that of the global scope, namely __main__. This difference gives you the ability to detect how a python file was ran, so you could act slightly differently in each case, and this is exactly what the boilerplate does.

In julia, however, a module is defined explicitly as code. Running a file that contains a module declaration will load that module regardless of whether you did using or include; however in the former case, the module will not be reloaded if it's already on the workspace, whereas in the latter case it's as if you "redefined" it.

Modules can have initialisation code via the special __init__() function, whose job is to only run the first time a module is loaded (e.g. when imported via a using statement). So one thing you could do is have a standalone script, which you could either include directly to run as a standalone script, or include it within the scope of a module definition, and have it detect the presence of module-specific variables such that it behaves differently in each case. But it would still have to be a standalone file, separate from the main module definition.

If you want the module to do stuff, that the standalone script shouldn't, this is easy: you just have something like this:

module MyModule
  __init__() = # do module specific initialisation stuff here
  include("MyModule_Implementation.jl")
end

If you want the reverse situation, you need a way to detect whether you're running inside the module or not. You could do this, e.g. by detecting the presence of a suitable __init__() function, belonging to that particular module. For example:

### in file "MyModule.jl"
module MyModule
  export fun1, fun2;
  __init__() = print("Initialising module ...");
  include("MyModuleImplementation.jl");
end

### in file "MyModuleImplementation.jl"
fun1(a,b) = a + b;
fun2(a,b) = a * b;

main() = print("Demo of fun1 and fun2.     \n" *
               "  fun1(1,2) = $(fun1(1,2)) \n" *
               "  fun2(1,2) = $(fun2(1,2)) \n");

if !isdefined(:__init__) || Base.function_module(__init__) != MyModule
  main()
end

If MyModule is loaded as a module, the main function in MyModuleImplementation.jl will not run.

If you run MyModuleImplementation.jl as a standalone script, the main function will run.

So this is a way to achieve something close to the effect you want; but it's very different to saying running a module-defining file as either a module or a standalone script; I don't think you can simply "strip" the module instruction from the code and run the module's "contents" in such a manner in julia.

Estop answered 21/3, 2018 at 11:52 Comment(2)
Unfortunately this code gets me ERROR: LoadError: ArgumentError: isdefined: too few arguments (expected 2)Empower
@Empower this answer is from 2018, presumably an earlier version of julia, which has changed rapidly since. There's probably been some minor syntactical change to the isdefined function (e.g. having to specify the module explicitly). Have a look at the documentation of isdefined.Estop
W
3

The answer is available at the official Julia docs FAQ. I am copy/pasting it here because this question comes up as the first hit on some search engines. It would be nice if people found the answer on the first-hit site.

How do I check if the current file is being run as the main script?

When a file is run as the main script using julia file.jl one might want to activate extra functionality like command line argument handling. A way to determine that a file is run in this fashion is to check if abspath(PROGRAM_FILE) == @__FILE__ is true.

Wreck answered 23/5, 2022 at 6:44 Comment(1)
Using abspath() solved my problem with inconsistent path format for PROGRAM_FILE.Dissolution

© 2022 - 2024 — McMap. All rights reserved.