Creating a Jupyter Notebook App for OSX
credit: Olena Paliasna

Creating a Jupyter Notebook App for OSX

Honestly, I like the ease of Apps on OSX and I got tired of loading Jupyter Notebook from the command-line. So I created a very basic Automator application so I can simply launch and go.

No alt text provided for this image

For those of you with little time, the application is downloadable from this link and is drop and go ready.

For everyone else, I will explain the fun issues of creating a drop and go application. Some of the issues are:

  1. Finding where "jupyter-notebook" is on the host system.
  2. Creating a working directory
  3. Creating a log file that can be viewed in real-time with the "Console App"
  4. Putting in a little safety logic
  5. And last but not least getting a decent icon for the application that is not the default.

1. Where is waldo

The binary file for jupyter-notebook can be in a few places and it will change if you update your python from 3.7 to 3.8 to 3.9 and so on.

If you are using Mac Ports, it will be in "/opt/local" somewhere, for HomeBrew it is /usr/local/Cellar, and if it is installed some other method, it could be in /usr/local somewhere. On my system it is located here:

/opt/local/Library/Frameworks/Python.framework/Versions/3.8/bin/jupyter-notebook

The fun part of this, is that "which" won't find it if the directory is not on the global path and OSX will use the sudo path for Automator tasks.

I came up with this logic to set an environment variable for the location.

# Script to run a jupyter notebook from a specific directory
# We will check in /opt/local, /usr/local/Cellar and /usr/local if no file is found we exit with error


if [ -f "$(eval "find /opt/local -type f -name jupyter-notebook -print -quit  2>/dev/null")" ];
then
	export JUPYTER_NOTEBOOK="$(eval "find /opt/local -type f -name jupyter-notebook -print -quit  2>/dev/null")"
  echo "$JUPYTER_NOTEBOOK exists in /opt/local." >> "${JUPYTER_LOG}"


elif [ -f "$(eval "find /usr/local/Cellar -type f -name jupyter-notebook -print -quit  2>/dev/null")" ];
  then
	export JUPYTER_NOTEBOOK="$(eval "find /usr/local/Cellar -type f -name jupyter-notebook -print -quit  2>/dev/null")"
	echo "$JUPYTER_NOTEBOOK exists in /usr/local/Cellar." >> "${JUPYTER_LOG}"


elif [ -f "$(eval "find /usr/local/ -type f -name jupyter-notebook -print -quit  2>/dev/null")" ];
  then
	export JUPYTER_NOTEBOOK="$(eval "find /usr/local/ -type f -name jupyter-notebook -print -quit  2>/dev/null")"
	echo "$JUPYTER_NOTEBOOK exists in /usr/local/." >> "${JUPYTER_LOG}"


else
	echo "$JUPYTER_NOTEBOOK not found in search directories, exiting" >> "${JUPYTER_LOG}"
	exit 1


fi 
# Test if Jupyter Notebook exists

It's not hugely sophisticated but it works. Using the find command with an exit after the first occurrence seemed to be the best choice. Actual mileage will vary and feedback is appreciated.

2. Working Directory

I am a bit "OCD" when it comes to finding files and making sure that they are not all over my system or created in the dark regions of my Mac.

# Create a working directory if it doesn't exist.
export JUPYTER_HOME="$HOME/Documents/${JUPYTER_DIR}"
[ ! -d "$JUPYTER_HOME" ] &&  mkdir "${JUPYTER_HOME}"
cd "$JUPYTER_HOME" || exit


# Create a subdirectory for the logs
[ ! -d "$JUPYTER_HOME/logs" ] &&  mkdir "${JUPYTER_HOME}/logs"



I am using variables here and I create a sub-directory for logs. I have found being able to see the logs in real-time is very necessary when dealing with some of my projects; and I don't want log files mixed with my notebook files.

3. Creating a log file and viewing it in real-time in a "Mac-ish way"

# Log File name
export JUPYTER_LOG="Jupyter-${USER}-$(date +'%Y-%M-%d-%s').log"

I set a log filename at the beginning of each run, so that it will change. This will allow me to keep or delete logs as I need to.

We start the console.app and load the log file so we can see it running.

# Create log file if missing
[ ! -d "$JUPYTER_LOG" ] &&  touch "${JUPYTER_LOG}"
# Open log file with Console so that messages are available for the user
open -a console "${JUPYTER_LOG}"

No alt text provided for this image

Now I have a browser and a window for the logs

4. Putting in a little safety logic

