]> arthur.barton.de Git - bup.git/commitdiff
web: fix for python3
authorJohannes Berg <johannes@sipsolutions.net>
Sun, 17 May 2020 19:38:14 +0000 (21:38 +0200)
committerRob Browning <rlb@defaultvalue.org>
Sun, 21 Jun 2020 16:20:58 +0000 (11:20 -0500)
Do the minimal adjustments to make 'bup web' work with python3.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Reviewed-by: Rob Browning <rlb@defaultvalue.org>
Makefile
lib/cmd/web-cmd.py

index 39202e79329fa113812687b4c24b29e854b450af..d7a076e8d4d588ab0fd338042a219d59722804ce 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -191,6 +191,7 @@ cmdline_tests := \
   t/test-fsck.sh \
   t/test-fuse.sh \
   t/test-ftp \
+  t/test-web.sh \
   t/test-gc.sh \
   t/test-import-duplicity.sh \
   t/test-import-rdiff-backup.sh \
@@ -221,13 +222,6 @@ cmdline_tests := \
   t/test-tz.sh \
   t/test-xdev.sh
 
-ifeq "2" "$(bup_python_majver)"
-  # unresolved
-  #   web: needs more careful attention, path bytes round-trips, reprs, etc.
-  cmdline_tests += \
-    t/test-web.sh
-endif
-
 tmp-target-run-test-get-%: all t/tmp
        $(pf); cd $$(pwd -P); TMPDIR="$(test_tmp)" \
          t/test-get $* 2>&1 | tee -a t/tmp/test-log/$$$$.log
index 305eeaad50ee2801014cdfa87958189cdcf813fd..a967f34f1ba15648b30e8f05906036d42474c731 100755 (executable)
@@ -8,6 +8,7 @@ exec "$bup_python" "$0" ${1+"$@"}
 from __future__ import absolute_import, print_function
 from collections import namedtuple
 import mimetypes, os, posixpath, signal, stat, sys, time, urllib, webbrowser
