comparison libervia/web/server/tasks/implicit/task_brython.py @ 1518:eb00d593801d

refactoring: rename `libervia` to `libervia.web` + update imports following backend changes
author Goffi <goffi@goffi.org>
date Fri, 02 Jun 2023 16:49:28 +0200
parents libervia/server/tasks/implicit/task_brython.py@73c848c2f41e
children
comparison
equal deleted inserted replaced
1517:b8ed9726525b 1518:eb00d593801d
1 #!/ur/bin/env python3
2
3 from ast import literal_eval
4 import json
5 from pathlib import Path
6 import shutil
7 from typing import Any, Dict
8
9 from libervia.backend.core import exceptions
10 from libervia.backend.core.i18n import _
11 from libervia.backend.core.log import getLogger
12 from libervia.backend.tools.common import utils
13
14 from libervia.web.server.classes import Script
15 from libervia.web.server.constants import Const as C
16 from libervia.web.server.tasks import task
17
18
19 log = getLogger(__name__)
20
21
22 class Task(task.Task):
23
24 def prepare(self):
25 if "brython" not in self.resource.browser_modules:
26 raise exceptions.CancelError("No brython module found")
27
28 brython_js = self.build_path / "brython.js"
29 if not brython_js.is_file():
30 installed_ver = None
31 else:
32 with brython_js.open() as f:
33 for line in f:
34 if line.startswith('// implementation ['):
35 installed_ver = literal_eval(line[18:])[:3]
36 log.debug(
37 f"brython v{'.'.join(str(v) for v in installed_ver)} already "
38 f"installed")
39 break
40 else:
41 log.warning(
42 f"brython file at {brython_js} doesn't has implementation "
43 f"version"
44 )
45 installed_ver = None
46
47 try:
48 import brython
49 try:
50 from brython.__main__ import implementation
51 except ImportError:
52 from brython.version import implementation
53 except ModuleNotFoundError as e:
54 log.error('"brython" module is missing, can\'t use browser code for Brython')
55 raise e
56 ver = [int(v) for v in implementation.split('.')[:3]]
57 if ver != installed_ver:
58 log.info(_("Installing Brython v{version}").format(
59 version='.'.join(str(v) for v in ver)))
60 data_path = Path(brython.__file__).parent / 'data'
61 # shutil has blocking method, but the task is run before we start
62 # the web server, so it's not a big deal
63 shutil.copyfile(data_path / "brython.js", brython_js)
64 shutil.copy(data_path / "brython_stdlib.js", self.build_path)
65 else:
66 log.debug("Brython is already installed")
67
68 self.WATCH_DIRS = []
69 self.set_common_scripts()
70
71 def set_common_scripts(self):
72 for dyn_data in self.resource.browser_modules["brython"]:
73 url_hash = dyn_data['url_hash']
74 import_url = f"/{C.BUILD_DIR}/{C.BUILD_DIR_DYN}/{url_hash}"
75 dyn_data.setdefault('scripts', utils.OrderedSet()).update([
76 Script(src=f"/{C.BUILD_DIR}/brython.js"),
77 Script(src=f"/{C.BUILD_DIR}/brython_stdlib.js"),
78 ])
79 dyn_data.setdefault('template', {})['body_onload'] = self.get_body_onload(
80 extra_path=[import_url])
81 self.WATCH_DIRS.append(dyn_data['path'].resolve())
82
83 def get_body_onload(self, debug=True, cache=True, extra_path=None):
84 on_load_opts: Dict[str, Any] = {"pythonpath": [f"/{C.BUILD_DIR}"]}
85 if debug:
86 on_load_opts["debug"] = 1
87 if cache:
88 on_load_opts["cache"] = True
89 if extra_path is not None:
90 on_load_opts["pythonpath"].extend(extra_path)
91
92 return f"brython({json.dumps(on_load_opts)})"
93
94 def copy_files(self, files_paths, dest):
95 for p in files_paths:
96 log.debug(f"copying {p}")
97 if p.is_dir():
98 if p.name == '__pycache__':
99 continue
100 shutil.copytree(p, dest / p.name)
101 else:
102 shutil.copy(p, dest)
103
104 async def on_dir_event(self, host, filepath, flags):
105 self.set_common_scripts()
106 await self.manager.run_task_instance(self)
107
108 def start(self):
109 dyn_path = self.build_path / C.BUILD_DIR_DYN
110 for dyn_data in self.resource.browser_modules["brython"]:
111 url_hash = dyn_data['url_hash']
112 if url_hash is None:
113 # root modules
114 url_prefix = dyn_data.get('url_prefix')
115 if url_prefix is None:
116 dest = self.build_path
117 init_dest_url = f"/{C.BUILD_DIR}/__init__.py"
118 else:
119 dest = self.build_path / url_prefix
120 dest.mkdir(exist_ok = True)
121 init_dest_url = f"/{C.BUILD_DIR}/{url_prefix}/__init__.py"
122
123 self.copy_files(dyn_data['path'].glob('*py'), dest)
124
125 init_file = dyn_data['path'] / '__init__.py'
126 if init_file.is_file():
127 self.resource.dyn_data_common['scripts'].update([
128 Script(src=f"/{C.BUILD_DIR}/brython.js"),
129 Script(src=f"/{C.BUILD_DIR}/brython_stdlib.js"),
130 Script(type='text/python', src=init_dest_url)
131 ])
132 self.resource.dyn_data_common.setdefault(
133 "template", {})['body_onload'] = self.get_body_onload()
134 else:
135 page_dyn_path = dyn_path / url_hash
136 log.debug(f"using dynamic path at {page_dyn_path}")
137 if page_dyn_path.exists():
138 log.debug("cleaning existing path")
139 shutil.rmtree(page_dyn_path)
140
141 page_dyn_path.mkdir(parents=True, exist_ok=True)
142 log.debug("copying browser python files")
143 self.copy_files(dyn_data['path'].iterdir(), page_dyn_path)
144
145 script = Script(
146 type='text/python',
147 src=f"/{C.BUILD_DIR}/{C.BUILD_DIR_DYN}/{url_hash}/__init__.py"
148 )
149 dyn_data.setdefault('scripts', utils.OrderedSet()).add(script)