If you look at the overall code, there are few checks for logic and exits when things don't work. And putting the whole script together.



## Written By Colin Bitterfield
## Version 1.0


# Note if you have weird startup issues look for the webbrowser.py file and change
# if sys.platform[:3] == "win": (to) elif sys.platform[:3] == "win":
# Around line 535 for python 3.8


# Document Directory
export JUPYTER_DIR="Jupyter-Documents"
# Log File name
export JUPYTER_LOG="Jupyter-${USER}-$(date +'%Y-%M-%d-%s').log"


# Change the PATH order to deal with MacPosts PHP
# This should be set on the path correctly; however if not we fix it here
# This is set "/etc/paths" (If you set in paths.d it will be after system directories)
export PATH=/opt/local/bin:$PATH


# Create a working directory if it doesn't exist.
export JUPYTER_HOME="$HOME/Documents/${JUPYTER_DIR}"
[ ! -d "$JUPYTER_HOME" ] &&  mkdir "${JUPYTER_HOME}"
cd "$JUPYTER_HOME" || exit


# Create a subdirectory for the logs
[ ! -d "$JUPYTER_HOME/logs" ] &&  mkdir "${JUPYTER_HOME}/logs"


# Create a log file in a variable
export JUPYTER_LOG="$JUPYTER_HOME/logs/${JUPYTER_LOG}"


echo "$(date) Starting Jupyter session for ${USER}" >> "${JUPYTER_LOG}"


env >> "${JUPYTER_LOG}"


# Script to run a jupyter notebook from a specific directory
# We will check in /opt/local, /usr/local/Cellar and /usr/local if no file is found we exit with error


if [ -f "$(eval "find /opt/local -type f -name jupyter-notebook -print -quit  2>/dev/null")" ];
then
	export JUPYTER_NOTEBOOK="$(eval "find /opt/local -type f -name jupyter-notebook -print -quit  2>/dev/null")"
  echo "$JUPYTER_NOTEBOOK exists in /opt/local." >> "${JUPYTER_LOG}"


elif [ -f "$(eval "find /usr/local/Cellar -type f -name jupyter-notebook -print -quit  2>/dev/null")" ];
  then
	export JUPYTER_NOTEBOOK="$(eval "find /usr/local/Cellar -type f -name jupyter-notebook -print -quit  2>/dev/null")"
	echo "$JUPYTER_NOTEBOOK exists in /usr/local/Cellar." >> "${JUPYTER_LOG}"


elif [ -f "$(eval "find /usr/local/ -type f -name jupyter-notebook -print -quit  2>/dev/null")" ];
  then
	export JUPYTER_NOTEBOOK="$(eval "find /usr/local/ -type f -name jupyter-notebook -print -quit  2>/dev/null")"
	echo "$JUPYTER_NOTEBOOK exists in /usr/local/." >> "${JUPYTER_LOG}"


else
	echo "$JUPYTER_NOTEBOOK not found in search directories, exiting" >> "${JUPYTER_LOG}"
	exit 1


fi 
# Test if Jupyter Notebook exists




# Create log file if missing
[ ! -d "$JUPYTER_LOG" ] &&  touch "${JUPYTER_LOG}"
# Open log file with Console so that messages are available for the user
open -a console "${JUPYTER_LOG}"


#Start Jupyter-notebook
echo "CMD: ${JUPYTER_NOTEBOOK}" >> "${JUPYTER_LOG}"
eval "${JUPYTER_NOTEBOOK}"  >> "${JUPYTER_LOG}" 2>&1 || (echo "Failure to start" >> "${JUPYTER_LOG}"; exit)




# Notice the log file
echo "$(date) Ending Jupyter session for ${USER}" >> "${JUPYTER_LOG}"



5. Adding an icon, this turns out to be harder than it looks at first login.

Credit to this article for figuring it out.

Putting it all together as an application

Now we have our application in Automator.

No alt text provided for this image

I added a little voice to tell you it's working. Save it to the application directory. And we have a working application.

No alt text provided for this image






To stop the notebook, you need to click on the gear icon and the X to stop it.


No alt text provided for this image

Work left to do:

Currently, the application will fail to start if there is already one running. I should add some logic with a lock file to prevent a second version from running and let you know that's the problem. However, that will have to wait for another day

Comments and Suggestions are always welcome and I will put the code up in a repository on Github over the holidays. Feel free to fork and modify to as much as needed. Please provide a link to this article if you.


要查看或添加评论,请登录

Colin Bitterfield的更多文章

社区洞察

其他会员也浏览了