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' )
@@ -1184,6 +1179,13 @@ def _update_mathjax_config(self, change):
1184
1179
def _default_info_file (self ):
1185
1180
info_file = "nbserver-%s.json" % os .getpid ()
1186
1181
return os .path .join (self .runtime_dir , info_file )
1182
+
1183
+ browser_open_file = Unicode ()
1184
+
1185
+ @default ('browser_open_file' )
1186
+ def _default_browser_open_file (self ):
1187
+ basename = "nbserver-%s-open.html" % os .getpid ()
1188
+ return os .path .join (self .runtime_dir , basename )
1187
1189
1188
1190
pylab = Unicode ('disabled' , config = True ,
1189
1191
help = _ ("""
@@ -1363,9 +1365,6 @@ def init_webapp(self):
1363
1365
self .tornado_settings ['cookie_options' ] = self .cookie_options
1364
1366
self .tornado_settings ['get_secure_cookie_kwargs' ] = self .get_secure_cookie_kwargs
1365
1367
self .tornado_settings ['token' ] = self .token
1366
- if (self .open_browser or self .file_to_run ) and not self .password :
1367
- self .one_time_token = binascii .hexlify (os .urandom (24 )).decode ('ascii' )
1368
- self .tornado_settings ['one_time_token' ] = self .one_time_token
1369
1368
1370
1369
# ensure default_url starts with base_url
1371
1370
if not self .default_url .startswith (self .base_url ):
@@ -1697,6 +1696,67 @@ def remove_server_info_file(self):
1697
1696
if e .errno != errno .ENOENT :
1698
1697
raise
1699
1698
1699
+ def write_browser_open_file (self ):
1700
+ """Write an nbserver-<pid>-open.html file
1701
+
1702
+ This can be used to open the notebook in a browser
1703
+ """
1704
+ # default_url contains base_url, but so does connection_url
1705
+ open_url = self .default_url [len (self .base_url ):]
1706
+
1707
+ with open (self .browser_open_file , 'w' , encoding = 'utf-8' ) as f :
1708
+ self ._write_browser_open_file (open_url , f )
1709
+
1710
+ def _write_browser_open_file (self , url , fh ):
1711
+ if self .token :
1712
+ url = url_concat (url , {'token' : self .token })
1713
+ url = url_path_join (self .connection_url , url )
1714
+
1715
+ jinja2_env = self .web_app .settings ['jinja2_env' ]
1716
+ template = jinja2_env .get_template ('browser-open.html' )
1717
+ fh .write (template .render (open_url = url ))
1718
+
1719
+ def remove_browser_open_file (self ):
1720
+ """Remove the nbserver-<pid>-open.html file created for this server.
1721
+
1722
+ Ignores the error raised when the file has already been removed.
1723
+ """
1724
+ try :
1725
+ os .unlink (self .browser_open_file )
1726
+ except OSError as e :
1727
+ if e .errno != errno .ENOENT :
1728
+ raise
1729
+
1730
+ def launch_browser (self ):
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 not browser :
1738
+ return
1739
+
1740
+ if self .file_to_run :
1741
+ if not os .path .exists (self .file_to_run ):
1742
+ self .log .critical (_ ("%s does not exist" ) % self .file_to_run )
1743
+ self .exit (1 )
1744
+
1745
+ relpath = os .path .relpath (self .file_to_run , self .notebook_dir )
1746
+ uri = url_escape (url_path_join ('notebooks' , * relpath .split (os .sep )))
1747
+
1748
+ # Write a temporary file to open in the browser
1749
+ fd , open_file = tempfile .mkstemp (suffix = '.html' )
1750
+ with open (fd , 'w' , encoding = 'utf-8' ) as fh :
1751
+ self ._write_browser_open_file (uri , fh )
1752
+ else :
1753
+ open_file = self .browser_open_file
1754
+
1755
+ b = lambda : browser .open (
1756
+ urljoin ('file:' , pathname2url (open_file )),
1757
+ new = self .webbrowser_open_new )
1758
+ threading .Thread (target = b ).start ()
1759
+
1700
1760
def start (self ):
1701
1761
""" Start the Notebook server app, after initialization
1702
1762
@@ -1726,38 +1786,19 @@ def start(self):
1726
1786
"resources section at https://jupyter.org/community.html." ))
1727
1787
1728
1788
self .write_server_info_file ()
1789
+ self .write_browser_open_file ()
1729
1790
1730
1791
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 ()
1792
+ self .launch_browser ()
1753
1793
1754
1794
if self .token and self ._token_generated :
1755
1795
# log full URL with generated token, so there's a copy/pasteable link
1756
1796
# with auth info.
1757
1797
self .log .critical ('\n ' .join ([
1758
1798
'\n ' ,
1759
- 'Copy/paste this URL into your browser when you connect for the first time,' ,
1760
- 'to login with a token:' ,
1799
+ 'To access the notebook, open this file in a browser:' ,
1800
+ ' %s' % urljoin ('file:' , pathname2url (self .browser_open_file )),
1801
+ 'Or copy and paste one of these URLs:' ,
1761
1802
' %s' % self .display_url ,
1762
1803
]))
1763
1804
@@ -1773,6 +1814,7 @@ def start(self):
1773
1814
info (_ ("Interrupted..." ))
1774
1815
finally :
1775
1816
self .remove_server_info_file ()
1817
+ self .remove_browser_open_file ()
1776
1818
self .cleanup_kernels ()
1777
1819
1778
1820
def stop (self ):
0 commit comments