-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce -m/--module flag to execute a main
function in a package
#52103
Conversation
Can we make it so that a "less to write" version doesn't clutter the main environment by default? As is, if you want to keep environments clean you have to do something like
which is super verbose. IMO tying executables/apps/<things-with- |
For CLI apps that you would run with just its name ( However, for this feature, which is quite a lot more limited in scope, I think it makes sense to basically see it as just a shorthand syntax for loading a package and running a function in it. Having to create and resolve a new environment which might have different versions of what you have installed etc feels like it would be annoying. And then to update things you would have to go activate and update all these environments etc. |
OK, but isn't this entire PR/idea based around CLI apps? What use other than running CLI apps does this have, since that's also the motivating example above? We already have I really don't think citing Python is a good example of how this can be done - python packaging like that is the cause of many problems for sysadmins. |
Yes, but it isn't so easy to propagate arguments to that, for example:
or work with stdin + piping etc. |
That's not a good argument for why the "path of least resistance" for CLI apps should clutter the default environment by default. |
It seems to me like Then the question is what happens if Rot13 isn't in that environment. It'd be an error here. And perhaps it should automatically do what you suggest (that is, create/use a named env, probably with a prompt on creation/install)... but that seems to be an "extra" feature that's pretty orthogonal, no? |
I don't think so - we don't really have CLI apps in the ecosystem precisely because it's a pain to use them with environments (not to mention startup time). Isolating them in an environment by default means we can precompile that environment completely for the I think it's worthwhile to think the usecase through here, and weigh the up & downsides before settling on an approach. To me, there are very clear UX, update & recompile downsides to making the "default environment" the "easy path", with very little (no?) upside (other than "it's how python does it", which is more of a downside to me..). |
This too is highly dependent on the package in question, not on this interface. I could just as well have written |
I think this would mostly be used for packages that can have both functionality as a library and a package. For example, the |
Very possible but could you perhaps just show it explicitly to make your point a bit clearer? |
That sounds much more like an argument for "don't clutter the default environment with CLI apps because it will be held back by other, unrelated stuff in that environment" than an argument for making this use the default environment by default. Do we really want to have lots of bug reports about old versions of CLIs floating around in the ecosystem? I think the SciML ecosystem still struggles with those because of old docs pages & old tutorials showing up on google.
Sure:
I've put the package into its own environment because I don't want random CLIs to clutter my main environment, and I don't want those to be held back by other random stuff in my main environment. |
The key question is what should |
I don't think it should. There should be a very clear seperation between uses as a library and uses as a packaged CLI/application. I think this should always use its bespoke environment. If we want to look for other ecosystems for inspiration, can we take a look at e.g. Rust? Their CLI game is well known to be amazing, so why not take a page out of their book? On the other hand, how do you propose to deal with held back packages because of CLI apps? Or the other way around? |
Thanks! So to be clear, the concrete suggestion here is for
|
if I understand correctly
this only happens once, on install? I'm no expert on CLI design, but imo makes more sense for However, definitely also feels like a reasonable ask to make isolated envs for cli apps ergonomic, but I think that's orthogonal feature and shouldn't be default? |
As I said before, I do think there is a difference between having a "driver" like I think there is value in providing both these features but the second one is significantly more design heavy. |
I agree fully, and I had typed up an almost identical comment but got sniped. In particular, the current behavior lets me choose/control the environment, which can be useful sometimes. That wouldn't be possible with the other suggested approach. Also, people should really relax about the global environment, it is amazingly convenient. I rarely, if ever, run into problems related to this. Maybe I just don't use "troublesome" ecosystems or combinations of packages. (Disclaimer: this is just a meme that documents my personal development with environments in Julia over the years and not meant to be offensive to anyone 🙂 ) |
I see this as being as much about As far as the pains of add/activate/instantiate and (potentially) dedicated environments, those span many more usages than just |
How about on first use:
would make an empty environment named Rot13, download Rot13 for you, the latest version, and on subsequent use, use that environment (and people could customize it in the meantime)? There's a question where it would be stored, in the current dir or elsewhere. If Rot13 exists in the global environment (and maybe in each case if there's a conflict, then it could ask, "Do you want to use the global environment (once)? Or copy from it into a local environment? Or download latest version?". Could a local environment point to the global environment somehow? Or can a local environment program already do that selectively, if it wants? It seems you might want to support this the same way (at least now since not registered):
If you feel like that, and people's ideas differ, should it maybe work like that with -M Rot13?
I run into trouble all the time, I can't install packages, since my environment is too cluttered. FYI: Python has Zip-apps, that I think are though not much used, that allows a whole programs with all dependencies stored in one zipped file with .pyz ending. Julia wouldn't needs such if you can just point to an URL. |
All of these issues literally only exist because you've made the Taking a page out of the rust book, why must
The latter, similar to Though again, there is no need to have
It's a regular environment, so you can do something like
The dependencies of the CLI resolve as any other package environment does. If
No, you can use Though again, that is only a limitation of being forced to use
I find this sort of commentary unnecessarily provocative, counterproductive and generally inappropriate. If you don't have anything productive to add to the discussion, please keep your memes to yourself. You're painting any usecase other than your own as being whiny and annoying - perhaps consider that there are different folks with different requirements who have good reasons for wanting to make the path of least resistance more welcoming to traditional software folk. I find it generally discouraging that every single time I try to advocate for learning from the mistakes of other languages/ecosystems, people seem to double down on reinventing the wheel. |
So it seems you are arguing for having (perhaps only) the "standalone" app functionality I described in #52103 (comment) and #52103 (comment), (which is more similar to https://python-poetry.org/docs/pyproject/#scripts and https://doc.rust-lang.org/cargo/reference/cargo-targets.html#binaries.). I agree that that functionality is useful and it is also something I am actively thinking about (see e.g a screenshot from some thoughts scratch document about this): Maybe your worry is that CLI support in Julia would end with this PR in which case I understand (and would share) your worry. However, I would only consider this PR as a "little brother" to the standalone executable functionality not a replacement. This is still useful in smaller scale but you would indeed not want to write the next Dropbox using it ( As I wrote in #52103 (comment), there is a lot of design for the standalone CLI support and I would be very grateful for help with the design there. If you write to me I'll invite you to my scratch design document I screenshotted from above and we can iterate there. |
While true, it will look up dependencies in your project environment first which may be incompatible with other dependencies that gets found from the global env so it can cause quite weird bugs. I wouldn't want any standard use-case to rely on it.
Keep fighting the good fight, your input is valuable and appreciated :). |
Yes indeed - having this be the "main" thing for apps is part of it, but another is that having multiple ways to do kind-of similar-ish things (run a CLI) with potentially different behaviors is confusing to say the least (as shown in this thread). Thus, I think we either have to reconcile the ideas somehow, or decide on a canonical way this ought to work.
Sounds good - I'll write you on Slack. |
base/client.jl
Outdated
mods = split(arg, ".") | ||
pkg = popfirst!(mods) | ||
# Some more validation of `mods` and `pkg`? | ||
m = Base.require(Main, Symbol(pkg)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, technically this is all that is required if the module exports its main function - that was one of the explicit design goals of @main
that you could use it as -e 'using Foo'
to get the python app like behavior. I think it's fine to further shortcut that to -m
, but I'm not sure about duplicating the main
invocation logic here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay I agree with that. So if an app want to have multiple "functionalities" (like the proposed julia -m Foo/Bar
to run Foo.Bar.main
) would you do that as julia -m Foo --bar
or still do julia -m Foo/Bar
and then have that still call Foo.main
and look at something like Base.PROGRAM_FILE
to determine what to execute.
The issue I see with using the global environment is that such a call will be hardly reproducible. Someone incorporating a Of course then one can manage a specific environment for that manually, but then the utility of the default behavior is just limited to saving a few keystrokes, and hardly recomendable to a general user. Since code cashing, using clean temporary (or not) environments became convenient (locally available packages install quickly), and IMHO Julia should move in the direction to encourage keeping the global environment as clean as possible. After you install CUDA, Makie, or another heavyweight precompiled package, getting random precompilations is really annoying. I bet no one wants to run |
@Keno, can you look at this new implementation? |
Having these be reproducible is not the goal of this specific feature though. It is mostly a convenience to be able to write For "full-scale apps" something more powerful is probably needed yes but that would look completely different (#52103 (comment)). |
I think everyone against |
I've been toying a bit with the "standalone app" stuff I've mentioned before and I realize that in the implementation of that this would still be useful to have. The reason for that is that even in that case in the end you want to actually start Julia and run the entry point to the app. You can either use |
Bump... Does the last comment manage to persuade any of the nay-sayers? If not, what is the alternative? If the answer is |
From my POV, the concern with dumping everything in the default environment (as this PR currently does) has not been addressed 🤷 I'm aware that in a scenario of a standalone app with |
This is not "dumping anything in the main environment". It provides an option to start up an entry point. This is needed to implement the full feature. |
Here is the PR that builds on top of this JuliaLang/Pkg.jl#3772 |
I'm not one of the naysayers, but I'd just like to lend my voice in favour of this implementation and choice of environment semantic. This is the most natural and straightforward extension of the way that the |
base/client.jl
Outdated
elseif cmd == 'm' | ||
@eval Main import $(Symbol(arg)).main | ||
if !should_use_main_entrypoint() | ||
error("`main` in `$arg` not declared as entry point (using `@main`)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
error("`main` in `$arg` not declared as entry point (using `@main`)") | |
error("`main` in `$arg` not declared as entry point (use `@main` to do so)") |
That Pkg PR addresses all my usability concerns, and I see it builds on this one. So I'm in favor. And let me say, what an ambitious vision - it looks like the beginning of something amazing! |
This aims to bring similar functionality to Julia as the `-m` flag for Python which exists to directly run some function in a package and being able to pass arguments to that function. While in Python, `python -m package args` runs the file `<package>.__main__.py`, the equivalent Julia command (`julia -m Package args`) instead runs `Package.main(args)`. The package is assumed to be installed in the environment `julia` is run in. An example usage could be: Add the package: ``` (@v1.11) pkg> add https://github.com/KristofferC/Rot13.jl Cloning git-repo `https://github.com/KristofferC/Rot13.jl` Updating git-repo `https://github.com/KristofferC/Rot13.jl` Resolving package versions... Updating `~/.julia/environments/v1.11/Project.toml` [43ef800a] + Rot13 v0.1.0 `https://github.com/KristofferC/Rot13.jl#master` Updating `~/.julia/environments/v1.11/Manifest.toml` [43ef800a] + Rot13 v0.1.0 `https://github.com/KristofferC/Rot13.jl#master` ``` And then it can be run (since it has a `main` function) via: ``` ❯ ./julia/julia -m Rot13 "encrypt this for me" "and this as well" rapelcg guvf sbe zr naq guvf nf jryy ```
As I said above, if this were exclusive to Apps, I wouldn't have a problem with this. However, the overall commit message and NEWS entry don't make a mention of that, instead likening this to what Python does with having everything that's installed being dumped into the same "environment". It's this standalone usage, separate from the usage as Apps, that the "naysayers" are worried about. In order to use this feature standalone, the most straightforward way would be to just add everything someone wants to use into the main environment, with the consequences outlined above. This has not been addressed. Moreover, the current design in JuliaLang/Pkg.jl#3772 simply sets the JULIA_LOAD_PATH to exclusively use the installed app environment. To my understanding, the same thing could be achieved through an empty |
As an alternative - can the flag just be made much longer to disincentivize its use? After all, if its intended for usage from Apps (& thus autogenerated shims), there should be no harm in having a longer flag. @lmiq since you also mentioned opposition to the current behavior, would that be a good compromise for you? |
I like the current state of the PR and am excited to use if you want to control the project you're running a module in, |
Ok, how do you propose to deal with held back packages because of liberal usage of standalone As far as I've noticed, that hasn't been addressed by anyone in favor of liberal usage of standalone |
this does not seem like a likely problem to me so I don't think it needs a solution
if it becomes an issue, I'll put each cli into its own environment. but I don't want that to happen by default |
It's no worse than regular scripts invoked as |
I'll wait with merging this until after branching for 1.11. Looking at the comments here I think there is more or less a consensus to not do some behind the scene project modification. |
…uliaLang#52103) This aims to bring similar functionality to Julia as the `-m` flag for Python which exists to directly run some function in a package and being able to pass arguments to that function. While in Python, `python -m package args` runs the file `<package>.__main__.py`, the equivalent Julia command (`julia -m Package args`) instead runs `<Package>.main(args)`. The package is assumed to be installed in the environment `julia` is run in. An example usage could be: Add the package: ```julia (@v1.11) pkg> add https://github.com/KristofferC/Rot13.jl Cloning git-repo `https://github.com/KristofferC/Rot13.jl` Updating git-repo `https://github.com/KristofferC/Rot13.jl` Resolving package versions... Updating `~/.julia/environments/v1.11/Project.toml` [43ef800a] + Rot13 v0.1.0 `https://github.com/KristofferC/Rot13.jl#master` Updating `~/.julia/environments/v1.11/Manifest.toml` [43ef800a] + Rot13 v0.1.0 `https://github.com/KristofferC/Rot13.jl#master` ``` And then it can be run (since it has a `main` function) via: ``` ❯ ./julia/julia -m Rot13 "encrypt this for me" "and this as well" rapelcg guvf sbe zr naq guvf nf jryy ``` I'm not sure if `-m/--module` is the best choice but perhaps the association to Python makes it worth it.
…uliaLang#52103) This aims to bring similar functionality to Julia as the `-m` flag for Python which exists to directly run some function in a package and being able to pass arguments to that function. While in Python, `python -m package args` runs the file `<package>.__main__.py`, the equivalent Julia command (`julia -m Package args`) instead runs `<Package>.main(args)`. The package is assumed to be installed in the environment `julia` is run in. An example usage could be: Add the package: ```julia (@v1.11) pkg> add https://github.com/KristofferC/Rot13.jl Cloning git-repo `https://github.com/KristofferC/Rot13.jl` Updating git-repo `https://github.com/KristofferC/Rot13.jl` Resolving package versions... Updating `~/.julia/environments/v1.11/Project.toml` [43ef800a] + Rot13 v0.1.0 `https://github.com/KristofferC/Rot13.jl#master` Updating `~/.julia/environments/v1.11/Manifest.toml` [43ef800a] + Rot13 v0.1.0 `https://github.com/KristofferC/Rot13.jl#master` ``` And then it can be run (since it has a `main` function) via: ``` ❯ ./julia/julia -m Rot13 "encrypt this for me" "and this as well" rapelcg guvf sbe zr naq guvf nf jryy ``` I'm not sure if `-m/--module` is the best choice but perhaps the association to Python makes it worth it.
This aims to bring similar functionality to Julia as the
-m
flag for Python which exists to directly run some function in a package and being able to pass arguments to that function.While in Python,
python -m package args
runs the file<package>.__main__.py
, the equivalent Julia command (julia -m Package args
) instead runs<Package>.main(args)
. The package is assumed to be installed in the environmentjulia
is run in.An example usage could be:
Add the package:
And then it can be run (since it has a
main
function) via:I'm not sure if
-m/--module
is the best choice but perhaps the association to Python makes it worth it.