diff --git a/README.md b/README.md index 8aaaff6..95bbc9c 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ # App Mode Jupyter Environment and Shortcut -Adds a shortcut for running [JupyterLab](https://jupyter.org/) in a Chromium-based browser's "app" mode (i.e. The application takes up the full browser window and has no browser UI e.g. the address bar, as if Jupyter Lab were a native application) for an existing environment with Jupyter Lab installed or creating one for you. +Adds a shortcut for running [JupyterLab](https://jupyter.org/) in a Chromium-based browser's "app" mode (i.e. The application takes up the full browser window and has no browser UI e.g. the address bar, as if Jupyter Lab were a native application) for an existing environment with Jupyter Lab installed or creating one for you. ## Requirements * An conda-based installation of [Anaconda](https://anaconda.org/), [Miniconda](https://docs.conda.io/en/latest/miniconda.html) or [Miniforge](https://github.com/conda-forge/miniforge). Miniforge is recommended. * [`menuinst`](https://conda.github.io/menuinst/) >= 2.0.0 must be installed in the `base` environment alongside `conda` or in whichever environment contains conda * A Chromium-based browser - * On Windows, [Google Chrome](https://www.google.com/chrome/), [Brave](https://brave.com/), and [Microsoft Edge](https://www.microsoft.com/en-us/edge) are supported. Preference will be given to the first one of these browsers found. Microsoft Edge is guaranteed to be installed on Windows 10+ and is a fall-back if no other supported browser is found. + * On Windows, [Google Chrome](https://www.google.com/chrome/), [Brave](https://brave.com/), and [Microsoft Edge](https://www.microsoft.com/en-us/edge) are supported. Preference will be given to your default browser, if it appears to be a Chromium-based browser, or to the first one of these browsers found. Microsoft Edge is guaranteed to be installed on Windows 10+ and is a fall-back if no other supported browser is found. * On MacOS, [Google Chrome](https://www.google.com/chrome/), [Microsoft Edge](https://www.microsoft.com/en-us/edge), [Brave](https://brave.com/), and [Chromium](https://www.chromium.org/getting-involved/download-chromium/) are preferred in that order. * On Linux, [Google Chrome](https://flathub.org/apps/com.google.Chrome), [Microsoft Edge](https://flathub.org/apps/com.microsoft.Edge), [Brave](https://flathub.org/apps/com.brave.Browser), and [Chromium](https://flathub.org/apps/org.chromium.Chromium) are supported and preferred in that order. If [flatpak](https://flatpak.org/) is installed and one of these browsers is installed with Flatpak (recommended), it will be preferred over any snap-installed or package manager-installed versions of any of these browsers. ## Setup 1. Clone or download this repository -2. From a terminal, activate your `base` conda environment (or whichever environment conda is installed in) +2. From a terminal, activate your `base` conda environment (or whichever environment conda is installed in) 3. Run `python ./setup_jupyter.py your_jupyter_env_name_here` ## Remove Shortcut @@ -24,12 +24,12 @@ To remove the shortcut created by this script, run `python ./setup_jupyter.py -- ## FAQ 1. Why don't you support Mozilla Firefox or Safari? - + To my knowledge, neither have an "app-mode" like Chromium-based browsers do. When/if they ever do, they will be supported. 2. Why am I being prompted for admin permissions? - By default, `menuinst` tries to install/remove the shortcut from a system-level location. You can safely cancel/ignore that prompt(s) (it may prompt you multiple times) which will cause `menuinst` to fallback to a user-specific location instead. Unfortunately, there does not seem to be a way to have `menuinst` prefer to install/remove shortcuts for the current user rather than system-wide. + By default, `menuinst` tries to install/remove the shortcut from a system-level location. You can safely cancel/ignore that prompt(s) (it may prompt you multiple times) which will cause `menuinst` to fallback to a user-specific location instead. Unfortunately, there does not seem to be a way to have `menuinst` prefer to install/remove shortcuts for the current user rather than system-wide. 3. What about other Chromium-based browsers? @@ -49,8 +49,8 @@ To remove the shortcut created by this script, run `python ./setup_jupyter.py -- 7. What is `nb_conda_kernels` and why does this script want to install it in my Jupyter environment. - `nb_conda_kernels` allows JupyterLab to run kernels in **any** installed conda environment which has a Jupyter kernel (e.g. ipykernel for Python) installed in it without having to install Jupyter and all its dependencies into that conda environment. It allows you to have a single version of JupyterLab installed in a single, purpose-made environment so you are not maintaining multiple JupyterLab versions or configurations. + `nb_conda_kernels` allows JupyterLab to run kernels in **any** installed conda environment which has a Jupyter kernel (e.g. ipykernel for Python) installed in it without having to install Jupyter and all its dependencies into that conda environment. It allows you to have a single version of JupyterLab installed in a single, purpose-made environment so you are not maintaining multiple JupyterLab versions or configurations. 8. How should I update my Jupyter environment? - That's up to you. I will periodically run `conda update -n jupyter --all && conda update -n jupyter python` to update everything in my Jupyter environment (named `jupyter`). + That's up to you. I will periodically run `conda update -n jupyter --all && conda update -n jupyter python` to update everything in my Jupyter environment (named `jupyter`). diff --git a/jupyter_lab_config.py b/jupyter_lab_config.py index 81d5c69..a9a9b01 100644 --- a/jupyter_lab_config.py +++ b/jupyter_lab_config.py @@ -3,8 +3,9 @@ import platform from io import StringIO from itertools import product -from os import environ +from os import environ, pathsep from pathlib import Path +from re import search from subprocess import CalledProcessError, run logging.getLogger(__name__).propagate=True @@ -12,7 +13,7 @@ logging.getLogger(__name__).propagate=True def find_flatpak_browser(): if platform.system() != "Linux": return - + for path_element in map(Path, environ["PATH"].split(":")): if (path_element/"flatpak").is_file(): flatpak_bin = path_element/"flatpak" @@ -20,7 +21,7 @@ def find_flatpak_browser(): else: # flatpak is not installed return - + flatpak_apps = ("com.google.Chrome", "com.microsoft.Edge", "com.brave.Browser", "org.chromium.Chromium") try: @@ -39,15 +40,17 @@ def find_flatpak_browser(): else: with StringIO(flatpak_process.stdout.decode()) as flatpak_output: installed_flatpaks = flatpak_output.readlines()[1:] - + for flatpak_app in flatpak_apps: if flatpak_app in [_.strip() for _ in installed_flatpaks]: return f"flatpak run --filesystem={Path.home()/'.local/share/jupyter/runtime'} {flatpak_app} --start-maximized --profile-directory=Default --app=%s" def find_browser(): - # find a chromium-based browser on the system to use. + # find a chromium-based browser on the system to use. if platform.system() == "Windows": + from winreg import HKEY_CLASSES_ROOT, OpenKey, QueryValue + cmds = ( "Google/Chrome/Application/chrome.exe", "BraveSoftware/Brave-Browser/Application/brave.exe", @@ -55,6 +58,17 @@ def find_browser(): ) path_elements = map(Path, (environ["ProgramFiles"], environ["ProgramFiles(x86)"])) + # do a lookup of default browser command in windows registry + try: + with OpenKey(HKEY_CLASSES_ROOT, r"http\shell\open") as k: + default_browser_cmd = QueryValue(k, "command").replace('"%1"', "").replace('"', "").strip() + except FileNotFoundError: + pass + else: + # if the command looks like one of the known chromium browsers or even vaguely resembles one, return immediately with a browser command + if any(default_browser_cmd.endswith(cmd.replace("/", pathsep)) for cmd in cmds) or search(r"\\Application\\\w+\.exe$", default_browser_cmd): + return f'"{default_browser_cmd}" --start-maximized --profile-directory=Default --app=%s' + elif platform.system() == "Linux": # prefers Google Chrome on Linux due to popularity of the browser cmds = ("google-chrome", "microsoft-edge", "brave", "chromium") @@ -63,8 +77,8 @@ def find_browser(): elif platform.system() == "Darwin": # also prefers Google Chrome on MacOS due to popularity of the browser cmds = ( - "Contents/MacOS/Google Chrome", - "Contents/MacOS/Microsoft Edge", + "Contents/MacOS/Google Chrome", + "Contents/MacOS/Microsoft Edge", "Contents/MacOS/Brave Browser", "Contents/MacOS/Chromium", ) diff --git a/jupyterlab_shortcut.json b/jupyterlab_shortcut.json index 16a9920..717f41d 100644 --- a/jupyterlab_shortcut.json +++ b/jupyterlab_shortcut.json @@ -15,9 +15,18 @@ "icon": "{{ MENU_DIR }}/jupyterlab.{{ ICON_EXT }}", "platforms": { "win": { - "file_extensions": [ - ".ipynb" - ], + "precreate": "{{ BASE_PREFIX }}\\condabin\\conda.bat init cmd.exe", + "command": [ + "{{ BASE_PREFIX }}\\condabin\\conda.bat", + "run", + "--live-stream", + "--prefix", + "{{ PREFIX }}", + "jupyter", + "lab", + "--config={{ MENU_DIR }}/jupyter_lab_config.py" + ], + "activate": false, "quicklaunch": false }, "osx": { diff --git a/setup_jupyter.py b/setup_jupyter.py index 7fe1502..2285fd8 100755 --- a/setup_jupyter.py +++ b/setup_jupyter.py @@ -312,9 +312,19 @@ def ensure_env(env_name: str) -> Path: def main(target_env_name: str, remove_shortcut: bool=False) -> Union[int, None]: if not in_base_env(): LOGGER.warning("Not in base environment. Re-running this script from the base environment") + + # I don't know why, but Windows *really* does not want to resolve conda in PATH if you aren't in the base environment + try: + conda_exe = environ["CONDA_EXE"] + except KeyError: + try: + conda_exe = environ["_CONDA_EXE"] + except KeyError: + conda_exe = "conda" + # call conda run to re-run this in the base prefix rerun_proces = run( - ["conda", "run", "--prefix", str(get_base_prefix()), "--no-capture-output", "python", *argv], + [conda_exe, "run", "--prefix", str(get_base_prefix()), "--no-capture-output", "python", *argv], capture_output=False, check=False )