diff libervia/server/pages.py @ 1216:b2d067339de3

python 3 port: /!\ Python 3.6+ is now needed to use libervia /!\ instability may occur and features may not be working anymore, this will improve with time /!\ TxJSONRPC dependency has been removed The same procedure as in backend has been applied (check backend commit ab2696e34d29 logs for details). Removed now deprecated code (Pyjamas compiled browser part, legacy blog, JSON RPC related code). Adapted code to work without `html` and `themes` dirs.
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:12:31 +0200
parents 584e29d9510a
children 62bf4f87c249
line wrap: on
line diff
--- a/libervia/server/pages.py	Tue Aug 13 09:39:33 2019 +0200
+++ b/libervia/server/pages.py	Tue Aug 13 19:12:31 2019 +0200
@@ -36,10 +36,11 @@
 
 import uuid
 import os.path
-import urllib
+import urllib.request, urllib.parse, urllib.error
 import time
 import hashlib
 import copy
+from functools import reduce
 
 log = getLogger(__name__)
 
@@ -176,11 +177,11 @@
             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:
-                raise ValueError(_(u'"/" is not allowed in page names'))
+                    _('a Libervia page named "{}" already exists'.format(name)))
+            if "/" in name:
+                raise ValueError(_('"/" is not allowed in page names'))
             if not name:
-                raise ValueError(_(u"a page name can't be empty"))
+                raise ValueError(_("a page name can't be empty"))
             self.named_pages[name] = self
         if access is None:
             access = C.PAGES_ACCESS_PUBLIC
@@ -190,7 +191,7 @@
             C.PAGES_ACCESS_NONE,
         ):
             raise NotImplementedError(
-                _(u"{} access is not implemented yet").format(access)
+                _("{} access is not implemented yet").format(access)
             )
         self.access = access
         self.dynamic = dynamic
@@ -202,8 +203,8 @@
                 for x in (parse_url, prepare_render, render, template)
             ):
                 raise ValueError(
-                    _(u"you can't use full page redirection with other rendering"
-                      u"method, check self.pageRedirect if you need to use them"))
+                    _("you can't use full page redirection with other rendering"
+                      "method, check self.pageRedirect if you need to use them"))
             self.redirect = redirect
         else:
             self.redirect = None
@@ -219,22 +220,18 @@
             # none pages just return a 404, no further check is needed
             return
         if template is not None and render is not None:
-            log.error(_(u"render and template methods can't be used at the same time"))
+            log.error(_("render and template methods can't be used at the same time"))
         if parse_url is not None and not callable(parse_url):
-            log.error(_(u"parse_url must be a callable"))
+            log.error(_("parse_url must be a callable"))
 
         # if not None, next rendering will be cached
         #  it must then contain a list of the the keys to use (without the page instance)
         # e.g. [C.SERVICE_PROFILE, "pubsub", server@example.tld, pubsub_node]
         self._do_cache = None
 
-    def __unicode__(self):
-        return u"LiberviaPage {name} at {url} (vhost: {vhost_root})".format(
-            name=self.name or u"<anonymous>", url=self.url, vhost_root=self.vhost_root)
-
     def __str__(self):
-        return self.__unicode__().encode("utf-8")
-
+        return "LiberviaPage {name} at {url} (vhost: {vhost_root})".format(
+            name=self.name or "<anonymous>", url=self.url, vhost_root=self.vhost_root)
 
     @property
     def named_pages(self):
