Idea: New julia CLI switches (`-m` and `--pkg`)

  1. -m: shorthand for -e using Module: Module
julia -e "import Pluto; Pluto.run()"

is a pattern I happen to use very often

With this idea it could get converted to

julia -m Pluto

which could even run pluto as-is (invoke the run or serve by default), but it probably makes sense to actually pass some command, like

julia -m Pluto -e "run()"

  1. Pkg operations with --pkg

This is usually used when we download a project file from the internet, and we need to prepare our environment, or when we build Dockerfiles
julia --project=some_project_from_colleage -e "import Pkg; Pkg.instantiate(); Pkg.precompile()"

What if we made this look like
julia --project=some_project_from_colleage --pkg=all|instantiate|precompile|resolve|generate

Of course that has some overlap with the previous one, as it could also be written

julia --project=some_project_from_colleage -m Pkg -e "instantiate(); precompile()"

These could even be chained

julia --project=some_project_from_colleage -m Pkg -e "instantiate(); precompile()" -m Pluto -e "run()"

But I think julia --project=some_project_from_colleage --pkg -m Pluto -e "run()" would be nicer. On the other hand, I realize that using Module already runs some code (in the __init__), so packages could hook into the “no -e” -m Module syntax (Genie notoriously runs a lot of code in __init__)

More nice examples:

julia -m Dates -E "now()"
julia -m UUIDs -E "uuid4()"

What do you think? Would these and/or other additions to cli switches make sense?

3 Likes

Yes, -m seems warranted (while not strictly needed).

At least Python has such:

Package names (including namespace packages) are also permitted. When a package name is supplied instead of a normal module, the interpreter will execute <pkg>.__main__ as the main module. This behaviour is deliberately similar to the handling of directories and zipfiles that are passed to the interpreter as the script argument.

I think you have this in mind for only (registered) Julia packages. I’ve not used Python enough to use this much. Is it mostly used for the same or rather for “normal” modules, and is that what you would like to work too?

Does it make sense to allow many modules -m mod1;mod2 (I had a lot of trouble getting that to work in Python and gave up)?

I believe the current environment is used by default, and some have suggested no env. should be the default (e.g. for Pluto), I think that’s --project=.. Should it be implicit when -m is used?

1 Like

If this were to be implemented, I definitely think it should try to treat the current working directory as a package. It would be awesome to be able to share a project with someone and just tell them to run julia -m MyPackage. I know that this is equivalent to having a script.jl file someone could run, but I think the -m syntax is nicer.

I don’t think so. I mean it doesn’t seem warranted just for a synonym (is that what happens in Python?). It should look up the (precompiled) module in a package, that you must have preinstalled. Same as in Python. What I mention as a possiblity, an empty env., is probably nonsensical.

Python needs to compile projects you run, and store somewhere. I don’t think that ever happens, no more than for individual Julia files. Is that something we could or would want for julia, then store in same directory? And possibly have the “zipfile” option Python has (see fuller updated quote in my previous comment).

[One other thing, Python has .py and .pyw (for pythonw) on Windows. Julia also needs the endings .jl and e.g. .jlw on Windows. Not sure it enters into this discussion.]

In Python, any directory can become a package by adding a __init__.py flag. You can make the package executable by adding a __main__.py file. All the packages in your current working directory are available to run or execute. So this works:

# my_pkg/__main__.py
print("hello world")
$ python -m my_pkg
hello world

However, the dependencies of that package are not guaranteed. Everything gets executed in whatever the current environment is, and this environment is where all the compiled code is stored.

For Julia, since we have such nice built-in dependency management, I think the logic would be more like

  1. User calls -m flag on x module name.
  2. Look for modules installed in current environment, if found, run it.
  3. Look for packages in the current working directory. If found, activate its environment, instantiate, and run it.

Does this make sense? I mean, in Python you can run an installed package or code in the working directory, and maybe that’s useful, but at least I think it will be similarly fast.

In Julia you can define a module and have it in some file, but I’m not even up-to-speed if you can have packages in your current directory. I’ve never made a package, only addationally deved packages from others. They are always under .julia. I suppose you’re right and they could be local when you first dev then before registration, and now curious are they handled the same, i.e. precompiled.

I don’t know if we need installed packages and in current dir, but if there’s a huge speed penalty, then I’m not sure we want that option, rather only support installed packages? Possibly this 3rd option could be skipped and always added later (defer that decision)? Or would it be a braking change?

1 Like

I hear your argument, for sure. We need to consider start time and its impact on useability. I would be behind adding the feature for installed packages only as a starting point!

I have two points where I’d push back though.

First, when I share a project with someone who doesn’t normally use Julia, I need to tell them to do the following:

$ git clone https://github.com/mrufsvold/mypackage
$ cd mypackage
$ julia
julia> ]
pkg> activate .
pkg> instantiate
julia> include("script.jl")

Alternatively, I can, of course, wrap the julia logic up in a script, but it’s a little silly to have to add the Pkg dance to the top when it’s the same for everyone.

My proposal is to just wrap up all that logic as automatic so that the following would do the same:

$ git clone https://github.com/mrufsvold/mypackage
$ julia -m mypackage

It’s the same speed, but yields less redudancy for me and mental overhead for my colleague.

Second, as long as you’re activating the project’s enviornment, code caching should still apply, so while there is certainly a tradeoff to be considered about the speed of the top-level logic, the dependencies would still be precompiled and fast after first use.

1 Like

I’m re-reading @pankgeorg original proposal, and I’ve definitely got astray here :sweat_smile: I brought too much of my python background to the conversation, I think.

I like the simple, understandable idea of -m package == using package: package, but it is incongruent with my idea of wrapping up instantiation and such to make local projects runnable. I retract my proposal, because explicitness is good.

yes, even Python agrees nominally:

  • Explicit is better than implicit.

but as we all know, the idea of importing dir / files as “pkg” is super confusing and I think we should avoid borrowing that idea in Julia…

3 Likes

an improved version of this is being adopted upstream! Introduce -m/--module flag to execute a `main` function in a package by KristofferC · Pull Request #52103 · JuliaLang/julia · GitHub

3 Likes