+from binascii import hexlify
 
 from bup import options, git, vfs
 from bup.helpers import (chunkyreader, debug1, format_filesize, handle_ctrl_c,
@@ -15,6 +16,7 @@ from bup.helpers import (chunkyreader, debug1, format_filesize, handle_ctrl_c,
 from bup.metadata import Metadata
 from bup.path import resource_path
 from bup.repo import LocalRepo
+from bup.io import path_msg
 
 try:
     from tornado import gen
@@ -40,14 +42,14 @@ def http_date_from_utc_ns(utc_ns):
 def _compute_breadcrumbs(path, show_hidden=False):
     """Returns a list of breadcrumb objects for a path."""
     breadcrumbs = []
-    breadcrumbs.append(('[root]', '/'))
-    path_parts = path.split('/')[1:-1]
-    full_path = '/'
+    breadcrumbs.append((b'[root]', b'/'))
+    path_parts = path.split(b'/')[1:-1]
+    full_path = b'/'
     for part in path_parts:
-        full_path += part + "/"
-        url_append = ""
+        full_path += part + b"/"
+        url_append = b""
         if show_hidden:
-            url_append = '?hidden=1'
+            url_append = b'?hidden=1'
         breadcrumbs.append((part, full_path+url_append))
     return breadcrumbs
 
@@ -58,9 +60,9 @@ def _contains_hidden_files(repo, dir_item):
 
     """
     for name, item in vfs.contents(repo, dir_item, want_meta=False):
-        if name in ('.', '..'):
+        if name in (b'.', b'..'):
             continue
-        if name.startswith('.'):
+        if name.startswith(b'.'):
             return True
     return False
 
@@ -68,15 +70,15 @@ def _contains_hidden_files(repo, dir_item):
 def _dir_contents(repo, resolution, show_hidden=False):
     """Yield the display information for the contents of dir_item."""
 
-    url_query = '?hidden=1' if show_hidden else ''
+    url_query = b'?hidden=1' if show_hidden else b''
 
     def display_info(name, item, resolved_item, display_name=None):
         # link should be based on fully resolved type to avoid extra
         # HTTP redirect.
+        link = tornado.escape.url_escape(name, plus=False)
         if stat.S_ISDIR(vfs.item_mode(resolved_item)):
-            link = urllib.quote(name) + '/'
-        else:
-            link = urllib.quote(name)
+            link += '/'
+        link = link.encode('ascii')
 
         size = vfs.item_size(repo, item)
         if opt.human_readable:
@@ -87,9 +89,9 @@ def _dir_contents(repo, resolution, show_hidden=False):
         if not display_name:
             mode = vfs.item_mode(item)
             if stat.S_ISDIR(mode):
-                display_name = name + '/'
+                display_name = name + b'/'
             elif stat.S_ISLNK(mode):
-                display_name = name + '@'
+                display_name = name + b'@'
             else:
                 display_name = name
 
@@ -98,12 +100,12 @@ def _dir_contents(repo, resolution, show_hidden=False):
     dir_item = resolution[-1][1]    
     for name, item in vfs.contents(repo, dir_item):
         if not show_hidden:
-            if (name not in ('.', '..')) and name.startswith('.'):
+            if (name not in (b'.', b'..')) and name.startswith(b'.'):
                 continue
-        if name == '.':
-            yield display_info(name, item, item, '.')
+        if name == b'.':
+            yield display_info(name, item, item, b'.')
             parent_item = resolution[-2][1] if len(resolution) > 1 else dir_item
-            yield display_info('..', parent_item, parent_item, '..')
+            yield display_info(b'..', parent_item, parent_item, b'..')
             continue
         res = vfs.try_resolve(repo, name, parent=resolution, want_meta=False)
         res_name, res_item = res[-1]
@@ -127,8 +129,8 @@ class BupRequestHandler(tornado.web.RequestHandler):
         return self._process_request(path)
     
     def _process_request(self, path):
-        path = urllib.unquote(path)
         print('Handling request for %s' % path)
+        sys.stdout.flush()
         # Set want_meta because dir metadata won't be fetched, and if
         # it's not a dir, then we're going to want the metadata.
         res = vfs.resolve(self.repo, path, want_meta=True)
@@ -148,9 +150,9 @@ class BupRequestHandler(tornado.web.RequestHandler):
         Return value is either a file object, or None (indicating an
         error).  In either case, the headers are sent.
         """
-        if not path.endswith('/') and len(path) > 0:
-            print('Redirecting from %s to %s' % (path, path + '/'))
-            return self.redirect(path + '/', permanent=True)
+        if not path.endswith(b'/') and len(path) > 0:
+            print('Redirecting from %s to %s' % (path_msg(path), path_msg(path + b'/')))
+            return self.redirect(path + b'/', permanent=True)
 
         hidden_arg = self.request.arguments.get('hidden', [0])[-1]
         try:
@@ -183,7 +185,7 @@ class BupRequestHandler(tornado.web.RequestHandler):
         
         self.set_header("Content-Length", str(meta.size))
         assert len(file_item.oid) == 20
-        self.set_header("Etag", file_item.oid.encode('hex'))
+        self.set_header("Etag", hexlify(file_item.oid))
         if self.request.method != 'HEAD':
             with vfs.fopen(self.repo, file_item) as f:
                 it = chunkyreader(f)
@@ -276,12 +278,15 @@ git.check_repo_or_die()
 
 settings = dict(
     debug = 1,
-    template_path = resource_path('web'),
-    static_path = resource_path('web/static')
+    template_path = resource_path(b'web').decode('utf-8'),
+    static_path = resource_path(b'web/static').decode('utf-8'),
 )
 
 # Disable buffering on stdout, for debug messages
-sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
+try:
+    sys.stdout._line_buffering = True
+except AttributeError:
+    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
 
 application = tornado.web.Application([
     (r"(?P<path>/.*)", BupRequestHandler, dict(repo=LocalRepo())),
@@ -291,12 +296,9 @@ http_server = HTTPServer(application)
 io_loop_pending = IOLoop.instance()
 
 if isinstance(address, InetAddress):
-    http_server.listen(address.port, address.host)
-    try:
-        sock = http_server._socket # tornado < 2.0
-    except AttributeError as e:
-        sock = http_server._sockets.values()[0]
-    print('Serving HTTP on %s:%d...' % sock.getsockname())
+    sockets = tornado.netutil.bind_sockets(address.port, address.host)
+    http_server.add_sockets(sockets)
+    print('Serving HTTP on %s:%d...' % sockets[0].getsockname())
     if opt.browser:
         browser_addr = 'http://' + address[0] + ':' + str(address[1])
         io_loop_pending.add_callback(lambda : webbrowser.open(browser_addr))