2626import signal
2727import socket
2828import sys
29+ import tempfile
2930import threading
3031import time
3132import warnings
107108from notebook ._sysinfo import get_sys_info
108109
109110from ._tz import utcnow , utcfromtimestamp
110- from .utils import url_path_join , check_pid , url_escape
111+ from .utils import url_path_join , check_pid , url_escape , urljoin , pathname2url
111112
112113#-----------------------------------------------------------------------------
113114# Module globals
@@ -1184,6 +1185,13 @@ def _update_mathjax_config(self, change):
11841185 def _default_info_file (self ):
11851186 info_file = "nbserver-%s.json" % os .getpid ()
11861187 return os .path .join (self .runtime_dir , info_file )
1188+
1189+ browser_open_file = Unicode ()
1190+
1191+ @default ('browser_open_file' )
1192+ def _default_browser_open_file (self ):
1193+ basename = "nbserver-%s-open.html" % os .getpid ()
1194+ return os .path .join (self .runtime_dir , basename )
11871195
11881196 pylab = Unicode ('disabled' , config = True ,
11891197 help = _ ("""
@@ -1697,6 +1705,67 @@ def remove_server_info_file(self):
16971705 if e .errno != errno .ENOENT :
16981706 raise
16991707
1708+ def write_browser_open_file (self ):
1709+ """Write an nbserver-<pid>-open.html file
1710+
1711+ This can be used to open the notebook in a browser
1712+ """
1713+ # default_url contains base_url, but so does connection_url
1714+ open_url = self .default_url [len (self .base_url ):]
1715+
1716+ with open (self .browser_open_file , 'w' , encoding = 'utf-8' ) as f :
1717+ self ._write_browser_open_file (open_url , f )
1718+
1719+ def _write_browser_open_file (self , url , fh ):
1720+ if self .token :
1721+ url = url_concat (url , {'token' : self .one_time_token })
1722+ url = url_path_join (self .connection_url , url )
1723+
1724+ jinja2_env = self .web_app .settings ['jinja2_env' ]
1725+ template = jinja2_env .get_template ('browser-open.html' )
1726+ fh .write (template .render (open_url = url ))
1727+
1728+ def remove_browser_open_file (self ):
1729+ """Remove the nbserver-<pid>-open.html file created for this server.
1730+
1731+ Ignores the error raised when the file has already been removed.
1732+ """
1733+ try :
1734+ os .unlink (self .browser_open_file )
1735+ except OSError as e :
1736+ if e .errno != errno .ENOENT :
1737+ raise
1738+
1739+ def launch_browser (self ):
1740+ try :
1741+ browser = webbrowser .get (self .browser or None )
1742+ except webbrowser .Error as e :
1743+ self .log .warning (_ ('No web browser found: %s.' ) % e )
1744+ browser = None
1745+
1746+ if not browser :
1747+ return
1748+
1749+ if self .file_to_run :
1750+ if not os .path .exists (self .file_to_run ):
1751+ self .log .critical (_ ("%s does not exist" ) % self .file_to_run )
1752+ self .exit (1 )
1753+
1754+ relpath = os .path .relpath (self .file_to_run , self .notebook_dir )
1755+ uri = url_escape (url_path_join ('notebooks' , * relpath .split (os .sep )))
1756+
1757+ # Write a temporary file to open in the browser
1758+ fd , open_file = tempfile .mkstemp (suffix = '.html' )
1759+ with open (fd , 'w' , encoding = 'utf-8' ) as fh :
1760+ self ._write_browser_open_file (uri , fh )
1761+ else :
1762+ open_file = self .browser_open_file
1763+
1764+ b = lambda : browser .open (
1765+ urljoin ('file:' , pathname2url (open_file )),
1766+ new = self .webbrowser_open_new )
1767+ threading .Thread (target = b ).start ()
1768+
17001769 def start (self ):
17011770 """ Start the Notebook server app, after initialization
17021771
@@ -1726,30 +1795,10 @@ def start(self):
17261795 "resources section at https://jupyter.org/community.html." ))
17271796
17281797 self .write_server_info_file ()
1798+ self .write_browser_open_file ()
17291799
17301800 if self .open_browser or self .file_to_run :
1731- try :
1732- browser = webbrowser .get (self .browser or None )
1733- except webbrowser .Error as e :
1734- self .log .warning (_ ('No web browser found: %s.' ) % e )
1735- browser = None
1736-
1737- if self .file_to_run :
1738- if not os .path .exists (self .file_to_run ):
1739- self .log .critical (_ ("%s does not exist" ) % self .file_to_run )
1740- self .exit (1 )
1741-
1742- relpath = os .path .relpath (self .file_to_run , self .notebook_dir )
1743- uri = url_escape (url_path_join ('notebooks' , * relpath .split (os .sep )))
1744- else :
1745- # default_url contains base_url, but so does connection_url
1746- uri = self .default_url [len (self .base_url ):]
1747- if self .one_time_token :
1748- uri = url_concat (uri , {'token' : self .one_time_token })
1749- if browser :
1750- b = lambda : browser .open (url_path_join (self .connection_url , uri ),
1751- new = self .webbrowser_open_new )
1752- threading .Thread (target = b ).start ()
1801+ self .launch_browser ()
17531802
17541803 if self .token and self ._token_generated :
17551804 # log full URL with generated token, so there's a copy/pasteable link
@@ -1773,6 +1822,7 @@ def start(self):
17731822 info (_ ("Interrupted..." ))
17741823 finally :
17751824 self .remove_server_info_file ()
1825+ self .remove_browser_open_file ()
17761826 self .cleanup_kernels ()
17771827
17781828 def stop (self ):
0 commit comments