-
Notifications
You must be signed in to change notification settings - Fork 5.1k
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
Nested shells should not be login (#5247) #5565
Conversation
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.
Nice, thanks!
@kevin-bates, you had marked this as 6.1 but I think because of the behavior change it should be 6.2. |
@blink1073 - this change fixes the regression that the previous change introduced in 6.0. With this change, typical notebook users (those starting notebook from a shell) will have terminals that retain the current environment (i.e., not using a login and equal to the pre-6.0 behavior), while those users gaining access to notebook servers launched from things like hub or running as a daemon, will launch terminals using a login shell (which addresses the intent of the 6.0 change). As a result, I view this more as bug fix than anything else and why it should be targetted for 6.1. |
Cool, sounds good, merging! |
Hmm - I was wanting to try to confirm proper behavior here before merging. If I launch notebook as a cron job (seemed to be the easiest way w/o Hub integration), I see the trimmed environment and SHLVL == 2! This implies the SHLVL approach is not adequate and we've essentially reverted the reason for @dmikushin's change in the first place. @dlukes - in what scenarios do you find SHLVL == 0? |
If I prefix by 'service command' with @dmikushin - would that behavior be sufficient? This doesn't seem sufficient in the Hub use case to me. @minrk, @Zsailer, @blink1073 - do any of you have additional ideas here? |
One "hook" I find in my environments (RHEL and mac) is that env
As a result, using this as the criteria for creating a login shell seems to work... if os.name != 'nt' and not os.environ.get("TERM"): In fact, using Thoughts? |
I run a JupyterHub instance on an Ubuntu 18.04 server and when I log in (i.e. a spawn a single-user server with SystemdSpawner), launch a notebook and run Binder seems to be configured the same way, if you want to try it out, I get the same behavior. Of course, I can imagine this may vary across *nix flavors and specific setups, particularly across spawners other than SystemdSpawner? If so, that's unfortunate, but I would argue that JupyterHub admins are typically more technically sophisticated than someone who runs the Jupyter notebook locally, and so they're in a better position to work around the issue. Contrast this with an individual user with a local install whose virtualenv gets overridden out of the blue.
Sorry, I have no idea if that would be more or less reliable than FWIW, running |
Thanks for the helpful response @dlukes. So clearly your use-case is covered by I'm learning that some shells don't use if os.name != 'nt' and (int(os.environ.get("SHLVL", 0)) < 1 or not os.environ.get("TERM")): Unless @dmikushin can confirm that using the |
It looks like not all shells implement It looks like checking for |
I just want to stress that we shouldn't lose sight of the fact that in the basic use case (= local installation), a non-login shell is the correct default. The typical local user will launch the notebook from a shell which presumably already has an appropriately configured environment, so it doesn't make sense to run the risk of breaking it (as can happen on macOS, cf. #5247) by running a login shell. Providing an escape hatch for more advanced use cases which don't have a login shell higher up the process tree, and therefore may lead to an incomplete environment, is definitely a good thing, but I think it should:
I thought checking At first glance, checking In summary, this is (unsurprisingly) a problem with any heuristic based on checking environment variables -- they can be overridden in every which way. So what about checking |
This may be no longer relevant if the
The If you run a shell script from As you've rightly pointed out, At that point though, it might be better to just use a dedicated env var (
I may be wrong, but I would tend to think that in most JupyterHub settings, |
I like the idea of using |
One potential problem I can think of is if a distribution provides a desktop launcher for the notebook which doesn't run it inside a terminal. Are there downstream packagers out there that do this? If so, it would be unfortunate, because literally the most beginner-friendly way of launching the notebook (= via the GUI) would do the wrong thing. Arguably though, not running inside a terminal is the wrong way to provide a desktop launcher for the notebook, because it hides potentially useful logging output from the server. Notably, the Anaconda Navigator launcher starts a terminal and runs the notebook server inside it, so that one would work fine. |
@captainsafia @rgbkrk does nteract launch the jupyter server in a system terminal? |
@timkpaine would this affect your custom launching logic? iirc you are launching subprocesses running the |
Ah, if the server is run in a docker container that is launched without the |
I'm testing the docker question now. |
I tried distro-specific ways of installing the notebook on Ubuntu, Debian, Fedora and Solus. The only package I found that provides a desktop launcher is this snap, and it opens a terminal, so that's fine.
Good point! I'm not sure what should be considered "correct" behavior in a Docker context, but I can see how having it be dependent on Docker command line switches is somewhat unfortunate. OTOH, people don't tend to muck about too much with shell configuration inside Docker containers, so maybe the login/non-login distinction doesn't matter as much there? The main argument against a login shell by default is that it might override manual changes to the environment. As in, stuff that the user literally types into the shell prior to launching the notebook, as opposed to stuff that lives in any (system-wide or user-specific) init files. In the Python world, a prime (and maybe the only?) example of that is virtualenvs (that's the bug that bit me in #5247). But running the notebook inside a virtualenv inside Docker sounds like belt and suspenders to me. IDK -- do people do that? If not, maybe it's not worth worrying too much about. |
Pulling down the jupyter docker image took longer than I thought, I'll have to experiment more later today. |
I thought of doing something like the following to walk up the process tree and see if there's a login shell (= a import os
from psutil import Process
def is_child_of_login_shell(pid):
for parent in Process(pid).parents():
if parent.cmdline()[0].startswith("-"):
return True
return False
print(is_child_of_login_shell(os.getpid())) But that doesn't work in a local GUI, because GUIs typically don't go through a login shell and use a custom mechanism to set up the environment. |
I'll catch up on the responses later today (thank you for the efforts - they're much appreciated!), but I wanted to clarify this comment from Steve...
I was merely using CRON as a means of launching a notebook instance in a manner similar to how a service might be launched. I didn't mean to imply that CRON-based launches are something we should try to support. I was just using it to generate one of these minimal configurations and how to work backwards from there in order to solve this.
@dlukes - I agree 100% and made comments similar to this in my original questioning of using |
Turns out I'm not feeling well enough to work today, I'll circle back to this. |
Sorry to hear you're not feeling well Steve - get better soon and thank you for spending time on this. (I spent some time with the David, I just caught up on your comments - wow - thanks for the great investigation! I too like the idea of leveraging if os.name != 'nt' and int(os.environ.get("JUPYTER_SHELL_LOGIN", not sys.stdout.isatty()): We'd document JUPYTER_SHELL_LOGIN to be '0' or '1' to simplify conversion to bool, etc. Another option would be ... we rollback everything! It turns out a solution was configurable all along ... ## Supply overrides for terminado. Currently only supports "shell_command".
c.NotebookApp.terminado_settings = {"shell_command": ['/bin/bash', '-l']} Since the ship has already sailed with 6.0 and the cc: @dmikushin |
I agree with all of the above (except for rolling back), thanks @kevin-bates! |
Of course! I'd completely forgotten about that, thanks for pointing it out :) Building on that: on reflection, the custom env var feels a bit inconsistent with how Jupyter is typically configured. Correct me if I'm wrong, but AFAIK, the entire ecosystem systematically uses config files and command line switches for this purpose. There are a few env vars here and there, but a potential Moreover, it seems like shell_override = nb_app.terminado_settings.get('shell_command')
shell = (
[os.environ.get('SHELL') or default_shell]
if shell_override is None
else shell_override
)
# When the notebook server is not running in a terminal (e.g. when
# it's launched by a JupyterHub spawner), it's likely that the user
# environment hasn't been fully set up. In that case, run a login
# shell to automatically source /etc/profile and the like, unless
# the user has specifically set a preferred shell command.
if os.name != 'nt' and shell_override is None and not sys.stdout.isatty():
shell.append('-l') (Here's the commit/diff: dlukes@fad1ce5) This is also better for edge cases such as someone using a very weird shell which doesn't accept an I don't know, just my two cents :) I realize I'm an outsider and my perspective may be fairly limited. |
@dlukes - thank you again.
You're absolutely correct in that Notebook has tried to avoid the use of env variables - although I think they are much more useful in container landscapes, etc. I think your latest solution provides all the necessary "options" for navigating this minefield and I'd be okay with that. This is another hybrid approach that, I believe, addresses things in the best way so far. @blink1073 (once you're feeling better) - what do you think? |
That's a very good point :) I just wanted to put that alternative out there, because I guess I have an aesthetic preference for not introducing another configuration option when one already exists that does the job perfectly well. So I was really glad you'd unearthed |
I support dlukes/notebook@fad1ce5 as well, thanks to you both! |
@dlukes - could you please create another PR? |
@kevin-bates Sure, happy to! #5588 |
Fixes #5247. Opened in response to #4112 (comment) under #4112.