Skip to content
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

Added continued WSL support #4346

Closed
wants to merge 2 commits into from
Closed

Added continued WSL support #4346

wants to merge 2 commits into from

Conversation

betteridiot
Copy link
Contributor

#4260 and jupyter/help#496 WSL support was broken with the update to using an intermediate tempfile. This is because WSL uses Linux-like paths that Windows does not handle.

If the user has setup a Xserver and runs a graphical browser within their WSL, the previous update would still work. However, if the user sets BROWSER to their Windows internet browser of choice, the tempfile path cannot be opened.

While the user can just copy & paste the URL into their browser, this isn't super helpful for beginning/intermediate level students.

This commit addresses that and dynamically opens the browser using the former one-time token method. This is dependent on the users platform having both 'Linux' as its system, and 'Microsoft' being present within the release. These two items should be true iff the user is on a WSL system.

All other cases should go through the tempfile method recently added.

#4260 and jupyter/help#496 WSL support was broken with the update to using an intermediate tempfile. This is because WSL uses Linux-like paths that Windows does not handle.

If the user has setup a Xserver  and runs a graphical browser within their WSL, the previous update would still work. **However**, if the user sets BROWSER to their Windows internet browser of choice, the tempfile path cannot be opened.

While the user can just copy & paste the URL into their browser, this isn't super helpful for beginning/intermediate level students.

This commit addresses that and dynamically opens the browser using the former one-time token method. This is dependent on the users platform having both 'Linux' as its system, and 'Microsoft' being present within the release. These two items should be true iff the user is on a WSL system.

All other cases should go through the tempfile method recently added.
@takluyver
Copy link
Member

Is there a reliable way to translate the Linux style path into a Windows one?

@betteridiot
Copy link
Contributor Author

There have been attempts within the WSL ecosystem: specifically wslpath. However, this cannot translate special directories (seen here like the WSL specific bindings to mounted drives, user home directories, etc.

Since there is no elegant/reliable way for WSL to create the file and translate the path such that it can be passed to a Windows browser, this PR checks to make sure that the user is within a WSL environment and checks to make sure they aren't using any x11 forwarding. If these conditions are met, it will spawn using a token instead of a file.

@takluyver
Copy link
Member

I don't think special directories are a big problem, because we can control where we put the file, and it looks like the translation is fairly straightforward for normal files. However, I guess it might be difficult to detect whether the default will launch a browser inside Linux (with a Linux path) or invoke a browser in Windows (so the path needs translating)?

@betteridiot
Copy link
Contributor Author

There is only one way to launch a browser from within the Linux WSL environment: the user has to have an Xserver installed on the Windows side and the Linux side needs the environment variable DISPLAY to be set.

Therefore, if you wanted a way to check if it is WSL and linux-based browser:

import os
from platform import uname

# Check to see if in a WSL environment
if uname().system == 'Linux' and 'Microsoft' in uname().release:
    if not os.environ['DISPLAY']:
        # This is a Windows-side browser
    else:
        # This is a Linux-side browser

@takluyver
Copy link
Member

And it would be unusual to have an X server set up but no browser installed in Linux (or installed but not configured as default?)

@betteridiot
Copy link
Contributor Author

betteridiot commented Jan 18, 2019

I guess the only way I can think of to be sure (after checking for the WSL environment above) is instead of checking for DISPLAY, look for BROWSER. The only way Jupyter can communicate to the Windows based browsers is if BROWSER is set.

This is in cases like mine, where I have both an Xserver and DISPLAY variable set, but I use BROWSER to go through my Windows-browser

@takluyver
Copy link
Member

Thanks. So if DISPLAY is set but BROWSER isn't, it must be a Linux browser. If BROWSER is set but not DISPLAY, it must be a Windows browser (or a terminal browser, but that won't work anyway). If they're both set, it's not certain what is being used, but there's a good chance it's a Windows browser.

Is it possible to see the command lines of processes from other users with WSL? The possibility of this in real Linux was what prompted the file-URL approach. If that's definitely not possible on WSL, I think detecting the platform and falling back to opening the http URL makes sense.

@betteridiot
Copy link
Contributor Author

betteridiot commented Jan 18, 2019

