# HG changeset patch # User Goffi # Date 1588174493 -7200 # Node ID 6d49fae517ba2464e79c99445b2ba6add742cf2c # Parent 80a92eb82b7f04064daa6e34c1265eca1dc6993a pages: browser metadata + root `_browser`: - the `_browser` directory can now be put in root of a site `pages` directory, it will then include modules for the whole website - in `_browser` directories (notably the root one), a `browser_meta.json` file can be put to specify settings for a browser engine - pathlib.Path is now used LiberviaRootResource.site_path - introduced some type hints - task_brython copy modules in root `_browser` to build_path root. - minimal python version is now 3.7 due to type hints diff -r 80a92eb82b7f -r 6d49fae517ba libervia/server/constants.py --- a/libervia/server/constants.py Wed Apr 29 15:00:54 2020 +0200 +++ b/libervia/server/constants.py Wed Apr 29 17:34:53 2020 +0200 @@ -60,6 +60,7 @@ ## Libervia pages ## PAGES_META_FILE = "page_meta.py" PAGES_BROWSER_DIR = "_browser" + PAGES_BROWSER_META_FILE = "browser_meta.json" PAGES_ACCESS_NONE = ( "none" ) #  no access to this page (using its path will return a 404 error) diff -r 80a92eb82b7f -r 6d49fae517ba libervia/server/pages.py --- a/libervia/server/pages.py Wed Apr 29 15:00:54 2020 +0200 +++ b/libervia/server/pages.py Wed Apr 29 17:34:53 2020 +0200 @@ -1,6 +1,5 @@ #!/usr/bin/env python3 - # Libervia: a Salut à Toi frontend # Copyright (C) 2011-2020 Jérôme Poisson @@ -17,14 +16,18 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from __future__ import annotations + import uuid import os.path import urllib.request, urllib.parse, urllib.error import time import hashlib import copy +import json +from pathlib import Path from functools import reduce -from pathlib import Path +from typing import Optional, List from twisted.web import server from twisted.web import resource as web_resource @@ -36,6 +39,7 @@ from sat.core.i18n import _ from sat.core import exceptions from sat.tools.common import date_utils +from sat.tools.common import utils from sat.core.log import getLogger from sat_frontends.bridge.bridge_frontend import BridgeException @@ -123,7 +127,7 @@ @param host(Libervia): the running instance of Libervia @param vhost_root(web_resource.Resource): root resource of the virtual host which handle this page. - @param root_dir(unicode): aboslute file path of the page + @param root_dir(Path): aboslute file path of the page @param url(unicode): relative URL to the page this URL may not be valid, as pages may require path arguments @param name(unicode, None): if not None, a unique name to identify the page @@ -260,7 +264,7 @@ def createPage(host, meta_path, vhost_root, url_elts, replace_on_conflict=False): """Create a LiberviaPage instance - @param meta_path(unicode): path to the page_meta.py file + @param meta_path(Path): path to the page_meta.py file @param vhost_root(resource.Resource): root resource of the virtual host @param url_elts(list[unicode]): list of path element from root site to this page @param replace_on_conflict(bool): same as for [LiberviaPage] @@ -268,7 +272,7 @@ - page_data: dict containing data of the page - libervia_page: created resource """ - dir_path = os.path.dirname(meta_path) + dir_path = meta_path.parent page_data = {"__name__": ".".join(["page"] + url_elts)} # we don't want to force the presence of __init__.py # so we use execfile instead of import. @@ -295,6 +299,47 @@ replace_on_conflict=replace_on_conflict ) + @staticmethod + def createBrowserData( + vhost_root, + resource: Optional(LiberviaPage), + browser_path: Path, + path_elts: Optional(List[str]), + engine: str = "brython" + ) -> None: + """create and store data for browser dynamic code""" + dyn_data = { + "path": browser_path, + "url_hash": ( + hashlib.sha256('/'.join(path_elts).encode()).hexdigest() + if path_elts is not None else None + ), + } + browser_meta_path = browser_path / C.PAGES_BROWSER_META_FILE + if browser_meta_path.is_file(): + with browser_meta_path.open() as f: + browser_meta = json.load(f) + utils.recursive_update(vhost_root.browser_modules, browser_meta) + if resource is not None: + utils.recursive_update(resource.dyn_data, browser_meta) + + init_path = browser_path / '__init__.py' + if init_path.is_file(): + vhost_root.browser_modules.setdefault( + engine, []).append(dyn_data) + if resource is not None: + resource.dyn_data[engine] = dyn_data + elif path_elts is None: + try: + next(browser_path.glob('*.py')) + except StopIteration: + # no python file, nothing for Brython + pass + else: + vhost_root.browser_modules.setdefault( + engine, []).append(dyn_data) + + @classmethod def importPages(cls, host, vhost_root, root_path=None, _parent=None, _path=None, _extra_pages=False): @@ -302,7 +347,7 @@ @param host(Libervia): Libervia instance @param vhost_root(LiberviaRootResource): root of this VirtualHost - @param root_path(unicode, None): use this root path instead of vhost_root's one + @param root_path(Path, None): use this root path instead of vhost_root's one Used to add default site pages to external sites @param _parent(Resource, None): _parent page. Do not set yourself, this is for internal use only @@ -315,23 +360,27 @@ _path = [] if _parent is None: if root_path is None: - root_dir = os.path.join(vhost_root.site_path, C.PAGES_DIR) + root_dir = vhost_root.site_path / C.PAGES_DIR else: - root_dir = os.path.join(root_path, C.PAGES_DIR) + root_dir = root_path / C.PAGES_DIR _extra_pages = True _parent = vhost_root + root_browser_path = root_dir / C.PAGES_BROWSER_DIR + if root_browser_path.is_dir(): + cls.createBrowserData(vhost_root, None, root_browser_path, None) else: root_dir = _parent.root_dir + for d in os.listdir(root_dir): - dir_path = os.path.join(root_dir, d) - if not os.path.isdir(dir_path): + dir_path = root_dir / d + if not dir_path.is_dir(): continue if _extra_pages and d in _parent.children: log.debug(_("[{host_name}] {path} is already present, ignoring it") .format(host_name=vhost_root.host_name, path='/'.join(_path+[d]))) continue - meta_path = os.path.join(dir_path, C.PAGES_META_FILE) - if os.path.isfile(meta_path): + meta_path = dir_path / C.PAGES_META_FILE + if meta_path.is_file(): new_path = _path + [d] try: page_data, resource = cls.createPage( @@ -342,7 +391,7 @@ continue else: raise e - _parent.putChild(d.encode('utf-8'), resource) + _parent.putChild(str(d).encode(), resource) log_msg = ("[{host_name}] Added /{path} page".format( host_name=vhost_root.host_name, path="[…]/".join(new_path))) @@ -374,17 +423,9 @@ host, vhost_root, _parent=resource, _path=new_path, _extra_pages=_extra_pages) # now we check if there is some code for browser - browser_path = Path(dir_path) / C.PAGES_BROWSER_DIR + browser_path = dir_path / C.PAGES_BROWSER_DIR if browser_path.is_dir(): - # for now we only handle Brython - dyn_data = { - "path": browser_path, - "url_hash": hashlib.sha256( - '/'.join(new_path).encode()).hexdigest(), - } - vhost_root.browser_modules.setdefault( - "brython", []).append(dyn_data) - resource.dyn_data['brython'] = dyn_data + cls.createBrowserData(vhost_root, resource, browser_path, new_path) @classmethod def onFileChange(cls, host, file_path, flags, site_root, site_path): diff -r 80a92eb82b7f -r 6d49fae517ba libervia/server/server.py --- a/libervia/server/server.py Wed Apr 29 15:00:54 2020 +0200 +++ b/libervia/server/server.py Wed Apr 29 17:34:53 2020 +0200 @@ -183,7 +183,7 @@ self.host = host self.host_name = host_name self.site_name = site_name - self.site_path = site_path + self.site_path = Path(site_path) self.named_pages = {} self.browser_modules = {} self.uri_callbacks = {} @@ -786,9 +786,9 @@ # we create virtual hosts and import Libervia pages into them self.vhost_root = vhost.NameVirtualHost() - default_site_path = os.path.abspath(os.path.dirname(libervia.__file__)) + default_site_path = Path(libervia.__file__).parent.resolve() # self.sat_root is official Libervia site - root_path = os.path.join(default_site_path, C.TEMPLATE_STATIC_DIR) + root_path = default_site_path / C.TEMPLATE_STATIC_DIR self.sat_root = default_root = LiberviaRootResource( host=self, host_name='', site_name='', site_path=default_site_path, path=root_path) diff -r 80a92eb82b7f -r 6d49fae517ba libervia/server/tasks/implicit/task_brython.py --- a/libervia/server/tasks/implicit/task_brython.py Wed Apr 29 15:00:54 2020 +0200 +++ b/libervia/server/tasks/implicit/task_brython.py Wed Apr 29 17:34:53 2020 +0200 @@ -7,7 +7,6 @@ from sat.core.i18n import _ from sat.core.log import getLogger from sat.core import exceptions -from sat.tools.common.template import safe from libervia.server.constants import Const as C from libervia.server.tasks import task @@ -16,7 +15,6 @@ class Task(task.Task): - DOC_DIRS_DEFAULT = ('doc', 'docs') def prepare(self): if "brython" not in self.resource.browser_modules: @@ -64,7 +62,8 @@ import_url = f"/{C.BUILD_DIR}/{C.BUILD_DIR_DYN}/{url_hash}" on_load_opts = { "debug": 1, - "pythonpath": [import_url], + "cache": True, + "pythonpath": [f"/{C.BUILD_DIR}", import_url], } dyn_data['template'] = { "scripts": [{"src": f"/{C.BUILD_DIR}/brython.js"}], @@ -72,28 +71,35 @@ } self.WATCH_DIRS.append(dyn_data['path'].resolve()) + def copyFiles(self, files_paths, dest): + for p in files_paths: + log.debug(f"copying {p}") + if p.is_dir(): + shutil.copytree(p, dest) + else: + shutil.copy(p, dest) + def start(self): dyn_path = self.build_path / C.BUILD_DIR_DYN for dyn_data in self.resource.browser_modules["brython"]: url_hash = dyn_data['url_hash'] - page_dyn_path = dyn_path / url_hash - if page_dyn_path.exists(): - log.debug("cleaning existing path") - shutil.rmtree(page_dyn_path) + if url_hash is None: + # root modules + self.copyFiles(dyn_data['path'].glob('*py'), self.build_path) + else: + page_dyn_path = dyn_path / url_hash + if page_dyn_path.exists(): + log.debug("cleaning existing path") + shutil.rmtree(page_dyn_path) - page_dyn_path.mkdir(parents=True, exist_ok=True) - log.debug("copying browser python files") - for p in dyn_data['path'].iterdir(): - log.debug(f"copying {p}") - if p.is_dir(): - shutil.copytree(p, page_dyn_path) - else: - shutil.copy(p, page_dyn_path) + page_dyn_path.mkdir(parents=True, exist_ok=True) + log.debug("copying browser python files") + self.copyFiles(dyn_data['path'].iterdir(), page_dyn_path) - script = { - 'type': 'text/python', - 'src': f"/{C.BUILD_DIR}/{C.BUILD_DIR_DYN}/{url_hash}/__init__.py" - } - scripts = dyn_data['template']['scripts'] - if script not in scripts: - scripts.append(script) + script = { + 'type': 'text/python', + 'src': f"/{C.BUILD_DIR}/{C.BUILD_DIR_DYN}/{url_hash}/__init__.py" + } + scripts = dyn_data['template']['scripts'] + if script not in scripts: + scripts.append(script) diff -r 80a92eb82b7f -r 6d49fae517ba setup.py --- a/setup.py Wed Apr 29 15:00:54 2020 +0200 +++ b/setup.py Wed Apr 29 17:34:53 2020 +0200 @@ -90,5 +90,5 @@ use_scm_version=libervia_dev_version if is_dev_version else False, install_requires=install_requires, package_data={"libervia": ["VERSION"]}, - python_requires=">=3.6", + python_requires=">=3.7", )