@@ -269,29 +266,29 @@
             - libervia_page: created resource
         """
         dir_path = os.path.dirname(meta_path)
-        page_data = {"__name__": u".".join([u"page"] + url_elts)}
+        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.
         # TODO: when moved to Python 3, __init__.py is not mandatory anymore
         #       so we can switch to import
-        execfile(meta_path, page_data)
+        exec(compile(open(meta_path, "rb").read(), meta_path, 'exec'), 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),
+            url="/" + "/".join(url_elts),
+            name=page_data.get("name"),
+            redirect=page_data.get("redirect"),
+            access=page_data.get("access"),
+            dynamic=page_data.get("dynamic", False),
+            parse_url=page_data.get("parse_url"),
+            prepare_render=page_data.get("prepare_render"),
+            render=page_data.get("render"),
+            template=page_data.get("template"),
+            on_data_post=page_data.get("on_data_post"),
+            on_data=page_data.get("on_data"),
+            on_signal=page_data.get("on_signal"),
+            url_cache=page_data.get("url_cache", False),
             replace_on_conflict=replace_on_conflict
         )
 
@@ -327,8 +324,8 @@
             if not os.path.isdir(dir_path):
                 continue
             if _extra_pages and d in _parent.children:
-                log.debug(_(u"[{host_name}] {path} is already present, ignoring it")
-                    .format(host_name=vhost_root.host_name, path=u'/'.join(_path+[d])))
+                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):
@@ -341,29 +338,29 @@
                         continue
                     else:
                         raise e
-                _parent.putChild(d, resource)
-                log_msg = (u"[{host_name}] Added /{path} page".format(
+                _parent.putChild(d.encode('utf-8'), resource)
+                log_msg = ("[{host_name}] Added /{path} page".format(
                     host_name=vhost_root.host_name,
-                    path=u"[…]/".join(new_path)))
+                    path="[…]/".join(new_path)))
                 if _extra_pages:
                     log.debug(log_msg)
                 else:
                     log.info(log_msg)
                 if "uri_handlers" in page_data:
                     if not isinstance(page_data, dict):
-                        log.error(_(u"uri_handlers must be a dict"))
+                        log.error(_("uri_handlers must be a dict"))
                     else:
-                        for uri_tuple, cb_name in page_data["uri_handlers"].iteritems():
-                            if len(uri_tuple) != 2 or not isinstance(cb_name, basestring):
-                                log.error(_(u"invalid uri_tuple"))
+                        for uri_tuple, cb_name in page_data["uri_handlers"].items():
+                            if len(uri_tuple) != 2 or not isinstance(cb_name, str):
+                                log.error(_("invalid uri_tuple"))
                                 continue
                             if not _extra_pages:
-                                log.info(_(u"setting {}/{} URIs handler")
+                                log.info(_("setting {}/{} URIs handler")
                                          .format(*uri_tuple))
                             try:
                                 cb = page_data[cb_name]
                             except KeyError:
-                                log.error(_(u"missing {name} method to handle {1}/{2}")
+                                log.error(_("missing {name} method to handle {1}/{2}")
                                           .format(name=cb_name, *uri_tuple))
                                 continue
                             else:
@@ -388,16 +385,16 @@
             return
         path = file_path.path.decode('utf-8')
         base_name = os.path.basename(path)
-        if base_name != u"page_meta.py":
+        if base_name != "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))
+        log.debug("{flags} event(s) received for {file_path}".format(
+            flags=", ".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")
+            raise exceptions.InternalError("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:
@@ -422,7 +419,7 @@
                     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")
+                        log.warning(_("Can't create a page at {path}, missing parents")
                                     .format(path=path))
                         return
                     new_page = True
@@ -442,7 +439,7 @@
                         #        EncodingResourceWrapper should probably be removed
                         resource.children = page.children
             except Exception as e:
-                log.warning(_(u"Can't create page: {reason}").format(reason=e))
+                log.warning(_("Can't create page: {reason}").format(reason=e))
             else:
                 url_elt = path_elts[-1]
                 if not new_page:
@@ -451,9 +448,9 @@
                 # we can now add the new page
                 parent.putChild(url_elt, resource)
                 if new_page:
-                    log.info(_(u"{page} created").format(page=resource))
+                    log.info(_("{page} created").format(page=resource))
                 else:
-                    log.info(_(u"{page} reloaded").format(page=resource))
+                    log.info(_("{page} reloaded").format(page=resource))
 
     def registerURI(self, uri_tuple, get_uri_cb):
         """Register a URI handler
@@ -466,7 +463,7 @@
             can't handle this URL
         """
         if uri_tuple in self.uri_callbacks:
-            log.info(_(u"{}/{} URIs are already handled, replacing by the new handler")
+            log.info(_("{}/{} URIs are already handled, replacing by the new handler")
                 .format( *uri_tuple))
         self.uri_callbacks[uri_tuple] = (self, get_uri_cb)
 
@@ -499,7 +496,7 @@
         # FIXME: add a timeout; if socket is not opened before it, signal handler
         #        must be removed
         if not self.dynamic:
-            log.error(_(u"You can't register signal if page is not dynamic"))
+            log.error(_("You can't register signal if page is not dynamic"))
             return
         signal_id = self.getSignalId(request)
         LiberviaPage.signals_handlers.setdefault(signal, {})[signal_id] = [
@@ -522,7 +519,7 @@
     def getPagePathFromURI(self, uri):
         return self.vhost_root.getPagePathFromURI(uri)
 
-    def getPageRedirectURL(self, request, page_name=u"login", url=None):
+    def getPageRedirectURL(self, request, page_name="login", url=None):
         """generate URL for a page with redirect_url parameter set
 
         mainly used for login page with redirection to current page
