Skip to content

Commit

Permalink
Add jupyterlab option; tinker with build tools.
Browse files Browse the repository at this point in the history
  • Loading branch information
culler committed Jun 14, 2023
1 parent d6f6956 commit f4d8c2d
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 37 deletions.
5 changes: 3 additions & 2 deletions bin/create_dmg
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ SAGE_DASH_VERSION=$(echo $SAGE_VERSION | sed s/\\\./\\\-/g)
SAGE_SCORE_VERSION=$(echo $SAGE_VERSION | sed s/\\\./\\_/g)
SOURCE=SageMath-$SAGE_VERSION
DMG_NAME=$SOURCE.dmg
APP=SageMath-$SAGE_DASH_VERSION.app
APP=`ls $SOURCE | grep SageMath`
PKG=`ls $SOURCE | grep Recommended`
rm -f $DMG_NAME
create-dmg \
--volname $SOURCE \
Expand All @@ -14,7 +15,7 @@ create-dmg \
--icon-size 96 \
--icon $APP 10 220 \
--app-drop-link 380 220 \
--icon "Recommended_10_0.pkg" 160 380 \
--icon $PKG 160 380 \
--format ULFO \
--no-internet-enable \
$DMG_NAME $SOURCE
10 changes: 8 additions & 2 deletions bin/render_templates
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
#! /usr/bin/env python3
import sys, os
import sys, os, re
bin = os.path.dirname(os.path.abspath(sys.argv[0]))
version_file = os.path.join(bin, '..', 'Sage_framework', 'repo', 'sage',
'VERSION.txt')
get_version_re = re.compile('SageMath version ([0-9]*\.[0-9]*)')
with open(version_file) as input_file:
m = get_version_re.match(input_file.readline())
sage_version = m.groups()[0]
jinja_dir = os.path.join(bin, '..', 'jinja')
sys.path.insert(0, jinja_dir)
os.chdir(jinja_dir)
import render
render.main()
render.main(sage_version)
9 changes: 5 additions & 4 deletions jinja/render/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ def make_file(self, name):
with open('output/%s'%name, 'w') as output:
output.write(template.render(self.params))

def main():
def main(sage_version='10.1'):
dashed = sage_version.replace('.', '-')
params={
'sage_version': '10.0',
'sage_dash_version': '10-0',
'sage_long_version': '10.0.0',
'sage_version': sage_version,
'sage_dash_version': dashed,
'sage_long_version': sage_version + '.0',
'year': str(datetime.datetime.now().year),
}
JM = JinjaMaster('templates', params)
Expand Down
157 changes: 128 additions & 29 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@
from tkinter.filedialog import askdirectory
from tkinter.messagebox import showerror, showwarning, askyesno, askokcancel
from tkinter.scrolledtext import ScrolledText
from sage.version import version as sage_version
import os
import plistlib
import platform

this_python = 'python' + '.'.join(platform.python_version_tuple()[:2])
jupyter_id = re.compile('nbserver-([0-9]+)-open.html')
contents_dir = abspath(path_join(sys.argv[0], pardir, pardir))
framework_dir = path_join(contents_dir, 'Frameworks')
info_plist = path_join(contents_dir, 'Info.plist')
current = path_join(framework_dir, 'Sage.framework', 'Versions', 'Current')
sage_executable = path_join(current, 'venv', 'bin', 'sage')
sage_userbase = path_join(os.environ['HOME'], '.sage', 'local')
sage_userlib = path_join(sage_userbase, 'lib', this_python)
sage_usersitepackages = path_join(sage_userlib, 'site-packages')

def get_version():
with open(info_plist, 'rb') as plist_file:
Expand All @@ -34,8 +40,10 @@ def get_version():
app_support_dir = path_join(os.environ['HOME'], 'Library', 'Application Support',
'SageMath')
settings_path = path_join(app_support_dir, 'Settings.plist')

jupyter_runtime_dir = path_join(app_support_dir, 'Jupyter')
jupyter_lab_dir = path_join(sage_userbase, 'share', 'jupyter', 'lab')
jupyter_lab_exe = path_join(sage_userbase, 'bin', 'jupyter-lab')

###config_file = path_join(app_support_dir, 'config')

