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
@@ -754,12 +755,6 @@ def _write_cookie_secret_file(self, secret):
754
755
""" )
755
756
).tag (config = True )
756
757
757
- one_time_token = Unicode (
758
- help = _ ("""One-time token used for opening a browser.
759
- Once used, this token cannot be used again.
760
- """ )
761
- )
762
-
763
758
_token_generated = True
764
759
765
760
@default ('token' )
@@ -1178,6 +1173,13 @@ def _update_mathjax_config(self, change):
1178
1173
def _default_info_file (self ):
1179
1174
info_file = "nbserver-%s.json" % os .getpid ()
1180
1175
return os .path .join (self .runtime_dir , info_file )
1176
+
1177
+ browser_open_file = Unicode ()
1178
+
1179
+ @default ('browser_open_file' )
1180
+ def _default_browser_open_file (self ):
1181
+ basename = "nbserver-%s-open.html" % os .getpid ()
1182
+ return os .path .join (self .runtime_dir , basename )
1181
1183
1182
1184
pylab = Unicode ('disabled' , config = True ,
1183
1185
help = _ ("""
@@ -1357,9 +1359,6 @@ def init_webapp(self):
1357
1359
self .tornado_settings ['cookie_options' ] = self .cookie_options
1358
1360
self .tornado_settings ['get_secure_cookie_kwargs' ] = self .get_secure_cookie_kwargs
1359
1361
self .tornado_settings ['token' ] = self .token
1360
- if (self .open_browser or self .file_to_run ) and not self .password :
1361
- self .one_time_token = binascii .hexlify (os .urandom (24 )).decode ('ascii' )
1362
- self .tornado_settings ['one_time_token' ] = self .one_time_token
1363
1362
1364
1363
# ensure default_url starts with base_url
1365
1364
if not self .default_url .startswith (self .base_url ):
@@ -1689,6 +1688,67 @@ def remove_server_info_file(self):
1689
1688
if e .errno != errno .ENOENT :
1690
1689
raise
1691
1690
1691
+ def write_browser_open_file (self ):
1692
+ """Write an nbserver-<pid>-open.html file
1693
+
1694
+ This can be used to open the notebook in a browser
1695
+ """
1696
+ # default_url contains base_url, but so does connection_url
1697
+ open_url = self .default_url [len (self .base_url ):]
1698
+
1699
+ with io .open (self .browser_open_file , 'w' , encoding = 'utf-8' ) as f :
1700
+ self ._write_browser_open_file (open_url , f )
1701
+
1702
+ def _write_browser_open_file (self , url , fh ):
1703
+ if self .token :
1704
+ url = url_concat (url , {'token' : self .token })
1705
+ url = url_path_join (self .connection_url , url )
1706
+
1707
+ jinja2_env = self .web_app .settings ['jinja2_env' ]
1708
+ template = jinja2_env .get_template ('browser-open.html' )
1709
+ fh .write (template .render (open_url = url ))
1710
+
1711
+ def remove_browser_open_file (self ):
1712
+ """Remove the nbserver-<pid>-open.html file created for this server.
1713
+
1714
+ Ignores the error raised when the file has already been removed.
1715
+ """
1716
+ try :
1717
+ os .unlink (self .browser_open_file )
1718
+ except OSError as e :
1719
+ if e .errno != errno .ENOENT :
1720
+ raise
1721
+
1722
+ def launch_browser (self ):
1723
+ try :
1724
+ browser = webbrowser .get (self .browser or None )
1725
+ except webbrowser .Error as e :
1726
+ self .log .warning (_ ('No web browser found: %s.' ) % e )
1727
+ browser = None
1728
+
1729
+ if not browser :
1730
+ return
1731
+
1732
+ if self .file_to_run :
1733
+ if not os .path .exists (self .file_to_run ):
1734
+ self .log .critical (_ ("%s does not exist" ) % self .file_to_run )
1735
+ self .exit (1 )
1736
+
1737
+ relpath = os .path .relpath (self .file_to_run , self .notebook_dir )
1738
+ uri = url_escape (url_path_join ('notebooks' , * relpath .split (os .sep )))
1739
+
1740
+ # Write a temporary file to open in the browser
1741
+ fd , open_file = tempfile .mkstemp (suffix = '.html' )
1742
+ with open (fd , 'w' , encoding = 'utf-8' ) as fh :
1743
+ self ._write_browser_open_file (uri , fh )
1744
+ else :
1745
+ open_file = self .browser_open_file
1746
+
1747
+ b = lambda : browser .open (
1748
+ urljoin ('file:' , pathname2url (open_file )),
1749
+ new = self .webbrowser_open_new )
1750
+ threading .Thread (target = b ).start ()
1751
+
1692
1752
def start (self ):
1693
1753
""" Start the Notebook server app, after initialization
1694
1754
@@ -1718,38 +1778,19 @@ def start(self):
1718
1778
"resources section at https://jupyter.org/community.html." ))
1719
1779
1720
1780
self .write_server_info_file ()
1781
+ self .write_browser_open_file ()
1721
1782
1722
1783
if self .open_browser or self .file_to_run :
1723
- try :
1724
- browser = webbrowser .get (self .browser or None )
1725
- except webbrowser .Error as e :
1726
- self .log .warning (_ ('No web browser found: %s.' ) % e )
1727
- browser = None
1728
-
1729
- if self .file_to_run :
1730
- if not os .path .exists (self .file_to_run ):
1731
- self .log .critical (_ ("%s does not exist" ) % self .file_to_run )
1732
- self .exit (1 )
1733
-
1734
- relpath = os .path .relpath (self .file_to_run , self .notebook_dir )
1735
- uri = url_escape (url_path_join ('notebooks' , * relpath .split (os .sep )))
1736
- else :
1737
- # default_url contains base_url, but so does connection_url
1738
- uri = self .default_url [len (self .base_url ):]
1739
- if self .one_time_token :
1740
- uri = url_concat (uri , {'token' : self .one_time_token })
1741
- if browser :
1742
- b = lambda : browser .open (url_path_join (self .connection_url , uri ),
1743
- new = self .webbrowser_open_new )
1744
- threading .Thread (target = b ).start ()
1784
+ self .launch_browser ()
1745
1785
1746
1786
if self .token and self ._token_generated :
1747
1787
# log full URL with generated token, so there's a copy/pasteable link
1748
1788
# with auth info.
1749
1789
self .log .critical ('\n ' .join ([
1750
1790
'\n ' ,
1751
- 'Copy/paste this URL into your browser when you connect for the first time,' ,
1752
- 'to login with a token:' ,
1791
+ 'To access the notebook, open this file in a browser:' ,
1792
+ ' %s' % urljoin ('file:' , pathname2url (self .browser_open_file )),
1793
+ 'Or copy and paste one of these URLs:' ,
1753
1794
' %s' % self .display_url ,
1754
1795
]))
1755
1796
@@ -1765,6 +1806,7 @@ def start(self):
1765
1806
info (_ ("Interrupted..." ))
1766
1807
finally :
1767
1808
self .remove_server_info_file ()
1809
+ self .remove_browser_open_file ()
1768
1810
self .cleanup_kernels ()
1769
1811
1770
1812
def stop (self ):
0 commit comments