@@ -532,9 +529,9 @@
             None to use request path (i.e. current page)
         @return (unicode): URL to use
         """
-        return u"{root_url}?redirect_url={redirect_url}".format(
+        return "{root_url}?redirect_url={redirect_url}".format(
             root_url=self.getPageByName(page_name).url,
-            redirect_url=urllib.quote_plus(request.uri)
+            redirect_url=urllib.parse.quote_plus(request.uri)
             if url is None
             else url.encode("utf-8"),
         )
@@ -551,7 +548,7 @@
             #  we check for redirection
             redirect_data = self.pages_redirects[self.name]
             args_hash = tuple(args)
-            for limit in xrange(len(args) + 1):
+            for limit in range(len(args) + 1):
                 current_hash = args_hash[:limit]
                 if current_hash in redirect_data:
                     url_base = redirect_data[current_hash]
@@ -572,7 +569,7 @@
         # the real request
 
         # we ignore empty path elements (i.e. double '/' or '/' at the end)
-        path_elts = [p for p in request.path.split("/") if p]
+        path_elts = [p for p in request.path.decode('utf-8').split("/") if p]
 
         if request.postpath:
             if not request.postpath[-1]:
@@ -584,7 +581,7 @@
                 # path elements
                 path_elts = path_elts[: -len(request.postpath)]
 
-        return u"/" + "/".join(path_elts).decode("utf-8")
+        return "/" + "/".join(path_elts)
 
     def getParamURL(self, request, **kwargs):
         """use URL of current request but modify the parameters in query part
@@ -594,10 +591,10 @@
         """
         current_url = self.getCurrentURL(request)
         if kwargs:
-            encoded = urllib.urlencode(
-                {k: v.encode("utf-8") for k, v in kwargs.iteritems()}
-            ).decode("utf-8")
-            current_url = current_url + u"?" + encoded
+            encoded = urllib.parse.urlencode(
+                {k: v for k, v in kwargs.items()}
+            )
+            current_url = current_url + "?" + encoded
         return current_url
 
     def getSubPageByName(self, subpage_name, parent=None):
@@ -612,15 +609,15 @@
         """
         if parent is None:
             parent = self
-        for path, child in parent.children.iteritems():
+        for path, child in parent.children.items():
             try:
                 child_name = child.name
             except AttributeError:
                 #  LiberviaPages have a name, but maybe this is an other Resource
                 continue
             if child_name == subpage_name:
-                return path, child
-        raise exceptions.NotFound(_(u"requested sub page has not been found"))
+                return path.decode('utf-8'), child
+        raise exceptions.NotFound(_("requested sub page has not been found"))
 
     def getSubPageURL(self, request, page_name, *args):
         """retrieve a page in direct children and build its URL according to request
@@ -645,7 +642,7 @@
         current_url = self.getCurrentURL(request)
         path, child = self.getSubPageByName(page_name)
         return os.path.join(
-            u"/", current_url, path, *[quote(a) for a in args if a is not None]
+            "/", current_url, path, *[quote(a) for a in args if a is not None]
         )
 
     def getURLByNames(self, named_path):
@@ -671,7 +668,7 @@
                 path.append(sub_path)
                 if page_args:
                     path.extend([quote(a) for a in page_args])
-        return self.host.checkRedirection(self.vhost_root, u"/".join(path))
+        return self.host.checkRedirection(self.vhost_root, "/".join(path))
 
     def getURLByPath(self, *args):
         """Generate URL by path
@@ -708,12 +705,12 @@
             else:
                 path, current_page = current_page.getSubPageByName(args.pop(0))
                 arguments = [path]
-        return self.host.checkRedirection(self.vhost_root, u"/".join(url_elts))
+        return self.host.checkRedirection(self.vhost_root, "/".join(url_elts))
 
     def getChildWithDefault(self, path, request):
         # we handle children ourselves
         raise exceptions.InternalError(
-            u"this method should not be used with LiberviaPage"
+            "this method should not be used with LiberviaPage"
         )
 
     def nextPath(self, request):
