# HG changeset patch # User Goffi # Date 1550851057 -3600 # Node ID 94f9d81a475ec2faa45544ad2b3c0c41c62012c0 # Parent 1c23252958edc33f32275b90a7b7a48df22755fd pages: auto reloading when developer mode is activated, pages are automatically reloaded when page_meta.py is modified. diff -r 1c23252958ed -r 94f9d81a475e libervia/server/pages.py --- a/libervia/server/pages.py Fri Feb 22 16:57:37 2019 +0100 +++ b/libervia/server/pages.py Fri Feb 22 16:57:37 2019 +0100 @@ -111,6 +111,7 @@ self, host, vhost_root, root_dir, url, name=None, redirect=None, access=None, dynamic=False, parse_url=None, prepare_render=None, render=None, template=None, on_data_post=None, on_data=None, on_signal=None, url_cache=False, + replace_on_conflict=False ): """Initiate LiberviaPage instance @@ -158,6 +159,8 @@ received. This method is used with Libervia's websocket mechanism @param url_cache(boolean): if set, result of parse_url is cached (per profile). Useful when costly calls (e.g. network) are done while parsing URL. + @param replace_on_conflict(boolean): if True, don't raise ConflictError if a + page of this name already exists, but replace it """ web_resource.Resource.__init__(self) @@ -167,7 +170,8 @@ self.url = url self.name = name if name is not None: - if name in self.named_pages: + if (name in self.named_pages + and not (replace_on_conflict and self.named_pages[name].url == url)): raise exceptions.ConflictError( _(u'a Libervia page named "{}" already exists'.format(name))) if u"/" in name: @@ -249,6 +253,45 @@ def main_menu(self): return self.vhost_root.main_menu + @staticmethod + 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 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] + @return (tuple[dict, LiberviaPage]): tuple with: + - page_data: dict containing data of the page + - libervia_page: created resource + """ + dir_path = os.path.dirname(meta_path) + page_data = {"__name__": u".".join([u"page"] + url_elts)} + # we don't want to force the presence of __init__.py + # so we use execfile instead of import. + # TODO: when moved to Python 3, __init__.py is not mandatory anymore + # so we can switch to import + execfile(meta_path, page_data) + return page_data, LiberviaPage( + host=host, + vhost_root=vhost_root, + root_dir=dir_path, + url=u"/" + u"/".join(url_elts), + name=page_data.get(u"name"), + redirect=page_data.get(u"redirect"), + access=page_data.get(u"access"), + dynamic=page_data.get(u"dynamic", False), + parse_url=page_data.get(u"parse_url"), + prepare_render=page_data.get(u"prepare_render"), + render=page_data.get(u"render"), + template=page_data.get(u"template"), + on_data_post=page_data.get(u"on_data_post"), + on_data=page_data.get(u"on_data"), + on_signal=page_data.get(u"on_signal"), + url_cache=page_data.get(u"url_cache", False), + replace_on_conflict=replace_on_conflict + ) + @classmethod def importPages(cls, host, vhost_root, root_path=None, _parent=None, _path=None, _extra_pages=False): @@ -286,32 +329,9 @@ continue meta_path = os.path.join(dir_path, C.PAGES_META_FILE) if os.path.isfile(meta_path): - page_data = {"__name__": u".".join([u"page"] + _path + [d])} new_path = _path + [d] - # we don't want to force the presence of __init__.py - # so we use execfile instead of import. - # TODO: when moved to Python 3, __init__.py is not mandatory anymore - # so we can switch to import - execfile(meta_path, page_data) try: - resource = LiberviaPage( - host=host, - vhost_root=vhost_root, - root_dir=dir_path, - url=u"/" + u"/".join(new_path), - name=page_data.get(u"name"), - redirect=page_data.get(u"redirect"), - access=page_data.get(u"access"), - dynamic=page_data.get(u"dynamic", False), - parse_url=page_data.get(u"parse_url"), - prepare_render=page_data.get(u"prepare_render"), - render=page_data.get(u"render"), - template=page_data.get(u"template"), - on_data_post=page_data.get(u"on_data_post"), - on_data=page_data.get(u"on_data"), - on_signal=page_data.get(u"on_signal"), - url_cache=page_data.get(u"url_cache", False), - ) + page_data, resource = cls.createPage(host, meta_path, vhost_root, new_path) except exceptions.ConflictError as e: if _extra_pages: # extra pages are discarded if there is already an existing page @@ -347,10 +367,85 @@ resource.registerURI(uri_tuple, cb) LiberviaPage.importPages( - host, vhost_root, _parent=resource, _path=new_path, _extra_pages=_extra_pages) + host, vhost_root, _parent=resource, _path=new_path, + _extra_pages=_extra_pages) + + @classmethod + def onFileChange(cls, host, file_path, flags, site_root, site_path): + """Method triggered by file_watcher when something is changed in files + + This method is used in dev mode to reload pages when needed + @param file_path(filepath.FilePath): path of the file which triggered the event + @param flags[list[unicode]): human readable flags of the event (from + internet.inotify) + @param site_root(LiberviaRootResource): root of the site + @param site_path(unicode): absolute path of the site + """ + if flags == ['create']: + return + path = file_path.path.decode('utf-8') + base_name = os.path.basename(path) + if base_name != u"page_meta.py": + # we only handle libervia pages + return + + log.debug(u"{flags} event(s) received for {file_path}".format( + flags=u", ".join(flags), file_path=file_path)) + + dir_path = os.path.dirname(path) + if not dir_path.startswith(site_path): + raise exceptions.InternalError(u"watched file should start with site path") + + path_elts = [p for p in dir_path[len(site_path):].split('/') if p] + if not path_elts: + return + + if path_elts[0] == C.PAGES_DIR: + # a page has been modified + del path_elts[0] + if not path_elts: + # we need at least one element to parse + return + # we retrieve page by starting from site root and finding each path element + parent = page = site_root + new_page = False + for idx, child_name in enumerate(path_elts): + try: + page = page.children[child_name] + except KeyError: + if idx != len(path_elts)-1: + # a page has been created in a subdir when one or more + # page_meta.py are missing on the way + log.warning(_(u"Can't create a page at {path}, missing parents") + .format(path=path)) + return + new_page = True + else: + if idx