Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# (c) Stefan Countryman, 2019
3import os
4import logging
5import atexit
6from subprocess import Popen, PIPE
7from plumbum.cmd import jupyter
8from llama.classes import COLOR
9from llama.utils import LOGDIR, OUTPUT_DIR
10from llama.cli import get_logging_cli, CliParser
11from llama.serve.jupyter import (
12 __doc__,
13 load_notebook,
14 README,
15 VIEW_LOGS,
16 LLAMA_JUP_PORT,
17 SERVER_LIST_FILE,
18)
19from llama.serve.gui.domain import LLAMA_DOMAIN
20from llama.com.slack import alert_maintainers
22LOGGER = logging.getLogger(__name__)
23JUPYTER_LOG = os.path.join(LOGDIR, 'jupyter.log')
24JUPYTER_FILES = os.path.join(OUTPUT_DIR, 'jupyter')
25DEFAULT_IP = '0.0.0.0'
28def get_parser():
29 """Get CLI Parser."""
30 parser = CliParser(description=__doc__,
31 parents=(get_logging_cli(JUPYTER_LOG, 'info'),))
32 parser.add_argument("--notebook-dir", default=OUTPUT_DIR, help=f"""
33 Where to store jupyter notebook files. By default, they will be saved
34 in the LLAMA data directory in {OUTPUT_DIR}""")
35 parser.add_argument("--ip", default=DEFAULT_IP, help=f"""
36 The IP address the server will listen on. (default: {DEFAULT_IP})""")
37 parser.add_argument("-a", "--alert-maintainers", action='store_true',
38 help="""
39 If provided, use ``llama.com.slack.alert_maintainers`` to
40 message LLAMA maintainers with a list of active Jupyter notebooks and
41 their login tokens. This allows
42 Slack users to access the notebook at the provided URL. These tokens
43 can be used to log in to this Jupyter notebook (and any others running
44 on this server/container). BE CAREFUL WITH THESE TOKENS! They provide
45 full access to the Jupyter notebook; you should probably only use this
46 in production, and make sure not to share those tokens. You should also
47 make sure to regenerate those tokens regularly. Note also that the
48 script will fail if you try to alert maintainers without providing
49 valid Slack credentials (see ``llama.com.slack``).""")
50 parser.add_argument("-w", "--writeable-docs", action='store_true', help="""
51 If provided, set any documentation notebooks (like README.ipynb) to
52 writeable. Use this for development mode and then commit the notebook
53 with ``llama dev upload -g README.ipynb`` (from the notebook directory)
54 and update the remote URL for ``llama.serve.jupyter.README`` with the
55 printed URL.""")
56 parser.add_argument("-k", "--keep-docs", action='store_true', help="""
57 If provided, don't bother downloading new README if it's stored
58 locally; in other words, keep existing documentation if present so as
59 not to accidentally delete development work. As with
60 ``--writeable-docs``, this should probably only be used in development
61 mode.""")
62 parser.add_argument("--show-hidden", action='store_true', help="""
63 If provided, show hidden files in the file browser. This is useful for
64 modifying metadata rider files used by the pipeline.""")
65 return parser
68def main():
69 """Run CLI."""
70 parser = get_parser()
71 args = parser.parse_args()
72 if not os.path.isdir(args.notebook_dir):
73 os.makedirs(args.notebook_dir)
74 load_notebook(README, 'README.ipynb', readonly=not args.writeable_docs,
75 clobber=not args.keep_docs)
76 load_notebook(VIEW_LOGS, 'VIEW_LOGS.ipynb', readonly=not args.writeable_docs,
77 clobber=not args.keep_docs)
78 cmd = [
79 'jupyter',
80 'notebook',
81 '--no-browser',
82 '--ip',
83 args.ip,
84 '--port',
85 LLAMA_JUP_PORT,
86 '--port-retries=0',
87 f'--ContentsManager.allow_hidden={args.show_hidden}',
88 '--notebook-dir',
89 args.notebook_dir,
90 '--log-level',
91 args.verbosity.upper(),
92 '-y',
93 '--allow-root',
94 ]
95 LOGGER.info("STARTING JUPYTER NOTEBOOK WITH COMMAND: %s", ' '.join(cmd))
96 with open(os.devnull, 'wb') as devnull:
97 proc = Popen(cmd, stdout=devnull, stderr=PIPE)
98 atexit.register(proc.kill) # kill the jupyter notebook on exit
99 while True:
100 output = proc.stderr.readline().decode().strip()
101 if output == '' and proc.poll() is not None:
102 break
103 if output:
104 LOGGER.info(COLOR.MAGENTA + output + COLOR.CLEAR)
105 if 'Or copy and paste one of these URLs' in output:
106 notebooks = jupyter['notebook', 'list']()
107 notebooks = notebooks.replace(
108 f"{args.ip}:{LLAMA_JUP_PORT}",
109 f"{LLAMA_DOMAIN}:{LLAMA_JUP_PORT}",
110 )
111 with open(SERVER_LIST_FILE, 'w') as server_list:
112 for line in notebooks.split('\n'):
113 if 'token=' in line:
114 server_list.write(line+'\n')
115 atexit.register(lambda: os.unlink(SERVER_LIST_FILE))
116 if args.alert_maintainers:
117 alert_maintainers("NOTEBOOK STARTED\n" + notebooks)
118 return proc.poll()
121if __name__ == '__main__':
122 exit(main())