@@ -726,25 +723,25 @@
         """
         pathElement = request.postpath.pop(0)
         request.prepath.append(pathElement)
-        return urllib.unquote(pathElement).decode("utf-8")
+        return urllib.parse.unquote(pathElement.decode('utf-8'))
 
     def _filterPathValue(self, value, handler, name, request):
         """Modify a path value according to handler (see [getPathArgs])"""
-        if handler in (u"@", u"@jid") and value == u"@":
+        if handler in ("@", "@jid") and value == "@":
             value = None
 
-        if handler in (u"", u"@"):
+        if handler in ("", "@"):
             if value is None:
-                return u""
-        elif handler in (u"jid", u"@jid"):
+                return ""
+        elif handler in ("jid", "@jid"):
             if value:
                 try:
                     return jid.JID(value)
                 except RuntimeError:
-                    log.warning(_(u"invalid jid argument: {value}").format(value=value))
+                    log.warning(_("invalid jid argument: {value}").format(value=value))
                     self.pageError(request, C.HTTP_BAD_REQUEST)
             else:
-                return u""
+                return ""
         else:
             return handler(self, value, name, request)
 
@@ -772,7 +769,7 @@
         data = self.getRData(request)
 
         for idx, name in enumerate(names):
-            if name[0] == u"*":
+            if name[0] == "*":
                 value = data[name[1:]] = []
                 while True:
                     try:
@@ -792,14 +789,14 @@
 
         values_count = idx + 1
         if values_count < min_args:
-            log.warning(_(u"Missing arguments in URL (got {count}, expected at least "
-                          u"{min_args})").format(count=values_count, min_args=min_args))
+            log.warning(_("Missing arguments in URL (got {count}, expected at least "
+                          "{min_args})").format(count=values_count, min_args=min_args))
             self.pageError(request, C.HTTP_BAD_REQUEST)
 
         for name in names[values_count:]:
             data[name] = None
 
-        for name, handler in kwargs.iteritems():
+        for name, handler in kwargs.items():
             if name[0] == "*":
                 data[name] = [
                     self._filterPathValue(v, handler, name, request) for v in data[name]
@@ -831,15 +828,15 @@
         if extra is None:
             extra = {}
         else:
-            assert not {u"rsm_max", u"rsm_after", u"rsm_before",
-                        C.KEY_ORDER_BY}.intersection(extra.keys())
-        extra[u"rsm_max"] = unicode(page_max)
+            assert not {"rsm_max", "rsm_after", "rsm_before",
+                        C.KEY_ORDER_BY}.intersection(list(extra.keys()))
+        extra["rsm_max"] = str(page_max)
         if order_by is not None:
             extra[C.KEY_ORDER_BY] = order_by
-        if u'after' in params:
-            extra[u'rsm_after'] = params[u'after']
-        elif u'before' in params:
-            extra[u'rsm_before'] = params[u'before']
+        if 'after' in params:
+            extra['rsm_after'] = params['after']
+        elif 'before' in params:
+            extra['rsm_before'] = params['before']
         return extra
 
     def setPagination(self, request, pubsub_data):
@@ -853,7 +850,7 @@
         """
         template_data = request.template_data
         try:
-            last_id = pubsub_data[u"rsm_last"]
+            last_id = pubsub_data["rsm_last"]
         except KeyError:
             # no pagination available
             return
@@ -862,10 +859,10 @@
             # We only show previous button if it's not the first page already.
             # If we have no index, we default to display the button anyway
             # as we can't know if we are on the first page or not.
-            first_id = pubsub_data[u"rsm_first"]
+            first_id = pubsub_data["rsm_first"]
             template_data['previous_page_url'] = self.getParamURL(request,
                                                                   before=first_id)
-        if not pubsub_data[u"complete"]:
+        if not pubsub_data["complete"]:
             # we also show the page next button if complete is None because we
             # can't know where we are in the feed in this case.
             template_data['next_page_url'] = self.getParamURL(request, after=last_id)
@@ -899,11 +896,11 @@
         self.cache_pubsub_sub.add((service, node, sub_id))
 
     def checkCacheSubscribeEb(self, failure_, service, node):
-        log.warning(_(u"Can't subscribe to node: {msg}").format(msg=failure_))
+        log.warning(_("Can't subscribe to node: {msg}").format(msg=failure_))
         # FIXME: cache must be marked as unusable here
 
     def psNodeWatchAddEb(self, failure_, service, node):
-        log.warning(_(u"Can't add node watched: {msg}").format(msg=failure_))
+        log.warning(_("Can't add node watched: {msg}").format(msg=failure_))
 
     def checkCache(self, request, cache_type, **kwargs):
         """check if a page is in cache and return cached version if suitable
@@ -932,9 +929,9 @@
                     short = kwargs["short"]
                     node = self.host.ns_map[short]
                 except KeyError:
-                    log.warning(_(u'Can\'t use cache for empty node without namespace '
-                                  u'set, please ensure to set "short" and that it is '
-                                  u'registered'))
+                    log.warning(_('Can\'t use cache for empty node without namespace '
+                                  'set, please ensure to set "short" and that it is '
+                                  'registered'))
                     return
             if profile != C.SERVICE_PROFILE:
                 #  only service profile is cache for now
@@ -963,14 +960,14 @@
                 return
 
         else:
-            raise exceptions.InternalError(u"Unknown cache_type")
-        log.debug(u"using cache for {page}".format(page=self))
+            raise exceptions.InternalError("Unknown cache_type")
+        log.debug("using cache for {page}".format(page=self))
         cache.last_access = time.time()
         self._setCacheHeaders(request, cache)
         self._checkCacheHeaders(request, cache)
         request.write(cache.rendered)
         request.finish()
-        raise failure.Failure(exceptions.CancelError(u"cache is used"))
+        raise failure.Failure(exceptions.CancelError("cache is used"))
 
     def _cacheURL(self, __, request, profile):
         self.cached_urls.setdefault(profile, {})[request.uri] = CacheURL(request)
@@ -982,18 +979,18 @@
             cache = cls.cache[profile][C.CACHE_PUBSUB][jid.JID(service)][node]
         except KeyError:
             log.info(_(
-                u"Removing subscription for {service}/{node}: "
-                u"the page is not cached").format(service=service, node=node))
+                "Removing subscription for {service}/{node}: "
+                "the page is not cached").format(service=service, node=node))
             d1 = host.bridgeCall("psUnsubscribe", service, node, profile)
             d1.addErrback(
                 lambda failure_: log.warning(
-                    _(u"Can't unsubscribe from {service}/{node}: {msg}").format(
+                    _("Can't unsubscribe from {service}/{node}: {msg}").format(
                         service=service, node=node, msg=failure_)))
             d2 = host.bridgeCall("psNodeWatchAdd", service, node, profile)
             # TODO: check why the page is not in cache, remove subscription?
             d2.addErrback(
                 lambda failure_: log.warning(
-                    _(u"Can't remove watch for {service}/{node}: {msg}").format(
+                    _("Can't remove watch for {service}/{node}: {msg}").format(
                         service=service, node=node, msg=failure_)))
         else:
             cache.clear()
@@ -1009,14 +1006,14 @@
         """
         for page, request, check_profile in cls.signals_handlers.get(
             signal, {}
-        ).itervalues():
+        ).values():
             if check_profile:
                 signal_profile = args[-1]
                 request_profile = page.getProfile(request)
                 if not request_profile:
                     # if you want to use signal without session, unset check_profile
                     # (be sure to know what you are doing)
-                    log.error(_(u"no session started, signal can't be checked"))
+                    log.error(_("no session started, signal can't be checked"))
                     continue
                 if signal_profile != request_profile:
                     #  we ignore the signal, it's not for our profile
@@ -1025,7 +1022,7 @@
                 # socket is not yet opened, we cache the signal
                 request._signals_cache.append((request, signal, args))
                 log.debug(
-                    u"signal [{signal}] cached: {args}".format(signal=signal, args=args)
+                    "signal [{signal}] cached: {args}".format(signal=signal, args=args)
                 )
             else:
                 page.on_signal(page, request, signal, *args)
@@ -1039,7 +1036,7 @@
         # we need to replace corresponding original requests by this websocket request
         # in signals_handlers
         signal_id = request.signal_id
-        for signal_handlers_map in self.__class__.signals_handlers.itervalues():
+        for signal_handlers_map in self.__class__.signals_handlers.values():
             if signal_id in signal_handlers_map:
                 signal_handlers_map[signal_id][1] = request
 
@@ -1058,10 +1055,10 @@
             try:
                 del LiberviaPage.signals_handlers[signal][signal_id]
             except KeyError:
-                log.error(_(u"Can't find signal handler for [{signal}], this should not "
-                            u"happen").format(signal=signal))
+                log.error(_("Can't find signal handler for [{signal}], this should not "
+                            "happen").format(signal=signal))
             else:
-                log.debug(_(u"Removed signal handler"))
+                log.debug(_("Removed signal handler"))
 
     def delegateToResource(self, request, resource):
         """continue workflow with Twisted Resource"""
@@ -1071,7 +1068,7 @@
         else:
             request.write(buf)
             request.finish()
-        raise failure.Failure(exceptions.CancelError(u"resource delegation"))
+        raise failure.Failure(exceptions.CancelError("resource delegation"))
 
     def HTTPRedirect(self, request, url):
         """redirect to an URL using HTTP redirection
@@ -1081,9 +1078,9 @@
         """
         web_util.redirectTo(url.encode("utf-8"), request)
         request.finish()
-        raise failure.Failure(exceptions.CancelError(u"HTTP redirection is used"))
+        raise failure.Failure(exceptions.CancelError("HTTP redirection is used"))
 
-    def redirectOrContinue(self, request, redirect_arg=u"redirect_url"):
+    def redirectOrContinue(self, request, redirect_arg="redirect_url"):
         """helper method to redirect a page to an url given as arg
 
         if the arg is not present, the page will continue normal workflow
@@ -1092,13 +1089,14 @@
         @interrupt: redirect the page to requested URL
         @interrupt pageError(C.HTTP_BAD_REQUEST): empty or non local URL is used
         """
+        redirect_arg = redirect_arg.encode('utf-8')
         try:
-            url = request.args["redirect_url"][0]
+            url = request.args[redirect_arg][0].decode('utf-8')
         except (KeyError, IndexError):
             pass
         else:
             #  a redirection is requested
-            if not url or url[0] != u"/":
+            if not url or url[0] != "/":
                 # we only want local urls
                 self.pageError(request, C.HTTP_BAD_REQUEST)
             else:
@@ -1126,13 +1124,14 @@
         @raise KeyError: there is no known page with this name
         """
         # FIXME: render non LiberviaPage resources
-        path = page_path.rstrip(u"/").split(u"/")
+        path = page_path.rstrip("/").split("/")
         if not path[0]:
             redirect_page = self.vhost_root
         else:
             redirect_page = self.named_pages[path[0]]
 
         for subpage in path[1:]:
+            subpage = subpage.encode('utf-8')
             if redirect_page is self.vhost_root:
                 redirect_page = redirect_page.children[subpage]
             else:
@@ -1148,7 +1147,7 @@
             self._do_cache = None
 
         redirect_page.renderPage(request, skip_parse_url=skip_parse_url)
-        raise failure.Failure(exceptions.CancelError(u"page redirection is used"))
+        raise failure.Failure(exceptions.CancelError("page redirection is used"))
 
     def pageError(self, request, code=C.HTTP_NOT_FOUND, no_body=False):
         """generate an error page and terminate the request
@@ -1164,13 +1163,13 @@
         if no_body:
             request.finish()
         else:
-            template = u"error/" + unicode(code) + ".html"
+            template = "error/" + str(code) + ".html"
             template_data = request.template_data
             session_data = self.host.getSessionData(request, session_iface.ISATSession)
             if session_data.locale is not None:
-                template_data[u'locale'] = session_data.locale
+                template_data['locale'] = session_data.locale
             if self.vhost_root.site_name:
-                template_data[u'site'] = self.vhost_root.site_name
+                template_data['site'] = self.vhost_root.site_name
 
             rendered = self.host.renderer.render(
                 template,
@@ -1179,7 +1178,7 @@
             )
 
             self.writeData(rendered, request)
-        raise failure.Failure(exceptions.CancelError(u"error page is used"))
+        raise failure.Failure(exceptions.CancelError("error page is used"))
 
     def writeData(self, data, request):
         """write data to transport and finish the request"""
@@ -1192,7 +1191,7 @@
             cache = reduce(lambda d, k: d.setdefault(k, {}), self._do_cache, self.cache)
             page_cache = cache[redirected_page] = CachePage(data_encoded)
             self._setCacheHeaders(request, page_cache)
