-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix %APPDATA% directory on Windows (for real this time). (#286)
* 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
Showing
2 changed files
with
113 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") |