By default, a WSL environment does not set BROWSER. However, if the user sets BROWSER (as per https://jupyter-notebook.readthedocs.io/en/stable/config.html), Jupyter will default to whatever BROWSER is pointing to.

This means that the user (of a WSL environment) would only set BROWSER if they wanted to default to a Windows-based browser.

Therefore, if we check like so:

import os
from platform import uname

# Check to see if in a WSL environment
if uname().system == 'Linux' and 'Microsoft' in uname().release:
    if os.environ['BROWSER']:
        # This is a Windows-side browser
    elif os.environ['DISPLAY']:
        # This is a X11 Linux-side browser
    else:
        # Will not natively spawn a browser, so maybe just default to --no-browser

We can properly detect a WSL environment, and (within reason) select the appropriate URL/File to pass to the browser.

@raulf2012
Copy link

Hello,

How would this logic be affected by a user running WSL and doesn't set a $BROWSER variable, but instead populated their jupyter_notebook_config.py with a command linking to a Windows browser
(this is how I do it)

c = get_config()
c.NotebookApp.browser = 'chrome.exe -incognito --app=%s'
c.NotebookApp.open_browser = True

Does the above logic handle this case

@betteridiot
Copy link
Contributor Author

betteridiot commented Jan 18, 2019

Didn't think about that @raulf2012 . I have attempted to fix that here.

The code below would take into account user-overrides in the jupyter_notebook_config.py as well as explicitly set BROWSER variables within WSL.

def launch_browser(self):
    # If 'Linux' and 'Microsoft' are both in the uname, the user is within a
    # WSL environment
    unix_name = platform.uname()
    wsl_environ = 'Linux' in unix_name.system and 'Microsoft' in unix_name.release
    
    # Check to see if the user has overridden the browser to redirect 
    # to a Windows-based browser. If this is the case, then redirect should
    # be a token-based URL instead of a file
    wsl_win_browser = os.environ['BROWSER'] or '.exe' in self.browser
    wsl_redirect = wsl_environ and wsl_win_browser

    try:
        browser = webbrowser.get(self.browser or None)
    except webbrowser.Error as e:
        self.log.warning(_('No web browser found: %s.') % e)
        browser = None

    if not browser:
        return

    # If both of these conditions are True, it is a reasonable assumption
    # that the user is in a WSL environment and redirecting the notebook
    # to a Windows-based browser. Therefore, we spawn using a token-based URL
    if wsl_redirect:
        uri = self.default_url[len(self.base_url):]

        if self.token:
            uri = url_concat(uri, {'token': self.token})

    # Now handle all the cases that aren't WSL porting to Windows-based browser
    elif self.file_to_run:
        if not os.path.exists(self.file_to_run):
            self.log.critical(_("%s does not exist") % self.file_to_run)
            self.exit(1)

        relpath = os.path.relpath(self.file_to_run, self.notebook_dir)
        uri = url_escape(url_path_join('notebooks', *relpath.split(os.sep)))

        # Write a temporary file to open in the browser
        fd, open_file = tempfile.mkstemp(suffix='.html')
        with open(fd, 'w', encoding='utf-8') as fh:
            self._write_browser_open_file(uri, fh)
    else:
        open_file = self.browser_open_file

    # Now control the file/URL redirect
    if wsl_redirect:
        if browser:
            b = lambda : browser.open(url_path_join(self.connection_url, uri),
                                      new=self.webbrowser_open_new)
            threading.Thread(target=b).start()
    else:
        b = lambda: browser.open(
            urljoin('file:', pathname2url(open_file)),
            new=self.webbrowser_open_new)
        threading.Thread(target=b).start()

This will track both the `jupyter_notebook_config.py` overrides mentioned by #4346 (comment) and `BROWSER` environment variable overrides originally proposed.
@ucgummy
Copy link

ucgummy commented Mar 9, 2019

This also an issue running on a chromebook. Jupyter must be installed on a linux vm, but the chrome os browser does not have easy access to the linux file system.

@betteridiot
Copy link
Contributor Author

This is still an issue which requires some fun conda create commands:
My current workaround for the WSL people out there is:

conda create -n some_env "python>=3.7" jupyter jupyterlab notebook=5.7.2 "tornado<6" # etc

Note: because of tornado updates, if you don't downgrade it along with notebook, jupyter lab instances wont spin up correctly. This is shown on ContinuumIO/anaconda-issues#8789

@betteridiot
Copy link
Contributor Author

As of today, this issue is still present and prevents a straightforward jupyterlab/jupyter notebook installation on WSL systems. #4346 (comment) is still a good work around

@nathanielatom
Copy link

This issue also exists for Termux on Android. I'm +1 for simple config option to open the http localhost URL instead of a file, since that would work cross-platform. It would be up to the user to override the default behaviour iff they know they only have a single user on their platform.

@nathanielatom
Copy link

I believe this issue is resolved by #4999 and the 6.0.2 release and can be closed, right?

Thank you very much!

@blink1073
Copy link
Contributor

I believe this issue is resolved by #4999 and the 6.0.2 release and can be closed, right?

I agree, thanks all!

@blink1073 blink1073 closed this Jun 7, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 24, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants