26
26
import signal
27
27
import socket
28
28
import sys
29
+ import tempfile
29
30
import threading
30
31
import time
31
32
import warnings
107
108
from notebook ._sysinfo import get_sys_info
108
109
109
110
from ._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
111
112
112
113
#-----------------------------------------------------------------------------
113
114
# Module globals
@@ -1184,6 +1185,13 @@ def _update_mathjax_config(self, change):
1184
1185
def _default_info_file (self ):
1185
1186
info_file = "nbserver-%s.json" % os .getpid ()
1186
1187
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 )
1187
1195
1188
1196
pylab = Unicode ('disabled' , config = True ,
1189
1197
help = _ ("""
@@ -1697,6 +1705,67 @@ def remove_server_info_file(self):
1697
1705
if e .errno != errno .ENOENT :
1698
1706
raise
1699
1707
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
+
1700
1769
def start (self ):
1701
1770
""" Start the Notebook server app, after initialization
1702
1771
@@ -1726,30 +1795,10 @@ def start(self):
1726
1795
"resources section at https://jupyter.org/community.html." ))
1727
1796
1728
1797
self .write_server_info_file ()
1798
+ self .write_browser_open_file ()
1729
1799
1730
1800
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 ()
1753
1802
1754
1803
if self .token and self ._token_generated :
1755
1804
# log full URL with generated token, so there's a copy/pasteable link
@@ -1773,6 +1822,7 @@ def start(self):
1773
1822
info (_ ("Interrupted..." ))
1774
1823
finally :
1775
1824
self .remove_server_info_file ()
1825
+ self .remove_browser_open_file ()
1776
1826
self .cleanup_kernels ()
1777
1827
1778
1828
def stop (self ):
0 commit comments