Skip to content

Commit

Permalink
Fix installation of JupyterLab extension. (#365)
Browse files Browse the repository at this point in the history
* Fix installation of JupyterLab extension.

* Prompt to auto-install NodeJS.

* Improve documentation for troubleshooting.

* Remove Compat dependency.

* Update TravisCI to not use nightly.
  • Loading branch information
twavv committed Dec 18, 2019
1 parent ae8c048 commit 99b5d78
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 50 deletions.
10 changes: 5 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ jobs:
julia: 0.7
env: TESTCMD="xvfb-run julia"
- os: linux
julia: 1.1
julia: 1.0
env: TESTCMD="xvfb-run julia"
- os: linux
julia: nightly
julia: 1.3
env: TESTCMD="xvfb-run julia"
- os: osx
julia: 0.7
env: TESTCMD="julia"
- os: osx
julia: 1.1
julia: 1.0
env: TESTCMD="julia"
- os: osx
julia: nightly
julia: 1.3
env: TESTCMD="julia"

- stage: "Documentation"
julia: 1.1
julia: 1.3
os: linux
script:
- julia --project=docs/ -e '
Expand Down
1 change: 0 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
Widgets = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
WebSockets = "104b5d7c-a370-577a-8038-80a2059c5097"
FunctionalCollections = "de31a74c-ac4f-5751-b3fd-e18cd04993ca"
Expand Down
114 changes: 87 additions & 27 deletions deps/jupyter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,46 +83,96 @@ function install_jupyter_serverextension()
return nothing
end

function find_condajl_jupyter_cmd(;
check_nodejs::Bool=false,
)::Cmd
conda_root = joinpath(first(Base.DEPOT_PATH), "conda", "3")
conda_bin_dir = joinpath(
conda_root,
@static(Sys.iswindows() ? "Scripts" : "bin"),
)

jupyter = joinpath(conda_bin_dir, exe("jupyter"))
if !isfile(jupyter)
error(
"Could not find the Conda.jl `jupyter` executable " *
"(is it installed?)."
)
end

# We need to add Conda's bin directory to the PATH so that jupyterlab can
# detect nodejs.
conda_env = copy(ENV)
conda_env["PATH"] = string(conda_bin_dir, ":", conda_env["PATH"])
cmd = Cmd(`$jupyter`, env=conda_env)

if check_nodejs
node_exe = joinpath(conda_bin_dir, exe("node"))
if !Sys.isfile(node_exe)
install_node = Base.prompt(
"NodeJS is not installed in your Conda environment " *
"but is neccessary to install the JupyterLab extension. " *
"Install it? [Y/n]"
)
if isyes(install_node)
@eval Main using IJulia
@eval Main.IJulia Conda.add("nodejs")
end
end
end

return cmd
end

function find_path_jupyter_cmd()::Cmd
jupyter = Sys.which("jupyter")
if jupyter === nothing
error("Could not find `jupyter` executable in PATH.")
end
return `$jupyter`
end

"""
find_jupyter_cmd()
find_jupyter_cmd([; conda=false])
Find the most likely candidate for the `jupyter` executable.
This will locate `jupyter` by searching the `PATH` environment variable and,
if not found, tries to return Conda.jl's jupyter.
If both of these approaches fail, an error is thrown.
"""
function find_jupyter_cmd(; force_conda_jupyter::Bool=false)::Cmd
function find_jupyter_cmd(;
# DEPRECATED: use `condajl` keyword argument
force_conda_jupyter::Union{Nothing, Bool}=nothing,
condajl::Union{Nothing, Bool}=force_conda_jupyter,
check_nodejs::Bool=false,
)::Cmd
# Try to find the "system" Jupyter (unless we explicitly want to force
# using IJulia's Jupyter installation).
if !force_conda_jupyter
jupyter = Sys.which("jupyter")
if jupyter !== nothing
return `$jupyter`
if condajl === nothing || condajl === false
try
return find_path_jupyter_cmd()
catch
# If conda is false, we explicitly don't want to look for jupyter
# in Conda.jl's installation, so we fail now.
# Otherwise, conda is nothing and we can continue searching.
if condajl === false
rethrow()
end
end
end

# Try to find IJulia/Conda.jl's Jupyter.
# This is a heuristic - it might be different. I'm not sure that there's
# an easy way to load Conda.jl (I tried adding it to extras but no dice).
conda_root = joinpath(first(Base.DEPOT_PATH), "conda", "3")
@static if Sys.iswindows()
jupyter = joinpath(conda_root, "Scripts", "jupyter.exe")
else
jupyter = joinpath(conda_root, "bin", "jupyter")
end

if isfile(jupyter)
return `$jupyter`
try
return find_condajl_jupyter_cmd(check_nodejs=check_nodejs)
catch
if condajl === nothing
error("Could not find `jupyter` executable in PATH or Conda.jl.")
end
rethrow()
end
@error(
"Unable to find a jupyter executable after searching PATH and Conda.jl.",
PATH=ENV["PATH"], conda_root,
)
error("Could not find the Jupyter executable.")
end

"""
install_jupyter_labextension([jupyter]; force_conda_jupyter=false)
install_jupyter_labextension([jupyter]; condajl=false)
Install the Jupyter Lab extension for WebIO using the specified `jupyter`
executable.
Expand All @@ -137,12 +187,14 @@ provides some more information (and caveats) about the relationship between
Jupyter Lab and WebIO.
"""
function install_jupyter_labextension(
jupyter::Union{Nothing, Cmd}=nothing;
force_conda_jupyter::Bool=false,
jupyter::Union{Cmd, Nothing}=nothing;
# Deprecated: use `condajl` keyword argument.
force_conda_jupyter::Union{Bool, Nothing}=nothing,
condajl::Union{Bool, Nothing}=force_conda_jupyter,
dev::Bool=isdev()
)
if jupyter === nothing
jupyter = find_jupyter_cmd(; force_conda_jupyter=force_conda_jupyter)
jupyter = find_jupyter_cmd(; condajl=condajl, check_nodejs=true)
@info(
"Using default Jupyter executable at $jupyter; to use a different "
* "executable, see the documentation by running "
Expand Down Expand Up @@ -229,3 +281,11 @@ function jupyter_config_dir()
end
jupyter_nbextensions_dir() = joinpath(jupyter_data_dir(), "nbextensions")
jupyter_nbconfig_dir() = joinpath(jupyter_config_dir(), "nbconfig")

isyes(s) = isempty(s) || lowercase(strip(s)) in ("y", "yes")

@static if Sys.iswindows()
exe(x::String) = endswith(x, "exe") ? x : string(x, ".exe")
else
exe(x::String) = x
end
4 changes: 4 additions & 0 deletions docs/src/providers/ijulia.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# IJulia

## WebIO Not Detected
For troubleshooting information, visit the [WebIO Not Detected](@ref) section
of the documentation.

## JupyterLab Considerations
JupyterLab presents a few issues that are different that than those associated
with the classic notebook.
Expand Down
40 changes: 28 additions & 12 deletions docs/src/troubleshooting/not-detected.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,27 @@ You may see this message in your frontend if WebIO is unable to find the active
WebIO instance in the browser.

## Troubleshooting Jupyter
This occurs most often when running in Jupyter when the frontend extensions
aren't installed correctly. Please check the [IJulia](@ref) documentation for
information about installing the frontend extensions. If you still have issues,
(after running the appropriate `WebIO.install_jupyter_XXXextension()` function
and refreshing the browser), keep reading.
WebIO requires the installation of a Jupyter extension to function properly.

Before continuing, make sure the correct Jupyter is the first `jupyter` in your
`PATH`.
If you're using Jupyter Notebook (classic), run the following.
```julia
using WebIO
WebIO.install_jupyter_nbextension()
```

If you're using Jupyter Lab, run the following.
```julia
using WebIO
WebIO.install_jupyter_nbextension()

# Or, if you are launching via IJulia.jupyterlab()
using WebIO
WebIO.install_jupyter_labextension(condajl=true)
```

Jupyter Lab poses some additional issues that the classic notebook does not.
For more information on those difficulties, read the [IJulia](@ref)
documentation.

### Jupyter Notebook
List all extensions by running
Expand All @@ -26,8 +39,8 @@ For example,
jupyter nbextension uninstall --user webio/main
jupyter nbextension uninstall --user webio-jupyter-notebook
```
(note that this may error if you don't actually have those extensions installed
and that's okay).
(note that this may print an error if you don't actually have those extensions
installed and that's okay).

Re-install the Jupyter notebook extension by running (at the Julia REPL)
```julia
Expand All @@ -42,9 +55,12 @@ using WebIO
WebIO.install_jupyter_labextension()
```

Note that JupyterLab has more pitfalls relating to which `jupyter` executable
is in the path. Make sure that the Jupyter you're using (you can determine this
by running `which jupyter`) is the Jupyter that you expect.
If you launch Jupyter by running `IJulia.jupyterlab()`, run the following
instead.
```julia
using WebIO
WebIO.install_jupyter_labextension(condajl=true)
```

## Still having problems?
Open a [GitHub issue](https://github.com/JuliaGizmos/WebIO.jl/issues/new).
Expand Down
11 changes: 7 additions & 4 deletions src/render.jl
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,13 @@ function Base.show(io::IO, m::MIME"text/html", x::Node)
document
.querySelector('[data-webio-mountpoint="$(mountpoint_id)"]')
.innerHTML = (
'<strong>WebIO not detected. Please read ' +
'<a href="https://juliagizmos.github.io/WebIO.jl/latest/troubleshooting/not-detected/">the troubleshooting guide</a> ' +
'for more information on how to resolve this issue.' +
'</strong>'
'<div style="padding: 1em; background-color: #f8d6da; border: 1px solid #f5c6cb">' +
'<p><strong>WebIO not detected.</strong></p>' +
'<p>Please read ' +
'<a href="https://juliagizmos.github.io/WebIO.jl/latest/troubleshooting/not-detected/" target="_blank">the troubleshooting guide</a> ' +
'for more information on how to resolve this issue.</p>' +
'<p><a href="https://juliagizmos.github.io/WebIO.jl/latest/troubleshooting/not-detected/" target="_blank">https://juliagizmos.github.io/WebIO.jl/latest/troubleshooting/not-detected/</a></p>' +
'</div>'
);
}
</script>
Expand Down
2 changes: 1 addition & 1 deletion src/scope.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export Scope,
import!,
addconnection!

import Compat.Sockets: send
import Sockets: send
import Observables: Observable, AbstractObservable, listeners

# bool marks if it is synced
Expand Down

0 comments on commit 99b5d78

Please sign in to comment.