/
convert.py
126 lines (116 loc) · 4.51 KB
/
convert.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import argparse
import json
import pathlib
import time
from bokeh.command.subcommand import Argument, Subcommand
from ..io.convert import convert_apps
class Convert(Subcommand):
''' Subcommand to convert Panel application to some build target, e.g. pyodide or pyscript.
'''
#: name for this subcommand
name = "convert"
help = "Convert a Panel App to another format, e.g. a HTML file."
args = (
('files', Argument(
metavar = 'DIRECTORY-OR-SCRIPT',
nargs = '*',
help = "The app directories or scripts to serve (serve empty document if not specified)",
default = None,
)),
('--to', dict(
action = 'store',
type = str,
help = "The format to convert to, one of 'pyodide' (default), 'pyodide-worker' or 'pyscript'",
default = 'pyodide'
)),
('--compiled', dict(
default = False,
action = 'store_false',
help = "Whether to use the compiled and faster version of Pyodide."
)),
('--out', dict(
action = 'store',
type = str,
help = "The directory to write the file to.",
)),
('--title', dict(
action = 'store',
type = str,
help = "A custom title for the application(s).",
)),
('--skip-embed', dict(
action = 'store_true',
help = "Whether to skip embedding pre-rendered content in the converted file to display content while app is loading.",
)),
('--index', dict(
action = 'store_true',
help = "Whether to create an index if multiple files are served.",
)),
('--pwa', dict(
action = 'store_true',
help = "Whether to add files to serve applications as a Progressive Web App.",
)),
('--requirements', dict(
nargs = '+',
help = (
"Explicit requirements to add to the converted file, a single requirements.txt file or a "
"JSON file containing requirements per app. By default requirements are inferred from the code."
)
)),
('--disable-http-patch', dict(
default = False,
action = 'store_false',
help = "Whether to disable patching http requests using the pyodide-http library."
)),
('--watch', dict(
action = 'store_true',
help = "Watch the files"
)),
('--num-procs', dict(
action = 'store',
type = int,
default = 1,
help = "The number of processes to start in parallel to convert the apps."
)),
)
_targets = ('pyscript', 'pyodide', 'pyodide-worker')
def invoke(self, args: argparse.Namespace) -> None:
runtime = args.to.lower()
if runtime not in self._targets:
raise ValueError(f'Supported conversion targets include: {self._targets!r}')
requirements = args.requirements or 'auto'
if (
isinstance(requirements, list) and
len(requirements) == 1 and
pathlib.Path(requirements[0]).is_file()
):
req = requirements[0]
if req.endswith('.txt'):
requirements = requirements[0]
elif req.endswith('.json'):
with open(req, encoding='utf-8') as req_file:
requirements = json.load(req_file)
prev_hashes = {}
built = False
while True:
hashes = {f: hash(open(f).read()) for f in args.files}
if hashes == prev_hashes:
time.sleep(0.5)
continue
files = [f for f, h in hashes.items() if prev_hashes.get(f) != h]
index = args.index and not built
try:
convert_apps(
files, dest_path=args.out, runtime=runtime, requirements=requirements,
prerender=not args.skip_embed, build_index=index, build_pwa=args.pwa,
title=args.title, max_workers=args.num_procs,
http_patch=not args.disable_http_patch, compiled=args.compiled,
verbose=True
)
except KeyboardInterrupt:
print("Aborted while building docs.") # noqa: T201
break
built = True
prev_hashes = hashes
if not args.watch:
return