class PopupMenu(ttk.Menubutton):
Expand Down Expand Up @@ -112,7 +120,7 @@ def launch_notebook(self, url=None):
if url is None:
if not self.check_notebook_dir():
return False
jupyter_notebook_dir = self.notebooks.get()
jupyter_notebook_dir = self.notebook_dir.get()
if not jupyter_notebook_dir:
jupyter_notebook_dir = os.environ['HOME']
subprocess.Popen([sage_executable, '--jupyter', 'notebook',
Expand All @@ -121,12 +129,43 @@ def launch_notebook(self, url=None):
subprocess.run(['open', url], env=environ, capture_output=True)
return True

lab_message = (
'To use SageMath {0} in a Jupyter Lab notebok you must install the '
'Recommended Extras from the SageMath {0} disk image, and you must also '
'install the jupyterlab package within Sage by starting sage and '
'running:\n\n'
'%pip install jupyterlab\n\n').format(sage_version)

def launch_lab(self):
if not self.jupyter_lab_installed():
InfoDialog(self, message=self.lab_message)
return False
if not self.check_notebook_dir():
showerror(message='Please select a notebook directory.')
return False
environ = {'PYTHONPATH': sage_usersitepackages}
environ.update(os.environ)
notebook_dir = self.notebook_dir.get()
if not notebook_dir:
notebook_dir = os.environ['HOME']
subprocess.Popen([jupyter_lab_exe, '--app-dir=%s'%jupyter_lab_dir,
'--notebook-dir=%s'%notebook_dir], env=environ)
return True

def find_app(self, bundle_id):
script = self.find_app_script%bundle_id
result = subprocess.run(['osascript', '-'], input=script, text=True,
capture_output=True)
return result.stdout.strip() == 'true'

def jupyter_lab_installed(self):
if not os.path.exists(jupyter_lab_dir):
return False
if not os.path.exists(jupyter_lab_exe):
return False
return True


class LaunchWindow(tkinter.Toplevel, Launcher):
def __init__(self, root):
Launcher.__init__(self)
Expand Down Expand Up @@ -164,28 +203,34 @@ def __init__(self, root):
self.terminals.append('iTerm.app')
self.terminal_var = tkinter.Variable(self, self.terminals[0])
self.terminal_option = PopupMenu(checks, self.terminal_var, self.terminals)
self.use_jupyter = ttk.Radiobutton(checks, text="Jupyter notebook from folder:",
self.notebook_types = ['Classic Jupyter', 'Jupyter Lab']
self.use_jupyter = ttk.Radiobutton(checks, text="Notebook",
variable=radio_var, value='nb', command=self.update_radio_buttons)
notebook_frame = ttk.Frame(checks)
self.notebooks = ttk.Entry(notebook_frame, width=24)
self.notebooks.insert(tkinter.END, self.settings['state']['notebook_dir'])
self.notebooks.config(state='readonly')
self.browse = ttk.Button(notebook_frame, text='Select ...', padding=(-8, 0),
self.nb_var = tkinter.Variable(self, self.notebook_types[0])
self.notebook_option = PopupMenu(checks, self.nb_var, self.notebook_types)
notebook_dir_frame = ttk.Frame(checks)
ttk.Label(notebook_dir_frame, text='Using notebooks from:').grid(
row=0, column=0, sticky=tkinter.W, padx=12)
self.notebook_dir = ttk.Entry(notebook_dir_frame, width=24)
self.notebook_dir.insert(tkinter.END, self.settings['state']['notebook_dir'])
self.notebook_dir.config(state='readonly')
self.browse = ttk.Button(notebook_dir_frame, text='Select ...', padding=(-8, 0),
command=self.browse_notebook_dir, state=tkinter.DISABLED)
self.notebooks.grid(row=0, column=0)
self.browse.grid(row=0, column=1)
self.notebook_dir.grid(row=1, column=0, padx=8)
self.browse.grid(row=1, column=1)
# Launch button
self.launch = ttk.Button(frame, text="Launch", command=self.launch_sage)
# Build the interfaces frame
self.use_cli.grid(row=0, column=0, sticky=tkinter.W, pady=5)
self.terminal_option.grid(row=1, column=0, sticky=tkinter.W, padx=10, pady=5)
self.use_jupyter.grid(row=2, column=0, sticky=tkinter.W, pady=5)
notebook_frame.grid(row=3, column=0, sticky=tkinter.W, pady=5)
self.notebook_option.grid(row=3, column=0, sticky=tkinter.W, padx=10, pady=5)
notebook_dir_frame.grid(row=4, column=0, sticky=tkinter.W, pady=5)
# Build the window
logo.grid(row=0, column=0, pady=5)
checks.grid(row=1, column=0, padx=10, pady=10, sticky=tkinter.EW)
self.launch.grid(row=2, column=0)
self.geometry('380x350+400+400')
self.geometry('380x380+400+400')
self.update_radio_buttons()

def quit(self):
Expand All @@ -202,6 +247,7 @@ def get_settings(self):
self.settings['state'] = {
'interface_type': 'cli',
'terminal_app': 'Terminal.app',
'notebook_type': 'Classic Jupyter',
'notebook_dir': '',
}

Expand All @@ -210,20 +256,23 @@ def save_settings(self):
self.settings['state'] = {
'interface_type': self.radio_var.get(),
'terminal_app': self.terminal_var.get(),
'notebook_dir': self.notebooks.get(),
'notebook_type': self.nb_var.get(),
'notebook_dir': self.notebook_dir.get(),
}
with open(settings_path, 'wb') as settings_file:
plistlib.dump(self.settings, settings_file)

def update_radio_buttons(self):
radio = self.radio_var.get()
if radio == 'cli':
self.notebooks.config(state=tkinter.DISABLED)
self.notebook_dir.config(state=tkinter.DISABLED)
self.browse.config(state=tkinter.DISABLED)
self.terminal_option.config(state=tkinter.NORMAL)
self.notebook_option.config(state=tkinter.DISABLED)
elif radio == 'nb':
self.notebooks.config(state='readonly')
self.notebook_dir.config(state='readonly')
self.browse.config(state=tkinter.NORMAL)
self.notebook_option.config(state=tkinter.NORMAL)
self.terminal_option.config(state=tkinter.DISABLED)

def update_environment(self):
Expand Down Expand Up @@ -255,19 +304,25 @@ def launch_sage(self):
if interface == 'cli':
launched = self.launch_terminal(app=self.terminal_var.get())
elif interface == 'nb':
jupyter_openers = [f for f in os.listdir(jupyter_runtime_dir)
app = self.nb_var.get()
if app == 'Classic Jupyter':
jupyter_openers = [f for f in os.listdir(jupyter_runtime_dir)
if f[-4:] == 'html']
if not jupyter_openers:
launched = self.launch_notebook(None)
if not jupyter_openers:
launched = self.launch_notebook(None)
else:
html_file = path_join(jupyter_runtime_dir, jupyter_openers[0])
launched = self.launch_notebook(html_file)
elif app == 'Jupyter Lab':
launched = self.launch_lab()
else:
html_file = path_join(jupyter_runtime_dir, jupyter_openers[0])
launched = self.launch_notebook(html_file)
raise RuntimeError()
if launched:
self.save_settings()
self.quit()

def check_notebook_dir(self):
notebook_dir = self.notebooks.get()
notebook_dir = self.notebook_dir.get()
if not notebook_dir.strip():
showwarning(parent=self,
message="Please choose or create a folder for your Jupyter notebooks.")
Expand Down Expand Up @@ -307,24 +362,24 @@ def browse_notebook_dir(self):
directory = askdirectory(parent=self, initialdir=os.environ['HOME'],
message='Choose or create a folder for Jupyter notebooks')
if directory:
self.notebooks.config(state=tkinter.NORMAL)
self.notebooks.delete(0, tkinter.END)
self.notebooks.insert(tkinter.END, directory)
self.notebooks.config(state='readonly')
self.notebook_dir.config(state=tkinter.NORMAL)
self.notebook_dir.delete(0, tkinter.END)
self.notebook_dir.insert(tkinter.END, directory)
self.notebook_dir.config(state='readonly')

class AboutDialog(Dialog):
def __init__(self, master, title='', content=''):
def __init__(self, parent, title='', content=''):
self.content = content
self.style = ttk.Style(master)
self.style = ttk.Style(parent)
resource_dir = abspath(path_join(sys.argv[0], pardir, pardir, 'Resources'))
logo_file = path_join(resource_dir, 'sage_logo_256.png')
try:
self.logo_image = tkinter.PhotoImage(file=logo_file)
except tkinter.TclError:
self.logo_image = None
Dialog.__init__(self, master, title=title)
Dialog.__init__(self, parent, title=title)

def body(self, master):
def body(self, parent):
self.resizable(False, False)
frame = ttk.Frame(self)
if self.logo_image:
Expand All @@ -345,6 +400,50 @@ def buttonbox(self):
self.bind("<Escape>", self.ok)
frame.pack()

class InfoDialog(Dialog):
def __init__(self, parent, title='', message='',
text_width=40, text_height=12, font_size=16):
self.message = message
self.text_width, self.text_height = text_width, text_height
self.text_font = tkinter.font.Font()
self.text_font.config(size=font_size)
self.style = ttk.Style(parent)
resource_dir = abspath(path_join(sys.argv[0], pardir, pardir, 'Resources'))
logo_file = path_join(resource_dir, 'sage_logo_256.png')
try:
self.logo_image = tkinter.PhotoImage(file=logo_file)
except tkinter.TclError:
self.logo_image = None
Dialog.__init__(self, parent, title=title)

def body(self, parent):
self.resizable(False, False)
frame = ttk.Frame(self)
if self.logo_image:
logo = ttk.Label(frame, image=self.logo_image)
else:
logo = ttk.Label(frame, text='Logo Here')
logo.grid(row=0, column=0, padx=20, pady=20, sticky=tkinter.N)
font = tkinter.font.Font()
font.config(size=18)
text = tkinter.Text(frame, wrap=tkinter.WORD, bd=0,
highlightthickness=0, bg='SystemWindowBackgroundColor',
width=self.text_width, height=self.text_height,
font=self.text_font)
text.grid(row=1, column=0, padx=20, sticky=tkinter.EW)
text.insert(tkinter.INSERT, self.message)
text.config(state=tkinter.DISABLED)
frame.pack()

def buttonbox(self):
frame = ttk.Frame(self, padding=(0, 0, 0, 20))
ok = ttk.Button(frame, text="OK", width=10, command=self.ok,
default=tkinter.ACTIVE)
ok.grid(row=2, column=0, padx=5, pady=5)
self.bind("<Return>", self.ok)
self.bind("<Escape>", self.ok)
frame.pack()

class EnvironmentEditor(tkinter.Toplevel):
def __init__(self, parent):
tkinter.Toplevel.__init__(self, parent)
Expand Down

0 comments on commit f4d8c2d

Please sign in to comment.