Skip to content

Commit

Permalink
Fix %APPDATA% directory on Windows (for real this time). (#286)
Browse files Browse the repository at this point in the history
* Fix %APPDATA% directory on Windows (for real this time).
* Write to both the .py and .json notebook config files.
* Don't use get! macro.
  • Loading branch information
twavv authored May 25, 2019
1 parent 119a22b commit 62b85aa
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 75 deletions.
151 changes: 76 additions & 75 deletions deps/build.jl
Original file line number Diff line number Diff line change
@@ -1,112 +1,113 @@
using JSON

include("./jupyterdirs.jl")

const BEGIN_MARKER = "###JULIA-WEBIO-CONFIG-BEGIN"
const END_MARKER = "###JULIA-WEBIO-CONFIG-END"
function install_ijulia_config()
config_file = joinpath(homedir(), ".jupyter", "jupyter_notebook_config.py")
if isfile(config_file)
config_str = String(read(config_file))
else
mkpath(dirname(config_file))
config_str = ""
end
"""
install_notebook_config()
Install necessary configuration for the `jlstaticserve` notebook (server)
extension. This function only configures the notebook extension, not browser
nbextension.
* Adds the path to `./deps` to Python's `sys.path` so that we can load the
`jlstaticserve.py` extension. This is done in `jupyter_notebook_config.py`
because there's no way to add to `sys.path` from the JSON config file.
* Adds `jlstaticserve` to the list of extensions loaded in the notebook server.
This is done in `jupyter_notebook_config.json` because that file has higher
precedence when both the `.py` and `.json` files exist (IPyWidgets, for
example, writes to the JSON file, so if we only wrote to the `.py` file,
that directive would take precedence and the `jlstaticserve` extension would
**not** be loaded).
"""
function install_notebook_config()
config_dir = jupyter_config_dir()
mkpath(config_dir)
config_file_py = joinpath(config_dir, "jupyter_notebook_config.py")
config_py = isfile(config_file_py) ? read(config_file_py, String) : ""

# remove previous config
config_str = replace(config_str, Regex("\n?" * BEGIN_MARKER * ".*" * END_MARKER * "\n?", "s") => "")
# Remove previous config
config_py = replace(config_py, Regex("\n?" * BEGIN_MARKER * ".*" * END_MARKER * "\n?", "s") => "")

config_str *= """
# We do a repr to make sure that all the necessary characters are escaped
# and the whole path is wrapped in quotes.
deps_dir = dirname(@__FILE__)
deps_dir_esc = repr(deps_dir)
error_msg = strip("""
Directory $deps_dir could not be found; WebIO will not work as
""")
config_py *= """
$BEGIN_MARKER
import sys, os
if os.path.isfile($(repr(joinpath(dirname(@__FILE__), "jlstaticserve.py")))):
sys.path.append($(repr(dirname(@__FILE__))))
c = get_config()
c.NotebookApp.nbserver_extensions = {
"jlstaticserve": True
}
# Add the path to WebIO/deps so that we can load the `jlstaticserve` extension.
import os, sys, warnings
webio_deps_dir = $deps_dir_esc
if os.path.isfile(os.path.join(webio_deps_dir, "jlstaticserve.py")):
sys.path.append(webio_deps_dir)
else:
print("WebIO config in ~/.jupyter/jupyter_notebook_config.py but WebIO plugin not found")
warning_msg = (
'Directory %s could not be found; WebIO.jl will not work as expected. '
+ 'Make sure WebIO.jl is installed correctly (try running '
+ 'Pkg.add("WebIO") and Pkg.build("WebIO") from the Julia console to '
+ 'make sure it is).'
) % webio_deps_dir
warnings.warn(warning_msg)
$END_MARKER
"""
write(config_file, config_str)
config_file_json = joinpath(homedir(), ".jupyter", "jupyter_notebook_config.json")

if isfile(config_file_json)
dict = try
JSON.parse(read(config_file_json, String))
catch err
println(stderr, "Error parsing Jupyter config file $config_file_json - fix it and build again or delete it to enable WebIO")
@goto jsondone
end
app = Base.@get! dict "NotebookApp" Dict()
nbext = Base.@get! app "nbserver_extensions" Dict()
nbext["jlstaticserve"] = true
open(config_file_json, "w") do io
prettyio = JSON.Writer.PrettyContext(io, 4)
JSON.print(prettyio, dict)
end
config_file_json = joinpath(config_dir, "jupyter_notebook_config.json")
config_json = isfile(config_file_json) ? read(config_file_json, String) : "{}"
config_json = try
JSON.parse(config_json)
catch exc
@error "Unable to parse Jupyter notebook config file ($config_file_json). Please fix it and rebuild WebIO." exception=exc
rethrow()
end
@label jsondone
end

function get_jupyter_datadir()
try
return readline(open(`jupyter --data-dir`))
catch (e)
# Is there a way to use Conda.jl if it's installed?
@warn "Didn't detect Jupyter."
end
# Magic to safely access nested JSON objects and set them to empty objects
# if they don't exist yet.
app_config = get!(config_json, "NotebookApp", Dict())
extensions_config = get!(app_config, "nbserver_extensions", Dict())
extensions_config["jlstaticserve"] = true

# Try guessing based on OS
# https://jupyter.readthedocs.io/en/latest/projects/jupyter-directories.html
if Sys.iswindows()
# Is this right?
return joinpath("%APPDATA%", "jupyter")
elseif Sys.isapple()
return joinpath(homedir(), "Library", "Jupyter")
else
# Maybe need to check XDG_DATA_HOME environment variable?
return joinpath(homedir(), ".local", "share", "jupyter")
end
# Defer writing until both files until the end to avoid inconsistent state.
write(config_file_py, config_py)
write(config_file_json, json(config_json, 4))
return nothing
end

"""
Install the Jupyter WebIO notebook extension.
"""
function install_webio_nbextension()
extension_dir = joinpath(get_jupyter_datadir(), "nbextensions")
mkpath(extension_dir)

# I think the config dir is always ~/.jupyter, even on Windows.
config_dir = joinpath(homedir(), ".jupyter", "nbconfig")
mkpath(config_dir)
config_file_json = joinpath(config_dir, "notebook.json")
extensions_dir = jupyter_nbextensions_dir()
mkpath(extensions_dir)
extension_dir = joinpath(extensions_dir, "webio")

# Copy the nbextension files.
@info "Copying WebIO nbextension files to $(extension_dir)."
cp(
joinpath(@__DIR__, "../packages/jupyter-notebook-provider/dist"),
joinpath(extension_dir, "webio"),
extension_dir,
; force=true
)

# Enable the notebook extension.
config_data = Dict()
if isfile(config_file_json)
config_data = try
JSON.parse(read(config_file_json, String))
catch err
println(stderr, "Error parsing Jupyter config file $config_file_json - fix it and build again or delete it to enable WebIO")
return
end
config_dir = jupyter_nbconfig_dir()
mkpath(config_dir)
config_file = joinpath(config_dir, "notebook.json")
config_data = try
isfile(config_file) ? JSON.parse(read(config_file, String)) : Dict()
catch exc
@error "Error parsing Jupyter config file $config_file - fix it and build again or delete it to enable WebIO." exception=exc
error("Unable to parse Jupyter config file.")
end
config_data["load_extensions"] = get(config_data, "load_extensions", Dict())
config_data["load_extensions"]["webio/main"] = true
open(config_file_json, "w") do io
prettyio = JSON.Writer.PrettyContext(io, 4)
JSON.print(prettyio, config_data)
open(config_file, "w") do io
JSON.print(JSON.Writer.PrettyContext(io, 4), config_data)
end
end

install_ijulia_config()
install_notebook_config()
install_webio_nbextension()
37 changes: 37 additions & 0 deletions deps/jupyterdirs.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# This code is "borrowed" from IJulia.jl (MIT license).
# https://github.com/JuliaLang/IJulia.jl/blob/68748c2b88b4b394e2802e911eb154b7008ebdd2/deps/kspec.jl
@static if Sys.iswindows()
function appdata() # return %APPDATA%
path = zeros(UInt16, 300)
CSIDL_APPDATA = 0x001a
result = ccall((:SHGetFolderPathW,:shell32), stdcall, Cint,
(Ptr{Cvoid},Cint,Ptr{Cvoid},Cint,Ptr{UInt16}),C_NULL,CSIDL_APPDATA,C_NULL,0,path)
return result == 0 ? transcode(String, resize!(path, findfirst(iszero, path)-1)) : get(ENV, "APPDATA", "")
end
function default_jupyter_data_dir()
APPDATA = appdata()
return !isempty(APPDATA) ? joinpath(APPDATA, "jupyter") : joinpath(get(ENV, "JUPYTER_CONFIG_DIR", joinpath(homedir(), ".jupyter")), "data")
end
elseif Sys.isapple()
default_jupyter_data_dir() = joinpath(homedir(), "Library/Jupyter")
else
function default_jupyter_data_dir()
xdg_data_home = get(ENV, "XDG_DATA_HOME", "")
data_home = !isempty(xdg_data_home) ? xdg_data_home : joinpath(homedir(), ".local", "share")
joinpath(data_home, "jupyter")
end
end

function jupyter_data_dir()
env_data_dir = get(ENV, "JUPYTER_DATA_DIR", "")
!isempty(env_data_dir) ? env_data_dir : default_jupyter_data_dir()
end

### End of borrowed code ###

function jupyter_config_dir()
env_config_dir = get(ENV, "JUPYTER_CONFIG_DIR", "")
!isempty(env_config_dir) ? env_config_dir : joinpath(homedir(), ".jupyter")
end
jupyter_nbextensions_dir() = joinpath(jupyter_data_dir(), "nbextensions")
jupyter_nbconfig_dir() = joinpath(jupyter_config_dir(), "nbconfig")

0 comments on commit 62b85aa

Please sign in to comment.