-            log.debug(_(u"{page} put in cache for [{profile}]")
+            log.debug(_("{page} put in cache for [{profile}]")
                 .format( page=self, profile=self._do_cache[0]))
             self._do_cache = None
             self._checkCacheHeaders(request, page_cache)
@@ -1200,8 +1199,8 @@
         try:
             request.write(data_encoded)
         except AttributeError:
-            log.warning(_(u"Can't write page, the request has probably been cancelled "
-                          u"(browser tab closed or reloaded)"))
+            log.warning(_("Can't write page, the request has probably been cancelled "
+                          "(browser tab closed or reloaded)"))
             return
         request.finish()
 
@@ -1214,19 +1213,19 @@
         If there is no unmanaged part of the segment, current page workflow is pursued
         """
         if request.postpath:
-            subpage = self.nextPath(request)
+            subpage = self.nextPath(request).encode('utf-8')
             try:
                 child = self.children[subpage]
             except KeyError:
                 self.pageError(request)
             else:
                 child.render(request)
-                raise failure.Failure(exceptions.CancelError(u"subpage page is used"))
+                raise failure.Failure(exceptions.CancelError("subpage page is used"))
 
     def _prepare_dynamic(self, __, request):
         # we need to activate dynamic page
         # we set data for template, and create/register token
-        socket_token = unicode(uuid.uuid4())
+        socket_token = str(uuid.uuid4())
         socket_url = self.host.getWebsocketURL(request)
         socket_debug = C.boolConst(self.host.debug)
         request.template_data["websocket"] = WebsocketMeta(
@@ -1250,21 +1249,21 @@
         # if confirm variable is set in case of successfuly data post
         session_data = self.host.getSessionData(request, session_iface.ISATSession)
         if session_data.popPageFlag(self, C.FLAG_CONFIRM):
-            template_data[u"confirm"] = True
+            template_data["confirm"] = True
         notifs = session_data.popPageNotifications(self)
         if notifs:
-            template_data[u"notifications"] = notifs
+            template_data["notifications"] = notifs
         if session_data.locale is not None:
-            template_data[u'locale'] = session_data.locale
+            template_data['locale'] = session_data.locale
         if self.vhost_root.site_name:
-            template_data[u'site'] = self.vhost_root.site_name
+            template_data['site'] = self.vhost_root.site_name
 
         return self.host.renderer.render(
             self.template,
             page_url=self.getURL(),
-            media_path=u"/" + C.MEDIA_DIR,
+            media_path="/" + C.MEDIA_DIR,
             cache_path=session_data.cache_dir,
-            build_path=u"/" + C.BUILD_DIR + u"/",
+            build_path="/" + C.BUILD_DIR + "/",
             main_menu=self.main_menu,
             **template_data)
 
@@ -1274,10 +1273,10 @@
 
     def _internalError(self, failure_, request):
         """called if an error is not catched"""
-        if failure_.check(BridgeException) and failure_.value.condition == u'not-allowed':
-            log.warning(u"not allowed exception catched")
+        if failure_.check(BridgeException) and failure_.value.condition == 'not-allowed':
+            log.warning("not allowed exception catched")
             self.pageError(request, C.HTTP_FORBIDDEN)
-        log.error(_(u"Uncatched error for HTTP request on {url}: {msg}")
+        log.error(_("Uncatched error for HTTP request on {url}: {msg}")
             .format( url=request.URLPath(), msg=failure_))
         self.pageError(request, C.HTTP_INTERNAL_ERROR)
 
@@ -1290,7 +1289,7 @@
         request.setResponseCode(C.HTTP_SEE_OTHER)
         request.setHeader("location", request.uri)
         request.finish()
-        raise failure.Failure(exceptions.CancelError(u"Post/Redirect/Get is used"))
+        raise failure.Failure(exceptions.CancelError("Post/Redirect/Get is used"))
 
     def _on_data_post_redirect(self, ret, request):
         """called when page's on_data_post has been done successfuly
@@ -1308,12 +1307,12 @@
         """
         if ret is None:
             ret = ()
-        elif isinstance(ret, basestring):
+        elif isinstance(ret, str):
             ret = (ret,)
         else:
             ret = tuple(ret)
             raise NotImplementedError(
-                _(u"iterable in on_data_post return value is not used yet")
+                _("iterable in on_data_post return value is not used yet")
             )
         session_data = self.host.getSessionData(request, session_iface.ISATSession)
         request_data = self.getRData(request)
@@ -1333,21 +1332,21 @@
         if not C.POST_NO_CONFIRM in ret:
             session_data.setPageFlag(redirect_page, C.FLAG_CONFIRM)
         request.setResponseCode(C.HTTP_SEE_OTHER)
-        request.setHeader("location", redirect_uri)
+        request.setHeader(b"location", redirect_uri)
         request.finish()
-        raise failure.Failure(exceptions.CancelError(u"Post/Redirect/Get is used"))
+        raise failure.Failure(exceptions.CancelError("Post/Redirect/Get is used"))
 
     def _on_data_post(self, __, request):
         csrf_token = self.host.getSessionData(
             request, session_iface.ISATSession
         ).csrf_token
         try:
-            given_csrf = self.getPostedData(request, u"csrf_token")
+            given_csrf = self.getPostedData(request, "csrf_token")
         except KeyError:
             given_csrf = None
         if given_csrf is None or given_csrf != csrf_token:
             log.warning(
-                _(u"invalid CSRF token, hack attempt? URL: {url}, IP: {ip}").format(
+                _("invalid CSRF token, hack attempt? URL: {url}, IP: {ip}").format(
                     url=request.uri, ip=request.getClientIP()
                 )
             )
@@ -1374,15 +1373,18 @@
         """
         #  FIXME: request.args is already unquoting the value, it seems we are doing
         #         double unquote
-        if isinstance(keys, basestring):
+        if isinstance(keys, str):
             keys = [keys]
             get_first = True
         else:
             get_first = False
 
+        keys = [k.encode('utf-8') for k in keys]
+
         ret = []
         for key in keys:
-            gen = (urllib.unquote(v).decode("utf-8") for v in request.args.get(key, []))
+            gen = (urllib.parse.unquote(v.decode("utf-8"))
+                                        for v in request.args.get(key, []))
             if multiple:
                 ret.append(gen)
             else:
@@ -1405,16 +1407,18 @@
         @param multiple(bool): if False, only the first values are returned
         @return (dict[unicode, list[unicode]]): post values
         """
-        except_ = tuple(except_) + (u"csrf_token",)
+        except_ = tuple(except_) + ("csrf_token",)
         ret = {}
-        for key, values in request.args.iteritems():
-            key = urllib.unquote(key).decode("utf-8")
+        for key, values in request.args.items():
+            key = key.decode('utf-8')
+            key = urllib.parse.unquote(key)
             if key in except_:
                 continue
+            values = [v.decode('utf-8') for v in values]
             if not multiple:
-                ret[key] = urllib.unquote(values[0]).decode("utf-8")
+                ret[key] = urllib.parse.unquote(values[0])
             else:
-                ret[key] = [urllib.unquote(v).decode("utf-8") for v in values]
+                ret[key] = [urllib.parse.unquote(v) for v in values]
         return ret
 
     def getProfile(self, request):
@@ -1472,7 +1476,7 @@
         if not accept_language:
             return
         accepted = {a.strip() for a in accept_language.split(',')}
-        available = [unicode(l) for l in self.host.renderer.translations]
+        available = [str(l) for l in self.host.renderer.translations]
         for lang in accepted:
             lang = lang.split(';')[0].strip().lower()
             if not lang:
@@ -1494,20 +1498,20 @@
         """
         if not self.dynamic:
             raise exceptions.InternalError(
-                _(u"renderPartial must only be used with dynamic pages")
+                _("renderPartial must only be used with dynamic pages")
             )
         session_data = self.host.getSessionData(request, session_iface.ISATSession)
         if session_data.locale is not None:
-            template_data[u'locale'] = session_data.locale
+            template_data['locale'] = session_data.locale
         if self.vhost_root.site_name:
-            template_data[u'site'] = self.vhost_root.site_name
+            template_data['site'] = self.vhost_root.site_name
 
         return self.host.renderer.render(
             template,
             page_url=self.getURL(),
-            media_path=u"/" + C.MEDIA_DIR,
+            media_path="/" + C.MEDIA_DIR,
             cache_path=session_data.cache_dir,
-            build_path=u"/" + C.BUILD_DIR + u"/",
+            build_path="/" + C.BUILD_DIR + "/",
             main_menu=self.main_menu,
             **template_data
         )
@@ -1533,9 +1537,9 @@
         html = self.renderPartial(request, template, template_data)
         try:
             request.sendData(
-                u"dom", selectors=selectors, update_type=update_type, html=html)
+                "dom", selectors=selectors, update_type=update_type, html=html)
         except Exception as e:
-            log.error(u"Can't renderAndUpdate, html was: {html}".format(html=html))
+            log.error("Can't renderAndUpdate, html was: {html}".format(html=html))
             raise e
 
     def renderPage(self, request, skip_parse_url=False):
@@ -1546,8 +1550,8 @@
             session_data = self.host.getSessionData(request, session_iface.ISATSession)
             csrf_token = session_data.csrf_token
             request.template_data = {
-                u"profile": session_data.profile,
-                u"csrf_token": csrf_token,
+                "profile": session_data.profile,
+                "csrf_token": csrf_token,
             }
 
             # XXX: here is the code which need to be executed once
@@ -1561,13 +1565,13 @@
                 try:
                     locale = request.args.pop(C.KEY_LANG)[0]
                 except IndexError:
-                    log.warning(u"empty lang received")
+                    log.warning("empty lang received")
                 else:
-                    if u"/" in locale:
+                    if "/" in locale:
                         # "/" is refused because locale may sometime be used to access
                         # path, if localised documents are available for instance
-                        log.warning(_(u'illegal char found in locale ("/"), hack '
-                                      u'attempt? locale={locale}').format(locale=locale))
+                        log.warning(_('illegal char found in locale ("/"), hack '
+                                      'attempt? locale={locale}').format(locale=locale))
                         locale = None
                     session_data.locale = locale
 
@@ -1596,7 +1600,7 @@
                     d.addCallback(self.parse_url, request)
                     d.addCallback(self._cacheURL, request, profile)
                 else:
-                    log.debug(_(u"using URI cache for {page}").format(page=self))
+                    log.debug(_("using URI cache for {page}").format(page=self))
                     cache_url.use(request)
             else:
                 d.addCallback(self.parse_url, request)